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.
| Host | Where agents run | Turn it on | Use it for |
|---|---|---|---|
| In-process (default) | Inside the app's own process | Nothing to do | One app at a time |
| Local daemon | One background process on this computer, shared by every window | useDaemon setting | Both apps, several windows, runs that outlive a window |
| zidane-server | Another machine, over a WebSocket | --host, or a saved host | A 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#
-
Open the setting in either app:
App Where Terminal Settings › Agent › Sessions › Use daemon runtime Desktop Settings › Advanced › Experimental › Shared daemon runtime. Settings › Interface › Terminal & browser links to it. -
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:
| App | Effect |
|---|---|
| Terminal | New sessions run in the daemon. Sessions already open stay where they are. |
| Desktop | The 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#
| Way | Effect |
|---|---|
--no-daemon | Runs zidane or zidane run in-process, whatever the setting says |
ZIDANE_NO_DAEMON=1 | Same, for every launch in that shell |
zidane run --context other than process | Forces 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.
| Data | With the daemon | Without the daemon |
|---|---|---|
| Running sessions and streaming transcripts | Live in every window | Each app runs its own agents. Saved turns show up after a reload. |
| Approvals and questions | Answer from any window | Only the window that started the run |
| Queue and steer | One queue, seen by every window | Per app |
| Todos and artifacts | Live in every window | Saved with the session |
| Terminals | Run in the daemon and keep running with no window | No desktop terminals |
| Runs after you close the window | Keep going, because the daemon owns the run | Need the app that started them to stay open |
| Drafts, search, who is looking at a session | Shared | Not shared |
| Session list | Live | Shared through sessions.db |
| Settings that shape agents (tool approvals, limits, compaction, enabled skills) | The daemon owns the file and tells every window | Shared through state.json |
| Provider sign-ins, MCP sign-ins, keybindings, saved hosts, safelist | Shared through files | Shared 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:
| Feature | Why |
|---|---|
| Desktop terminals | Terminals run in the daemon. Settings › Interface › Terminal & browser says "Terminals run in the daemon." and links to the setting. |
| The Main session | It drives other sessions, which must run in a shared host |
zidane attach | It 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:
| Verb | Does |
|---|---|
create | Start a worker session in a folder, with a prompt |
send | Send a prompt to a worker |
steer | Add guidance to a worker's current run |
cancel | Stop a worker's run |
read / transcript | Read a worker's state or its last turns |
list | List 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.
| Flag | Does |
|---|---|
--session <id> | Attach to this session (a prefix of the id works) |
--new | Start 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.
| What | Value |
|---|---|
| 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) |
| Override | ZIDANE_DAEMON_SOCKET=<path> |
| Dev builds | dev.sock instead of daemon.sock when ZIDANE_DEV is set and not 0 |
| Permissions | Folder 0700, socket 0600: only your user can connect |
| Discovery | No pid file. A client connects to the socket. If nothing answers, it starts a daemon. |
| Idle exit | After 30 minutes with no window connected and no run, queue or question in flight |
| Upgrade | A 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#
zidane daemon --verbose| Flag | Default | Does |
|---|---|---|
--socket <path> | the path above | Listen on another socket |
--idle-exit <ms> | 1800000 (30 min) | Idle time before exit. 0 never exits. |
--verbose | off | Log 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#
| App | Where |
|---|---|
| Terminal | A health dot in the footer, shown while the daemon is on |
| Desktop | The 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#
| Symptom | Cause | Fix |
|---|---|---|
| Desktop terminals do not open | The daemon is off, or not connected yet | Turn 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 start | Install the terminal app (zidane), or start zidane daemon yourself |
| A protocol version mismatch error | The window and the daemon come from different releases | Upgrade the older side, then use Restart daemon (desktop) or quit every window |
| An old daemon keeps running after an upgrade | It waits for its runs to end before it exits | Stop the runs, or use Restart daemon |
| A dev build from a checkout does not see the installed daemon's runs | ZIDANE_DEV gives dev builds their own dev.sock | Expected. Unset ZIDANE_DEV to share the installed daemon. |
| You need to see what the daemon sends | Debugging a client | Run zidane daemon --verbose, or set ZIDANE_IPC_DEBUG_STREAM=1 to mirror every signal |
| Older messages of a long session do not load | The history worker, the thread that reads older transcript pages, fails on this machine | Set 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).