Skip to content

The daemon

Turn on the local daemon so every terminal and desktop window shares live sessions.

The daemon is a background process on your computer that runs your agents for every window. With it on, a run you start in one app streams live in the other, you answer its approvals from either app, and the run keeps going when you close the window.

It is off by default (turn it on). Without it, each app runs agents in its own process and the two share only files (one engine, two apps).

How a window runs agents#

Each window (the terminal app, a desktop window, zidane attach) connects to a host: the process that runs sessions, the queue, approvals and terminals.

HostWhere agents runTurn it onUse it for
In-process (default)Inside the app's own processNothing to doOne app at a time
Local daemonOne background process on this computer, shared by every windowuseDaemon settingBoth apps, several windows, runs that outlive a window
zidane-serverAnother machine, over a WebSocket--host, or a saved hostA remote box, a team server, a browser

The terminal app picks a remote host first (--host or ZIDANE_HOST_URL), then the daemon when the setting is on, then in-process.

Turn it on#

  1. Open the setting in either app:

    AppWhere
    TerminalSettings › Agent › Sessions › Use daemon runtime
    DesktopSettings › Advanced › Experimental › Shared daemon runtime. Settings › Interface › Terminal & browser links to it.
  2. Turn it on. Both apps read the same setting, so it turns on in both. The desktop app reads the setting every 5 seconds, so it sees a change from the terminal app within 5 seconds.

What a flip does:

AppEffect
TerminalNew sessions run in the daemon. Sessions already open stay where they are.
DesktopThe app stops its own host, connects to the daemon and reloads every transcript. Every session moves.

The setting belongs to this computer. It stays with the window even when a server owns the other settings. Settings sync never sends it to a server.

Turn it off for one launch#

WayEffect
--no-daemonRuns zidane or zidane run in-process, whatever the setting says
ZIDANE_NO_DAEMON=1Same, for every launch in that shell
zidane run --context other than processForces in-process, because the container or sandbox belongs to that launch

zidane attach has no in-process mode. It always connects to the daemon, and starts one if none answers, or to a server with --host.

What it shares#

With the daemon, every window sees the same live state. Some data is shared through files either way.

DataWith the daemonWithout the daemon
Running sessions and streaming transcriptsLive in every windowEach app runs its own agents. Saved turns show up after a reload.
Approvals and questionsAnswer from any windowOnly the window that started the run
Queue and steerOne queue, seen by every windowPer app
Todos and artifactsLive in every windowSaved with the session
TerminalsRun in the daemon and keep running with no windowNo desktop terminals
Runs after you close the windowKeep going, because the daemon owns the runNeed the app that started them to stay open
Drafts, search, who is looking at a sessionSharedNot shared
Session listLiveShared through sessions.db
Settings that shape agents (tool approvals, limits, compaction, enabled skills)The daemon owns the file and tells every windowShared through state.json
Provider sign-ins, MCP sign-ins, keybindings, saved hosts, safelistShared through filesShared through files

Without the daemon, a run guard stops two apps from driving the same session at once. Appearance, layout and display settings stay per app either way (settings). File locations: files and folders.

What needs the daemon#

Everything live in the table above needs the daemon (or a server). Three features have no fallback at all:

FeatureWhy
Desktop terminalsTerminals run in the daemon. Settings › Interface › Terminal & browser says "Terminals run in the daemon." and links to the setting.
The Main sessionIt drives other sessions, which must run in a shared host
zidane attachIt is a client of the daemon

Main session#

The Main session is one pinned session that starts, steers and reads your other sessions. Turn on Main session (terminal: Settings › Agent › Sessions, desktop: Settings › Advanced › Experimental). It needs the daemon. Open it with CtrlShiftM in both apps. Its agent gets a sessions tool:

VerbDoes
createStart a worker session in a folder, with a prompt
sendSend a prompt to a worker
steerAdd guidance to a worker's current run
cancelStop a worker's run
read / transcriptRead a worker's state or its last turns
listList sessions

A worker's events show in the Main transcript as they happen. The Main agent reads them at its next turn. A finished worker wakes it, unless the call set wake: false. You can still open and steer any worker yourself.

Watch from another terminal#

