Skip to main content

Central daemon and workspaces

autodev can operate one repository entirely in process. The central daemon is optional: use it when one machine should have a single runtime-store owner for several repositories, or when the control workspace and CLI need to share that owner.

The planning board does not move into a database. Stories, epics, decisions, and other planning entities remain Markdown in each repository and continue to be committed to Git.

Runtime history does move: the daemon alone opens one global SQLite database at ~/.local/share/autodev/fleet.sqlite. Registered workspaces are scoped inside that store; there is not one database file per workspace. A checkout-local .autodev/fleet.sqlite is only an unregistered repository's store or legacy history waiting to be imported. ~/.local/share/autodev/workspaces.json maps workspace ids to their Git roots, while ~/.local/share/autodev/daemon.json records the current local endpoint and machine credential.

Understand the process boundary​

One process, two loopback listeners with different jobs:

ListenerDefault addressServes
Client surface127.0.0.1:7777The browser client, its control websocket, /api/*, the public summary, and this documentation
Machine API127.0.0.1:7788Versioned JSON under /v1, workspace registry access, and daemon-owned fleet stores

They are separate sockets so that exposing the machine API cannot expose an operator console: the client is not served on 7788 at all. See Ports and the API surface for the full route table and the authority model on each.

autodev serve used to run the client as a second process holding its own --root. It still starts, and still works, for one release. It is no longer the supported way to run the console, and a project's bytes are now resolved in exactly one place.

Start and inspect the daemon​

autodev daemon start --act-token <token>
autodev daemon status

The console is then at http://127.0.0.1:7777/?token=<token>, and the documentation at http://127.0.0.1:7777/docs without any token.

On one machine with one person, the token is friction with nothing behind it:

autodev daemon start --single-user

Every client that can reach the loopback endpoint then holds act, and none is asked for a token. It is refused on any non-loopback address, because there the claim it makes about who can open the socket is not weaker but false. The banner says which mode a daemon came up in, every start.

The default bind is loopback. When no bearer token is supplied, start generates and records one for local clients. Do not copy that token into repository files, transcripts, or screenshots.

The daemon's published API is generated from the Rust request and response types:

autodev daemon openapi

docs/api/openapi.json in the source repository is generated with daemon openapi --write; it is not a hand-maintained specification.

Register a repository​

From a Git repository:

autodev workspace register .
autodev workspace list

Pass --id <ID> when the directory-derived id is not the identity you want. A daemon refuses an unregistered workspace by name; it never guesses that two paths describe the same project.

Store resolution checks that the recorded endpoint is live before using it. A live daemon plus a matching registration selects the daemon-owned store. A dead or stale endpoint makes a registered workspace's runtime store unreachable; it never selects a checkout-local database containing a different history. Check daemon status before any store-backed command against a registered workspace.

Give the fleet a checkout of its own​

By default the fleet works in your checkout: it cuts worktrees there and lands applied waves onto the branch you have open. That is fine for one person on one machine and unpleasant the moment you are editing while a wave applies.

autodev workspace materialise

The fleet then works in a clone under the data directory, and your checkout stops being the fleet's workspace. Two consequences follow, and both are commands rather than assumptions.

Work travels in both directions, by name​

From the forge, at start. When a drive lane starts, the fleet's checkout first follows origin's copy of its branch — fast-forward only — so a PR merged on the forge reaches the fleet at the next start without you pulling anything. A workspace with no origin, a divergence, or an unreachable forge is said by name and the lane starts from what it has:

sync main from origin advanced 830a409cd3a2 → 9f21ee7e2c6b (2 commits)

Into the fleet. Anything that resolves the fleet's checkout brings it up to what you have committed, and says so. A fetch alone would move objects and no branch, which looks exactly like being up to date:

fleet checkout main advanced 4078441cd3a2 → 7214ff9e2c6b (9 commits)

Back to you. Applied waves land in the fleet's clone, so collect them:

autodev workspace sync
sync autodev main advanced d3d1b15f0a41 → 9cebba9a1f2c (3 commits) — from the fleet's checkout at …

Both directions are fast-forward only and neither ever resets. A checkout that has diverged, or has uncommitted changes in the way, keeps the commit it has and says which:

sync autodev main kept d3d1b15f0a41 — 2 uncommitted path(s) are in the way; commit or stash
them and run this again — nothing was reset

workspace sync exits non-zero when nothing arrived, so a script can branch on whether work moved without reading prose. Neither direction pushes anywhere: publishing to a forge is a separate, granted capability, and carrying work between two local checkouts is not a route to it.

Carry existing history across​

A repository that has already run a local fleet may have .autodev/fleet.sqlite. Import it while registering:

autodev workspace register . --import

Or import after registration:

autodev workspace import .

Import proves the persisted counts survived before reporting success. A registered workspace with an unimported legacy store reports that state and names the old path; it does not present the workspace as a fleet that never ran.

Daemon data and the workspace registry live under the platform data directory, normally ~/.local/share/autodev/ on Linux. The registry scopes every workspace inside the one fleet.sqlite; the daemon is the only process that opens it.

Replace a running binary safely​

autodev daemon stop and autodev daemon restart report the required sequence, but do not signal or re-exec the daemon. fleet shutdown owns the fleet-wide process floor and the timeout policy.

For every workspace that can dispatch:

autodev fleet shutdown --reason "replace autodev binary" --principal operator --wait 600

Only after shutdown reports frozen with zero process groups should the supervisor stop the daemon, install or build the replacement, and start it again. Verify it before starting the fleet:

autodev daemon status
autodev workspace list
autodev fleet start --principal operator

Stopping the daemon stops the drive, because the daemon owns it. Its banner names every workspace it drives and every registered one it does not, with the reason — a daemon that came up without a drive never reads like one that came up with it:

drive autodev driving in /home/you/projects/autodev — a tick every 5s
drive other not driven — the drive lease is held by `fleet drive` (pid 41099)

--no-drive starts a daemon that drives nothing, for an operator who wants the loop in their own terminal. A hand-run fleet drive against a workspace the daemon is driving is refused and names the holder, so there is one reconciler rather than two racing.

Never kill a drive during a worker or judge turn. If a process died unexpectedly, start with fleet status, fleet doctor, and the event log; recover through typed fleet verbs rather than editing the store.

When not to use it​

Stay in local mode when one repository and one operator process are enough. Board reads and local fleet operation remain supported without registration, and adding a service merely to read a story would weaken the repository-native model.