Storage
Backend
Everything is persisted in a single SQLite database accessed synchronously,
with SQLite bundled into the cafleet binary. There is no separate database
daemon to operate, monitor, or back up — the database is a single file.
The default database path is ~/.local/share/cafleet/cafleet_v6.db (XDG state
directory), expanded once at config load time. Override with the
CAFLEET_DATABASE_URL environment variable, e.g.
sqlite:////var/lib/cafleet/cafleet_v6.db; see
CLI options for the full CAFLEET_* variable set.
Concurrency: PRAGMA busy_timeout=5000 lets SQLite retry for up to 5 s
before returning SQLITE_BUSY. Member registration uses an IMMEDIATE
transaction to serialize writers, including the active-monitor recheck and
member/placement inserts. Polling is read-only. Fleet bootstrap holds its
write transaction across pane creation; its lock can therefore last for the
multiplexer call.
Relational model
Every routing and indexed field is a typed column; the only JSON TEXT blob is
members.member_card_json. See data model for the full
schema.
Schema management
The schema is managed by a chain of SQL migrations embedded in the binary;
the applied versions are recorded in the refinery_schema_history table.
Operators run plain cafleet setup once before starting the server — it is
the migrations-apply path. Existing data (message
history included) is preserved, so the command is idempotent and safe to
re-run after every upgrade.
Every non-setup command checks the schema version before running and fails
with guidance naming cafleet setup — never a raw SQLite error; doctor
reports instead of blocking. The exact rules and error strings are in
CLI options.
Duplicate-monitor detection reports the conflicting fleet and member ids; the operator chooses which monitor to retain. The complete diagnostic, transaction rollback and concurrent-write behavior are owned by setup's migration contract, using the active-monitor index predicate.
Recovering duplicate active monitors
- Stop new registrations against the affected database. Choose the monitor to retain from the reported ids.
- Use the preceding release that supports the old schema, from a separate
binary path, with the same
CAFLEET_DATABASE_URLand backend configuration directories. The new binary'smember deleteis blocked by its behind-schema guard. - The failed new
setupmay have successfully updated assets: its DB and assets halves run independently. If so, use the old binary'ssetupfor the same backend(s) to restore assets compatible with that binary. Then use the old binary'smember delete <surplus-id>, one isolated invocation per surplus monitor. The database still has the old schema, so these old commands can pass its schema guard. - Run the new binary's
setupagain. After the schema upgrade succeeds, automatic downgrade to the old schema is refused.
Assets-install recording
The asset_installs table records, per coding agent and install path, the
CLI version that last installed the skills and preset
(where one exists) there — not a schema version. The assets
half of cafleet setup upserts one row per installed agent — all three
agents on the no-flag form — keyed on the
agent's resolved config path. Every fleet-scoped command validates these
rows before running — the
stale-assets guard — so the
assets can never silently go stale after a CLI upgrade. cafleet doctor
reports the per-agent detail. See
data model for the table schema.
No physical cleanup
Deregistered members and their messages remain in the database forever. There is
no background cleanup loop. Active query paths filter status='active' so
dead rows are invisible to normal traffic; the WebUI is the only consumer
that surfaces deregistered members (so their inbox history can be inspected).
owner_member_id convention
The broker sets owner_member_id = recipient_member_id on every delivery
message, so recipients discover their inbox by polling for messages whose
owner_member_id equals their own member id — trading per-conversation
grouping for the simple fire-and-forget inbox discovery that suits coding
agents. owner_member_id is an opaque routing key.