The daemon¶
Behind the TUI is a long-lived background process: the Agent Factory daemon.
It is the piece that makes sessions survive restarts, keeps automations firing,
and guarantees the TUI, the CLI, and the HTTP API never disagree about what
exists. You rarely interact with it directly, but understanding it explains a lot
about how af behaves.
The single-writer model¶
The daemon is the single writer and owner of all state. Session records,
task definitions, the worktrees on disk — the daemon performs every mutation.
The TUI and the af CLI are pure clients: they render a read-only projection of
the daemon's state and send it RPCs to request changes; they never write state
files themselves.
This is a deliberate design (the "#960 single-writer model"), and it's worth the
paragraph because it's why af doesn't corrupt itself:
- One writer, no clobbering. When two front-ends could both write the same state file, the last writer silently wins and the other's change vanishes. With a single writer, that entire class of bug cannot occur — every change is serialized through the daemon.
- One source of truth. The sidebar you see, the JSON
af sessions listprints, and the HTTP API'sSnapshotall read the same in-memory state. They can't drift, because there is only one authority. - Clients can come and go. Close the TUI and your sessions keep running. Reopen it and it reconnects to the daemon's live state — nothing was lost, because the TUI was never holding the state in the first place.
What the daemon does¶
- Keeps sessions alive. If a session's process dies unexpectedly, the daemon re-spawns it in place — the worktree and record are preserved, so the agent comes back where it was. The always-on root agent is re-created rather than re-spawned — its record is replaced, but its recorded conversation and its tabs are carried across, so it comes back on the same context with the same tab strip. When that conversation cannot be resumed the root still comes back, on a fresh context — an always-on root that exists outranks one that keeps its history — and the application log says which happened.
- Runs the scheduler. All tasks — cron schedules and watch-script triggers — are hosted by the daemon. It arms the timers, watches the scripts, and delivers prompts on time, whether or not a TUI is open.
- Handles usage limits. With auto-resume enabled, the daemon parks a session
that hit a
claude,codex, ordevinusage limit and resumes it — when the banner's reset window elapses, or on thelimit_retry_intervalcadence for a banner that states no reset time (everydevinlimit) — see Usage limits. - Serves the HTTP/JSON API. The daemon exposes every session and task operation over a local Unix socket — see the HTTP API reference.
Lifecycle¶
The daemon starts on demand: whenever you run af and there is work to host
(an enabled task or a root agent), af makes sure a daemon is running.
That means for interactive use you usually don't have to think about it at all.
To keep tasks and sessions running across logouts and reboots, install the daemon's autostart unit once:
af daemon install # systemd user service on Linux, launchd agent on macOS
af daemon status # liveness, supervision ownership, and config freshness
af daemon restart # restart the daemon process; sessions are re-adopted
af daemon uninstall # remove the autostart unit
Because the daemon owns live state, you don't stop it by force while sessions
are running; let af manage its lifecycle. af daemon restart restarts only
the daemon process and re-adopts existing tmux sessions from persisted state.
af daemon status is the right tool when you want to know whether it's up,
whether the installed unit actually owns the responder, whether config edits
have reached it, and where its sockets are. Those checks are read-only; an
unavailable manager or older daemon is reported as unknown rather than guessed.
Sockets¶
The daemon listens on two local Unix sockets under $AGENT_FACTORY_HOME
(default ~/.agent-factory): an internal control socket the TUI and CLI use, and
the HTTP/JSON socket (daemon-http.sock) for the public API. Both are
owner-only (0600) and local — never a TCP port, never the network. See the
HTTP API guide for the transport and auth details.