Install & setup
Prerequisites
Section titled “Prerequisites”- Go 1.26+ — to build the binary (only needed for
go installor building from source) - tmux — every agent session runs in a detached tmux window
- git — worktree creation and guarded cleanup
- Claude Code (
claudeon PATH) — the default agent runtime launched in each session (other backends are opt-in via--aicli— see Agent backends) gh(GitHub CLI) — required forpr-reviewsessions to check out the PR branch
Windows and WSL
Section titled “Windows and WSL”warden supports Linux and macOS releases. On Windows, use a supported Linux distribution under WSL and follow the Linux instructions below; systemd user services must be available in that WSL distribution. Native Windows installation and service management are not supported.
Install
Section titled “Install”warden is one self-contained binary. The official install needs only curl — no git, Go, npm, or OS package manager.
1. Official one-liner (recommended)
Section titled “1. Official one-liner (recommended)”curl -fsSL https://raw.githubusercontent.com/srjn45/warden/main/scripts/install.sh | bashThat downloads a verified GitHub release archive for your OS/arch, installs ~/.local/bin/warden (plus the wd alias), wires the user-level daemon service (launchd on macOS, systemd --user on Linux), links the Claude skill, registers the MCP server, and probes /healthz. Pin a release with WARDEN_VERSION=9.9.0 (with or without a leading v).
2. go install (Go toolchain)
Section titled “2. go install (Go toolchain)”go install github.com/srjn45/warden/cmd/warden@latestThis installs the warden binary (CLI + daemon + MCP server + TUI). Note: go install does not bundle the web dashboard (the UI is built from web/ and embedded at release time, and isn’t committed to the repo). The CLI, daemon API, TUI, and MCP server all work; for the embedded web GUI use the official installer (option 1) or build from source (option 3). go install also does not register the background service — run ./scripts/install.sh --no-build from a checkout, or start warden daemon manually.
3. Build from source
Section titled “3. Build from source”git clone https://github.com/srjn45/warden.gitcd wardenmake build # CLI/daemon/TUI only → bin/wardenmake release # builds the web UI first, then embeds it → full GUI./scripts/install.sh --no-build # deploy + start the user serviceUpgrade
Section titled “Upgrade”Keep a curl-installed binary current with the first-class updater:
warden update # or: wd updatewarden update --plan # preview upgrade path, migrations, breaking changes & downtimewarden update --check # report only — no downloadwarden update --yes # non-interactive update without confirmation promptwarden update --version v9.9.0warden update --force # reinstall the current / target versionwarden update queries GitHub Releases, verifies checksums.txt, downloads the archive for your GOOS/GOARCH into ~/.warden/tmp/, atomically swaps ~/.local/bin/warden (keeping a backup), re-signs on macOS when the warden-codesign identity is present, runs config migrations, restarts the user-level daemon service, and probes /healthz — rolling the binary back unless the new daemon reports ok on the target version and on the data schema that binary writes (schema_version; warden version prints it).
The v11 major moves all data into a single warden-db; warden update runs that one-time, verified cutover for you and rolls everything back on failure — see Schema 2 upgrade.
To inspect the upgrade plan without mutating state or stopping the daemon:
warden update --planRollback & Data Maintenance
Section titled “Rollback & Data Maintenance”For the full decision tree, backups, offline repairs, reset scopes, and backup-gated legacy cleanup, see the Operator guide: updates and data safety.
To roll back an update, run:
warden rollback # or: wd rollback (-y to skip confirmation)warden rollback restores the previous binary. If no schema changed, it swaps binaries directly. If the schema advanced, it restores data from <data>/backups/pre-<ver>-<ts>/ with confirmation.
warden init initializes a fresh data directory (or brings an existing one to
the current schema). For an offline migration, start with warden migrate --check
or warden migrate --plan; schema-1 repairs use warden repair all, while schema-2
verification uses warden repair warden-db --dry-run.
In-cockpit update & hot-reload
Section titled “In-cockpit update & hot-reload”In the TUI cockpit (warden / warden tui):
- When a newer release is available, the footer shows
[u] Update to vX.Y.Z available (press 'u'). Pressu, confirm, and the cockpit runs the same update flow, thensyscall.Execs itself so the TUI reloads in place without closing the terminal. - If the binary was upgraded externally (CLI
warden update, another machine, etc.) while the cockpit is open, the footer shows[r] Warden upgraded to vX.Y.Z — press 'r' to reload TUI. Pressrto hot-reload. Active tmux agent sessions keep running.
Install the daemon as a service (auto-start)
Section titled “Install the daemon as a service (auto-start)”The official one-liner already installs and starts the daemon. From a git checkout the same script auto-detects your OS, builds from source (unless --no-build), installs the binary to ~/.local/bin/warden, renders and loads the service unit, links the Claude skill, and registers the MCP server:
./scripts/install.sh # or: make install# or, without a checkout:# curl -fsSL https://raw.githubusercontent.com/srjn45/warden/main/scripts/install.sh | bashThe daemon then starts automatically, restarts on crash, and listens on 127.0.0.1:8765 by default. The same script powers ./scripts/reinstall.sh (redeploy after a code change) and ./scripts/uninstall.sh (covered below) on both platforms.
~/.local/binmust be on yourPATHto runwardenfrom the shell — the installer warns if it isn’t.
Linux (systemd user service)
Section titled “Linux (systemd user service)”On Linux the installer requires a systemd user session (systemctl on PATH) and installs warden as a per-user unit — no sudo, nothing system-wide. It renders deploy/warden.service.template to ~/.config/systemd/user/warden.service, then:
loginctl enable-linger "$USER" # keep the daemon alive after logout (≈ launchd RunAtLoad)systemctl --user daemon-reloadsystemctl --user enable --now warden # start now + on every loginenable-linger is what lets the daemon survive between SSH/terminal sessions. The installer runs all of this for you; you only need the commands below to manage it afterward:
systemctl --user status warden # is it running?systemctl --user restart warden # apply a rebuilt binary or config changesystemctl --user stop warden # stop without disablingjournalctl --user -u warden -f # follow the unit's own journalThe unit also writes stdout/stderr to the same log files used on macOS (see Logs below). When you enable remote access, the unit gains an EnvironmentFile= line pointing at your token file so the daemon picks up WARDEN_TOKEN on start; warden daemon token rotate issues systemctl --user restart warden to apply a new token.
Stop macOS “warden would like to access…” prompts (optional, macOS)
Section titled “Stop macOS “warden would like to access…” prompts (optional, macOS)”The launchd daemon is the macOS TCC responsible process for the agents it spawns and for its own directory picker, so reads of protected folders (Downloads, Documents, Desktop, the Music/media library) surface as “warden would like to access…” prompts. Granting Full Disk Access once silences them — but macOS ties the grant to the binary’s code identity, and an unsigned Go binary gets a new identity on every rebuild, which brings the prompts back.
Run the one-time setup to give the binary a stable self-signed identity:
./scripts/codesign-setup.sh # creates a self-signed code-signing cert (once)./scripts/install.sh # reinstall so the binary is signedThen grant access once: System Settings → Privacy & Security → Full Disk Access → ”+” and add ~/.local/bin/warden. Because the signing identity is stable, the grant survives future rebuilds. (install.sh/reinstall.sh sign automatically when the cert exists; without it they warn and leave the binary unsigned.)
Redeploy after a code change (replaces make release && ./bin/warden daemon):
./scripts/reinstall.sh # rebuild UI + binary, redeploy, restart./scripts/reinstall.sh --no-build # redeploy the existing build only# or: make reinstall / make reinstall NO_BUILD=1Uninstall (stops and removes the service, binary, skill link, and MCP registration; preserves your data at ~/.warden (including warden-db) and the logs):
./scripts/uninstall.sh # or: make uninstall./scripts/uninstall.sh --keep-binary # leave ~/.local/bin/warden in place- macOS (launchd): files under
/tmp- stdout:
/tmp/warden.daemon.log - stderr:
/tmp/warden.daemon.err
- stdout:
- Linux (systemd): the systemd journal — the unit sets
StandardOutput=journal/StandardError=journalso logs survive reboots even where/tmpis a tmpfs (Fedora, Arch, …):journalctl --user -u warden -f(follow live)journalctl --user -u warden -n 100(last 100 lines)
Notifications: off by default. When enabled with
WARDEN_NOTIFY=on, the daemon posts a macOS notification when an agent enterswaiting_for_input,idle(stuck),orphaned, orerrored. These appear only when the daemon runs in your GUI login session (a terminal, or a launchd user agent); a headless/system daemon logs them instead.
Wire in the Claude Code hooks
Section titled “Wire in the Claude Code hooks”The hook script posts lifecycle events (SessionStart, Notification, Stop, SubagentStop, SessionEnd) to the daemon so it can update agent status in real time without polling. SessionEnd marks the session done (terminal) when claude exits.
Merge hooks/settings.snippet.json into ~/.claude/settings.json. The snippet uses a __WARDEN_HOOK__ placeholder — substitute the absolute path to hooks/warden-hook.sh in your clone first:
# from the repo root, render the snippet with the real hook path:sed "s|__WARDEN_HOOK__|$(pwd)/hooks/warden-hook.sh|g" hooks/settings.snippet.json
# If ~/.claude/settings.json doesn't exist yet, write it directly:sed "s|__WARDEN_HOOK__|$(pwd)/hooks/warden-hook.sh|g" hooks/settings.snippet.json > ~/.claude/settings.json
# If it already exists, merge the rendered "hooks" key into the root of your# existing settings.json object.The hook fails soft — it never blocks or errors the agent, even if the daemon is down or the session is unknown.