Storage¶
Backend¶
Everything is persisted in a single SQLite database accessed through
SQLAlchemy 2.x with the sync pysqlite driver. Schema changes are managed
by Alembic, bundled inside the cafleet wheel and applied via
cafleet db init. 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.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.db.
Concurrency: PRAGMA busy_timeout=5000 is set on every connection.
SQLite retries internally for up to 5 seconds before returning SQLITE_BUSY.
Expected contention is low — CLI operations are short transactions (single
INSERT or UPDATE), and multiple agents polling concurrently is read-only.
Relational model¶
Indexed and routing fields are typed columns; the only JSON TEXT blob is
agents.agent_card_json (an AgentCard-shaped document, not queried by
content). See data model for the full schema —
tables, columns, indexes, foreign keys, and the ER diagram.
Schema management¶
The schema is created by a single initial Alembic migration. Operators run
cafleet db init once before starting the server; it is idempotent and safe
to re-run after upgrades, refuses to auto-downgrade a database that is ahead
of the bundled head, and refuses an unversioned database with tables it does
not recognize. Without db init, the first request fails with
OperationalError: no such table: agents.
No physical cleanup¶
Deregistered agents and their tasks 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 agents (so their inbox history can be inspected).
contextId convention¶
The broker sets contextId = recipient_agent_id on every delivery Task.
This enables inbox discovery — recipients poll for messages whose
context_id equals their own agent id to find everything addressed to them.
This trades per-conversation grouping (the typical contextId use case) for
simple inbox discovery, which suits the fire-and-forget messaging pattern of
coding agents. contextId is treated as an opaque routing key.