Skip to content

Install & setup

  • Go 1.26+ — to build the binary (only needed for go install or building from source)
  • tmux — every agent session runs in a detached tmux window
  • git — worktree creation and guarded cleanup
  • Claude Code (claude on PATH) — the default agent runtime launched in each session (other backends are opt-in via --aicli — see Agent backends)
  • gh (GitHub CLI) — required for pr-review sessions to check out the PR branch

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.

warden is one self-contained binary. The official install needs only curl — no git, Go, npm, or OS package manager.

Terminal window
curl -fsSL https://raw.githubusercontent.com/srjn45/warden/main/scripts/install.sh | bash

That 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).

Terminal window
go install github.com/srjn45/warden/cmd/warden@latest

This 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.

Terminal window
git clone https://github.com/srjn45/warden.git
cd warden
make build # CLI/daemon/TUI only → bin/warden
make release # builds the web UI first, then embeds it → full GUI
./scripts/install.sh --no-build # deploy + start the user service

Keep a curl-installed binary current with the first-class updater:

Terminal window
warden update # or: wd update
warden update --plan # preview upgrade path, migrations, breaking changes & downtime
warden update --check # report only — no download
warden update --yes # non-interactive update without confirmation prompt
warden update --version v9.9.0
warden update --force # reinstall the current / target version

warden 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:

Terminal window
warden update --plan

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:

Terminal window
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 the TUI cockpit (warden / warden tui):

  • When a newer release is available, the footer shows [u] Update to vX.Y.Z available (press 'u'). Press u, confirm, and the cockpit runs the same update flow, then syscall.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. Press r to 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:

Terminal window
./scripts/install.sh # or: make install
# or, without a checkout:
# curl -fsSL https://raw.githubusercontent.com/srjn45/warden/main/scripts/install.sh | bash

The 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/bin must be on your PATH to run warden from the shell — the installer warns if it isn’t.

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:

Terminal window
loginctl enable-linger "$USER" # keep the daemon alive after logout (≈ launchd RunAtLoad)
systemctl --user daemon-reload
systemctl --user enable --now warden # start now + on every login

enable-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:

Terminal window
systemctl --user status warden # is it running?
systemctl --user restart warden # apply a rebuilt binary or config change
systemctl --user stop warden # stop without disabling
journalctl --user -u warden -f # follow the unit's own journal

The 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:

Terminal window
./scripts/codesign-setup.sh # creates a self-signed code-signing cert (once)
./scripts/install.sh # reinstall so the binary is signed

Then 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):

Terminal window
./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=1

Uninstall (stops and removes the service, binary, skill link, and MCP registration; preserves your data at ~/.warden (including warden-db) and the logs):

Terminal window
./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
  • Linux (systemd): the systemd journal — the unit sets StandardOutput=journal / StandardError=journal so logs survive reboots even where /tmp is 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 enters waiting_for_input, idle (stuck), orphaned, or errored. 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.

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:

Terminal window
# 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.