Skip to content

Architecture & the daemon

One self-contained Go binary that wears several faces, all sharing the same on-disk state:

CapabilityDescription
Single-binary distributionwarden bundles the daemon, CLI clients, MCP server, TUI, and (in release builds) the embedded web GUI. wd is an installed symlink.
Local daemonThe single writer to the session store. Serves a loopback REST API (127.0.0.1:8765) and runs a background poller that keeps each agent’s status and subject fresh.
Embedded ScrivaDB session storeSessions persist in an embedded ScrivaDB (github.com/srjn45/scriva, SyncModeNone) rooted at ~/.warden/sessions-db/ — an active collection for live sessions and a closed one for archived, each record keyed by session id. A mutation appends one record instead of rewriting a whole JSON file; still no database server to run. On first launch after upgrading, warden imports the legacy sessions/+closed/ JSON once and keeps it as a read-only backup — see Upgrading & storage migration.
Claude Code lifecycle hooksA hook script posts SessionStart/Notification/Stop/SubagentStop/SessionEnd to the daemon so status updates in real time without polling. Fails soft (never blocks the agent).
launchd auto-start (macOS)Installs as an auto-starting, crash-restarting background service.
Stable code identityOne-time self-signed code-signing cert keeps the macOS TCC (Full Disk Access) grant stable across rebuilds.
Security hardening0700 data dir, slowloris/body/write timeouts (bypassed for SSE/WS/long-poll), refuses non-loopback bind unless WARDEN_ALLOW_NONLOOPBACK=1.
warden doctorPreflight checks: required binaries (tmux, git, claude, gh), daemon reachability, data directory.

warden architecture

When projects are organized into a project group, the daemon guarantees one live orch-<project> orchestrator per project. These orchestrators are peers — there is no global orchestrator above them. Each one plans and delegates to local workers, planners, and pipelines; it reaches the other projects by messaging their orchestrator directly.

PROJECT GROUP — e.g. app-suiteorch-frontendorchestrator · never writes codeorch-backendorchestrator · peersend_message — task ⇄ replyspawns / delegatesworkerimplement → PRplannerspec · plan modepipelineDAG of jobsspawnsworkerimplement → PRpipelineDAG of jobsshared context (ctx) + mailbox — coordination substrate
spawn / delegatepeer message (both ways)coordination substrate
The claim: orchestrators are peers, not a hierarchy. When frontend needs a change in backend, orch-frontend messages orch-backend — which coordinates its own local agents and replies. warden auto-spawns the orch-<project> agent on first group use and injects peer awareness (group name + sibling names) into each one at every relaunch.

The session store used to be one JSON file per session under ~/.warden/sessions/ and ~/.warden/closed/. It now runs on an embedded ScrivaDB rooted at ~/.warden/sessions-db/ (active + closed collections, keyed by session id), so a mutation appends one record instead of rewriting a whole file. This is an internal storage swap — the daemon’s REST API, the CLI/MCP clients, and warden inspect export/import are byte-for-byte unchanged.

On the first daemon launch after upgrading, warden runs a one-time import:

  • Every legacy sessions/*.json and closed/*.json is decoded individually into the new collections (a corrupt or unsafe-id file is skipped with a warning, never blocking the upgrade), folding in the former provenance backfill.
  • A ~/.warden/.sessions-filedb-imported sentinel is written last, only after both collections load. If the import dies partway the sentinel is absent, so the next boot wipes the half-built sessions-db/ and re-imports from the intact legacy JSON — nothing is lost.

The legacy sessions//closed/ directories are read-only from now on and kept as a cold backup; this release never deletes them.