zidane attach is a small text client for the daemon, for SSH sessions and scripts. Plain zidane always opens the full terminal app.

FlagDoes
--session <id>Attach to this session (a prefix of the id works)
--newStart a new session
--host <url>, --token-file <path>, --token <token>Attach to a zidane-server instead

Type a prompt and press Enter. /interrupt stops the run. /exit or /quit leaves, and the run keeps going. An approval takes one letter: Y once, S for the session, A always (adds it to the safelist). Any other answer denies it.

Lifecycle#

The first client that needs the daemon starts it in the background. Clients that start at the same time share one start. You do not have to start it by hand.

WhatValue
Socket on macOS~/.zidane/daemon/daemon.sock
Socket on Linux~/.local/state/zidane/daemon/daemon.sock on a fresh install (the state folder, see files)
OverrideZIDANE_DAEMON_SOCKET=<path>
Dev buildsdev.sock instead of daemon.sock when ZIDANE_DEV is set and not 0
PermissionsFolder 0700, socket 0600: only your user can connect
DiscoveryNo pid file. A client connects to the socket. If nothing answers, it starts a daemon.
Idle exitAfter 30 minutes with no window connected and no run, queue or question in flight
UpgradeA client from a strictly newer release asks an older daemon to exit. A daemon with runs in flight exits when they end. Both apps do this.

Run it yourself#

Terminal
zidane daemon --verbose
FlagDefaultDoes
--socket <path>the path aboveListen on another socket
--idle-exit <ms>1800000 (30 min)Idle time before exit. 0 never exits.
--verboseoffLog connections and streams to stderr

zidane-daemon (from the zidane npm package) takes the same flags but needs Bun, because the session store uses bun:sqlite. The zidane binary embeds Bun, so zidane daemon works without it.

Channels run only in the zidane-daemon program. It takes --no-channels, as ZIDANE_NO_CHANNELS=1. zidane daemon does not start channels. It rejects --no-channels as an unknown option and exits on SIGHUP. The terminal app starts zidane daemon. The desktop app starts it too when the zidane command is installed. Without that command, it runs zidane-daemon with Bun. To use channels on your machine, start zidane-daemon yourself before you open an app. The apps then connect to it.

See its state#

AppWhere
TerminalA health dot in the footer, shown while the daemon is on
DesktopThe daemon menu at the bottom left, shown while the daemon is on

The desktop daemon menu lists running sessions (stop one, or jump to it). It has Stop all runs, Reconnect now, Restart daemon, Relaunch Zidane and Copy diagnostics.

Troubleshooting#

SymptomCauseFix
Desktop terminals do not openThe daemon is off, or not connected yetTurn on Shared daemon runtime. Wait until the daemon menu shows it connected.
Failed to spawn zidane daemon (zidane-daemon): …The client found no daemon binary to startInstall the terminal app (zidane), or start zidane daemon yourself
A protocol version mismatch errorThe window and the daemon come from different releasesUpgrade the older side, then use Restart daemon (desktop) or quit every window
An old daemon keeps running after an upgradeIt waits for its runs to end before it exitsStop the runs, or use Restart daemon
A dev build from a checkout does not see the installed daemon's runsZIDANE_DEV gives dev builds their own dev.sockExpected. Unset ZIDANE_DEV to share the installed daemon.
You need to see what the daemon sendsDebugging a clientRun zidane daemon --verbose, or set ZIDANE_IPC_DEBUG_STREAM=1 to mirror every signal
Older messages of a long session do not loadThe history worker, the thread that reads older transcript pages, fails on this machineSet ZIDANE_NO_HISTORY_WORKER=1 for the process that holds your sessions (the daemon, the desktop app, zidane --no-daemon or zidane-server) and restart it. Pages then load on its main thread.

The daemon and zidane-server#

zidane-server runs the same host on another machine and speaks the same protocol over a WebSocket (zidane-server, connecting clients).

For your own client, zidane/ipc exports startInProcessHost, connectOrSpawnDaemon and the IpcClient type (reference). zidane/ipc/remote connects to a server from Node (reference). The wire format is JSON-RPC 2.0, protocol version 16 (see the protocol).