DuoDuo runs as a daemon on your machine, and looking after it is mostly DuoDuo's own work. Ask whether everything is healthy, what went wrong last night, or to upgrade itself, and it answers from the same command line an operator would use. This page covers what it looks at, and why.
Give DuoDuo the operations skills#
npx -y skills add https://github.com/openduo/duoduo --global --yes --skill duoduo-admin duoduo-channel-admin duoduo-runtime-admin
The three skills carry the procedures for the version you run. duoduo-admin covers how the install fits together, where things live, upgrades, and archiving sessions; duoduo-channel-admin covers channels; duoduo-runtime-admin covers daemon settings, diagnostics, runtimes, sessions, jobs and the event history. After that, ask in plain language.
The skills are published separately from the npm package, so a DuoDuo upgrade does not refresh them. Manager's upgrade flow does, and so does asking DuoDuo to refresh its skills.
Check before you change#
Before touching anything, DuoDuo reads what the running daemon reports: duoduo daemon status for process and scheduler health, duoduo daemon config for the paths and settings in effect, and duoduo daemon logs for the readable tail.
What is running right now#
When tool calls are in flight, status lists each one: which session, which tool, how long ago it started. When no tool calls are open, the list is empty.
It passes no judgment. No threshold, watchdog or timeout decides a call has taken too long: status reports, and /cancel acts. An empty list means only that no tool call is open, which is also true while the model is writing.
Read the event history#
Ask what happened on a given day, in a given conversation, and DuoDuo reads it from the Spine WAL, the source of truth. On a busy host a single day is tens of megabytes of JSONL, so it goes through duoduo spine instead of the files: cat prints speech in full and folds each tool call together with its result, and show prints one event whole.
duoduo spine also protects the reader's context. It counts a window before pulling it in, continues a long read from the last event instead of starting over, and refuses to print an unfiltered day unless asked to.
The by-id index is a bounded recency cache, compacted at boot, so it does not grow without limit on a long-lived host. ALADUO_SPINE_INDEX_RETENTION_DAYS sets how much it keeps. When an id falls out of it, the read falls back to scanning that day's partition, so nothing becomes unreadable.
Upgrade#
Ask DuoDuo to upgrade itself. duoduo upgrade does the whole job: it installs into the prefix that owns the binary currently running, reinstalls and restarts the channels, restarts the daemon with the version as the reason, and waits for a health check before reporting success.
The prefix is why it is one command. If DuoDuo was installed outside npm's default global prefix, which is how the menubar app installs it, a plain npm install -g writes a second copy elsewhere, leaves the running binary untouched, and reports success anyway.
It is safe to run from inside a DuoDuo conversation. The work is handed to a detached process that the daemon restart cannot kill, and the conversation that asked can be woken when it is done.

Installing a new package by any other route does not hot-swap the running daemon. It has to be restarted, with a reason that says what changed: the reason reaches every session that wakes afterward, and a session whose turn the restart ended has no other way to learn why.
Upgrades that cross a major boundary occasionally need a manual step. Those are called out in the release notes.
How the daemon is reached#
The daemon serves its full interface on a unix socket with owner-only permissions, so the operating system decides who may drive your agent. The CLI, the channels and the menubar app find it by default, with nothing to configure.
127.0.0.1:20233 is a read-only surface for the dashboard. It is pinned to loopback, answers only the methods the dashboard needs, and does not speak WebSocket. Control belongs to the CLI; nothing should drive the daemon through that port.
| Setting | What it controls |
|---|---|
ALADUO_PORT | The read-only TCP port, default 20233. Always loopback. |
ALADUO_DAEMON_SOCKET | Absolute path override for the socket. Rarely needed. |
ALADUO_DAEMON_HOST + ALADUO_REMOTE_PORT + ALADUO_DAEMON_TOKEN | The opt-in remote listener. |
Reaching a daemon from another machine#
Remote access is opt-in and always carries a token, minted with duoduo daemon token new and stored with owner-only permissions. The listener starts only when host, port and token are all present, and the remote port must differ from the read-only one. A misconfiguration, such as a public address with no token or a port collision, refuses to start instead of exposing anything.
Tool access is an allowlist#
Claude sessions receive a fixed core tool surface. Extra built-in tools, such as web search or fetch, are added per channel, in its kind or instance descriptor under claude.tools; the list only ever adds to the core set. Codex manages tools separately. A scheduled job can carry the same block in its own frontmatter, with host-wide defaults in kernel/config/job.md (see Loops & Jobs).
To give a channel web search, tell DuoDuo which channel. If a tool still seems missing, ask it to check the configuration that session received, with duoduo session config.