A Rust control plane that runs coding agents in disposable containers and holds the authoritative state of the fleet. You dispatch a task; the daemon provisions an isolated agent, drives it through a build → check → review lifecycle under hard cost and time budgets, opens a verified-mergeable PR, and streams every state change to a thin desktop cockpit — surviving crashes and restarts because all state lives in SQLite, not memory.
Status: the control plane and workflow layer are feature-complete and tested (116 workspace tests). The product shell around it — plugin embedding, a design pass, remote control — is on the roadmap. See honest status.
Running autonomous coding agents well is a systems problem, not a prompting problem. An agent that edits code is the easy part; the hard part is everything around it — isolating it so it can't wreck your machine, capping what it can spend, retrying through rate limits, keeping authoritative state when a process dies mid-run, and giving an operator one place to watch and steer the whole fleet. Command Center is that surrounding machine: a small, durable daemon that treats each agent run as a supervised, budgeted, resumable unit of work.
Two Rust crates behind a thin desktop UI:
fleet-core— the pure domain: the unit lifecycle state machine (Phase), tier/gate policy (the human-in-the-loop "autonomy ladder"), and the events and commands that move a unit between phases. No I/O — exhaustively unit-tested.fleetd— the daemon: an axum HTTP/WS server, a per-unit async driver, the container Runner seam, retry/budget logic, and the SQLite store.cockpit/— a Tauri + Svelte desktop app ("Fleet Command") that bundlesfleetdas a sidecar and renders fleet state live.
flowchart LR
UI["Tauri cockpit<br/>(thin renderer)"]
subgraph D["fleetd daemon (authoritative)"]
API["axum HTTP / WS API"]
DRV["per-unit driver<br/>build -> check -> review"]
REC["reconcile<br/>(startup + periodic loop)"]
STORE[("SQLite: fleet.db")]
end
RUN["Runner seam<br/>disposable container per agent"]
UI -- "POST /missions, commands" --> API
API -- "WS /units/:id/stream (replay + live)" --> UI
API --> DRV
DRV -- "provision / exec / reap" --> RUN
DRV -- "append events" --> STORE
REC -- "reap orphans, halt stranded" --> RUN
REC <--> STORE
The parts worth a closer look:
The daemon — not the UI, not the container — is the source of truth. Every unit's
phase and full event log is persisted to SQLite as it happens. Because the world
(running containers) can drift from that truth, fleetd reconciles the two in two
places (crates/fleetd/src/reconcile.rs):
- On startup, before accepting connections: reap orphan containers left by a
crash and mark stranded units
haltedso they can be resumed. - On a periodic loop (
CC_RECONCILE_SECS, default 30s): the same convergence, continuously — but crucially it spares any unit that still has a live driver, so steady-state reconciliation reaps genuine orphans without ever disturbing in-flight work. (reconcile_live/reconcile_tick.)
Renderers are thin: they hold no authoritative state, they subscribe to it. A
client connects to GET /units/:id/stream?since=<seq>; the server replays the
persisted history from that sequence, then tails live events over the same socket
(server.rs). Reload the cockpit and it rebuilds from
since=0; nothing is lost. Covered end-to-end by an integration test that drives a
real WebSocket client against the running server.
Kill the daemon at any point and restart it: because state is in SQLite and the
driver is event-sourced, the new process reconstructs every unit from disk. A resumed
real unit reuses its kept container volume and skips the already-frozen test oracle;
a stranded unit is halted by startup reconciliation and can be resumed.
Prove it yourself — no Docker or API key needed:
cargo build -p fleetd --bin serve
node scripts/demo-restart-recovery.mjsdaemon #1 up
dispatched demo unit u1
before kill: phase=done events=26
daemon #1 killed (SIGKILL)
daemon #2 up (same SQLite db, cold memory)
after restart: phase=done events=26
PASS — 26-event history for u1 survived a hard restart, restored from demo-restart.db by a cold process.
An agent run is bounded on every axis
(retry.rs, driver.rs):
- USD budget — a hard per-unit cap, plus a rolling-24h global spend ceiling
(
CC_GLOBAL_USD_CAP) that refuses new missions with429once hit. - Wall-clock cap — a backstop against an agent that loops or stalls and burns money without tripping the cost cap between steps.
- Rate-limit handling — on an Anthropic throttle the driver accounts the attempt,
emits a "rate limited" event, waits out an exponential backoff with a cap, and
re-execs; after ~1h of accumulated throttle time it parks the unit at
NeedsHumanrather than spinning.
Everything container-specific lives behind the Runner trait
(runner.rs). LocalDockerRunner launches each agent
in a disposable cc-agent container (network open, filesystem and secrets isolated);
FakeRunner replays scripted output so the entire lifecycle is testable without
Docker, git, or a real model.
Fastest path to "it works" — a $0 demo mission, no Docker or API key:
# 1. Build the workspace
cargo build --release
# 2. Run the daemon (binds 127.0.0.1:8787, persists to ./fleet.db)
./target/release/serve # Windows: .\target\release\serve.exe
# 3. Dispatch a demo mission (scripted agent, metered $0, no container)
curl -s -X POST http://127.0.0.1:8787/missions \
-H 'content-type: application/json' \
-d '{"task":"add a sum() helper with tests","tier":"t1","mode":"demo","min_review_rounds":1}'
# → {"unit_id":"u1"}
# 4. Watch it walk its phases
curl -s http://127.0.0.1:8787/units/u1For the desktop cockpit, a real (Docker + ANTHROPIC_API_KEY) mission, resuming a
halted unit, and troubleshooting, see the full quickstart —
every command in it is cross-checked against the code. Run the tests with:
cargo test --workspace # 116 tests; Docker/network ITs are #[ignore]d| Path | What |
|---|---|
crates/fleet-core |
Pure domain: lifecycle state machine, tiers, gates, events |
crates/fleetd |
The daemon: HTTP/WS server, driver, Runner seam, retry/budget, SQLite store |
cockpit/ui |
Tauri + Svelte desktop cockpit ("Fleet Command") |
deploy/agent-image |
The cc-agent container image the real runner launches |
scripts/ |
Runnable demos (e.g. restart recovery) |
docs/ |
Quickstart, architecture vision, roadmap |
One-time, per clone:
git config core.hooksPath "$(pwd)/.githooks" # run from the repo root
EG_TOKEN='<token to forbid>' node scripts/embargo-guard.mjs --add-entry <id> # repeat per tokenUse an absolute path. A relative core.hooksPath is resolved against each
worktree's own root, so in a worktree whose branch predates .githooks/ git finds no hook and
commits without checking — a silent fail-open. An absolute path points every worktree back at this
checkout, and the hooks resolve the guard and its denylist by their own location.
This enables the embargo guard (scripts/embargo-guard.mjs), which
blocks commits whose staged content or commit message contains a forbidden token, matching a
normalized sliding window so case, punctuation and line wrapping don't evade it.
The denylist is not committed. It holds salted digests rather than plaintext, but the tokens are
low-entropy, so a digest published next to its salt is just a slow-release copy of the token — a
10-digit one fell to a targeted search in 22.6s on one CPU core. So it lives in
.embargo-guard.local.json (gitignored) locally and in the EMBARGO_GUARD_CONFIG repo secret for
CI. Nothing about the forbidden tokens is in the repository: not the plaintext, not a regex, not a
digest, not a length.
The same check runs as the embargo job in CI, so skipping the hook — or committing with
--no-verify — does not skip the check. It fails closed when no denylist is available.
Feature-complete and tested:
- ✅ Rust control plane — daemon, per-unit driver, unit lifecycle state machine
- ✅ Authoritative state + startup and periodic reconciliation
- ✅ WebSocket state streaming (replay + live) to thin renderers
- ✅ Durable recovery across restarts (SQLite-backed, event-sourced)
- ✅ Retry/backoff, rate-limit handling, per-unit + global budget rules
- ✅ Container isolation via the
Runnerseam; demo mode with no Docker/key - ✅ Tauri desktop cockpit; cross-platform CI (workspace tests + 3-OS Tauri bundles)
On the roadmap (tracked in docs/ROADMAP.md) — the "one-stop
shop for agentic engineering" vision beyond the core control plane:
- ⏳ Hosting other tools inside the cockpit (app-/view-plugin embedding)
- ⏳ Turning the project board from a viewer into a dispatch surface
- ⏳ A visual design pass on the cockpit
- ⏳ Remote control (drive the fleet from away-from-desk)
Signed release bundles are wired in CI (release.yml)
and gate on code-signing certificates; no tagged release exists yet.
MIT — see LICENSE.