Skip to main content

The daemon

PreviewAvailable on: WindowsShips in the preview channel only. Not a stable release.

The single durable writer: what it owns, why it outlives the app, and the rules for adding state to it.

The daemon is the process that makes Harmony's durability claims true. Clients come and go; the daemon holds the state.

What it owns

DomainContents
SessionsTerminal and agent sessions, scrollback, attachment state
OrchestrationPlans, plan revisions, runs, delegation edges
WorkDurable tasks and their states
ArtifactsMarkdown outputs attached to work
A2AAgent-to-agent messages and delivery status
ChannelsDurable rooms and the mention inbox
PrincipalsRegistered identities sessions run as

The single-writer rule

The daemon is the single durable writer for everything above. Two clients can watch the same run without corrupting it because neither writes directly — they issue RPCs and the daemon serialises the result. Where several participants could write the same record, a writer lease decides who may.

IMPORTANT

If you are adding state that must survive a restart, it belongs in the daemon. State kept in the renderer is a view, and views are disposable. This is the most common architectural mistake in this codebase.

Why it survives app close

The daemon is a separate process from the Electron app. Closing the window detaches a client; it does not signal the daemon. That single fact explains:

  • Terminals and scrollback returning after a restart
  • /detach leaving an agent working
  • The CLI seeing tasks created in the desktop
  • A run continuing while no window is open

Instance isolation

Everything the daemon owns is namespaced by the data-suffix environment variable: data directory, socket path, auth token, and pipes. Development builds use -dev.

A client only ever reaches the daemon matching its own suffix. When the CLI reports that it cannot find a daemon while the app is plainly running, mismatched suffixes are the usual cause — not a crashed daemon.

Transports into it

CallerPath
Electron mainNamed pipe / unix socket via a daemon client
TUIDirect daemon RPC client
CLIPipe server, RPC router
MCP hostsMCP server → pipe

RPC method strings are namespaced (nala.*) and grouped into families — sessions, tasks, artifacts, settings, providers, A2A, and more.

Structured errors

Failures carry codes, not just prose. Transport codes (DAEMON_NOT_CONNECTED, DAEMON_TIMEOUT, DAEMON_CANCELLED, DAEMON_RPC_ERROR) are a separate union from domain errors, and each carries a retryable flag.

This matters for callers: "the daemon is unreachable" and "the model refused" should never be handled by the same branch.

Adding to the daemon

Before adding a new RPC family, check:

  1. Does it need to be durable? If not, it may belong in a client.
  2. Who is the writer? If more than one participant can write, it needs a lease.
  3. What is the failure code? New failure modes need to fit the structured error union, not return a bare string.
  4. Is it observable? State that cannot be inspected from nala doctor or a task listing is state that will be debugged by guesswork.