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 warden-db. Serves a loopback REST API (127.0.0.1:8765) and runs a background poller that keeps each agent’s status and subject fresh.
Single embedded warden-dbEvery durable store (agents/sessions, pipelines, plans, projects, schedules, backends, snapshot metadata, …) lives in one embedded ScrivaDB (github.com/srjn45/scriva, SyncModeNone) rooted at <data_dir>/warden-db/ (default ~/.warden/warden-db/) — schema 2, the v11 layout. A mutation appends one record instead of rewriting a whole JSON file; still no database server to run. Existing installs are cut over once by warden update — see Upgrading & storage migration, the Schema 2 upgrade guide and Agent store integrity.
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

Warden has four separate entities: Project, Agent, Terminal, and Pipeline. A project’s agents[], pipelines[], and terminals[] are complete, authoritative membership id lists, including nested agents and pipeline job agents. Clients render these stored lists instead of inferring membership from paths or scanning project_id back-references.

Agents store parent_id, child_agents[], and child_pipelines[]. A pipeline stores parent_agent_id (empty for an operator-created pipeline) as the reverse of its owner’s child_pipelines[]. Pipeline job agents never appear in the owner’s child_agents[]: reach them through the pipeline’s jobs and their pipeline_id. Pipelines retain their own DAG; terminals are project leaf members with no agent children, AI transcript, or cost.

Opening a new project leaves it empty; spawn agents and terminals explicitly. Reopening restores previously hibernated members but never auto-spawns an orchestrator. Record creation alone is distinct from launching a live session; spawn wires membership and parent/child edges. Stored lists are ordered and de-duplicated, and may retain ids whose records are unavailable or hibernated.

See Agents & lifecycle for the seven presented states and recovery rules.

When projects are organized into a project group, you run one orch-<project> orchestrator per project (spawned explicitly — the daemon does not create one on open). 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. You spawn the orch-<project> agent into each project, and warden injects peer awareness (group name + sibling names) into every orchestrator at each relaunch.

Through v10 (schema 1) warden kept a separate store per concern — sessions-db/ (with active and closed collections, imported from the older sessions/ and closed/ JSON), agents-db/, pipelines-db/, plans/plans-db/, projects/, backends/ and others. The v11 major (schema 2) moves all of them into one embedded ScrivaDB at <data_dir>/warden-db/. Agents are one collection there; the active/closed split is no longer a pair of stores. This is an internal storage change — the daemon’s REST API and the CLI/MCP clients are unchanged.

  • Fresh install: stamped at schema 2 on first boot; only warden-db/ is created.
  • Existing install: warden update runs a one-time, offline, journaled cutover for you: it stops the daemon, takes an immutable checksummed backup under backups/pre-schema2-<timestamp>/, exports to a staging store, proves it equal to the legacy data (record counts, canonical hashes, relationships), atomically promotes it, and only then moves the schema ledger (<data_dir>/schema.json) to 2. On any failure the previous binary and data are restored. Preview with warden update --plan; the manual path is warden migrate --plan / warden migrate --apply.
  • Nothing is deleted. The schema-1 directories are kept as inert copies and are never read at runtime; the daemon runs on warden-db only (no dual-write, no fallback). Compare them with warden-db using warden inspect legacy.
  • Integrity and repair: verify the store offline with warden repair warden-db --dry-run. Ownership, health checks and the repair procedure are in Agent store integrity.