Expand description
Background-process bootstrapper for the MCP path.
Post-pair, an agent shouldn’t have to ask the user “start the daemon?” —
wire_pair_confirm invokes ensure_daemon_running + ensure_notify_running
so push/pull and OS toasts are already armed by the time the agent surfaces
“paired ✓” back to chat.
§Idempotency
Each subcommand writes its pid record to $WIRE_HOME/state/wire/<name>.pid
on spawn. The next call reads the record and skips spawning if the pid is
still alive. Stale pid files (process died) are silently overwritten.
§Pid-file shape (P0.4, 0.5.11)
The pid file used to be a raw integer (12345\n). Today’s debug surfaced
a process running an OLD binary text in memory under a current symlink,
and wire status had no way to detect that. The pid file is now a
versioned JSON record:
{
"schema": "wire-daemon-pid-v1",
"pid": 12345,
"bin_path": "/usr/local/bin/wire",
"version": "0.5.11",
"started_at": "2026-05-16T01:23:45Z",
"did": "did:wire:paul-mac",
"relay_url": "https://wireup.net"
}Readers are TOLERANT of the legacy int form for one transition cycle —
read_daemon_pid falls through to raw-int parse when JSON decode fails
and reports version: None so callers can degrade gracefully.
§Wait-until-alive
On spawn, we wait briefly for the child to be alive before persisting the pid file. A concurrent CLI seeing the file pointing at a not-yet-bound PID is the “daemon reports running but can’t accept connections” race spark flagged in our P0.4 design call.
§Detachment (Unix)
Spawned with stdio nulled. Since wire mcp runs without a controlling
TTY (it’s a stdio MCP server, not a login shell), the spawned children
inherit no TTY → no SIGHUP arrives when the parent exits, so they
survive a Claude Code restart cycle. PIDs are reaped by init.
Worst case: a child dies; the next wire_pair_confirm call respawns it.
No data is lost (outbox/inbox is on disk, content-addressed dedupe).
Structs§
- Daemon
Liveness - Snapshot of daemon liveness state read through ONE consistent
view. Consumed by
wire status,wire doctor’sdaemoncheck, anddaemon_pid_consistencyso all three surfaces agree by construction — issue #2 root cause was three call sites that each computed liveness independently and disagreed for 25 min. - Daemon
Pid - Versioned daemon pid record — the JSON form written by 0.5.11+.
- Daemon
PidGuard - Drop guard for a claimed daemon-pid singleton. On drop, removes the pidfile only if it still names the pid we wrote — protects against the case where another daemon raced in after we exited the singleton check but before we wrote, and we don’t want to wipe their record on our exit.
- Last
Sync Record - Versioned record written by
wire daemonafter each successful sync cycle. Readers (wire status,mcp__wire__wire_status,mcp__wire__wire_sendannotations) inspect it to surface “is the sync loop alive RIGHT NOW?” — distinct from “is there a process withwire daemonin its cmdline?” (the existing pidfile- alive check), which can be true while the loop has been wedged for minutes. v0.14.2 (#162): closes the silent-send class where the MCP surface reportsstatus:"queued"while no one is actually pushing.
Enums§
- PidRecord
- Result of reading a pid file. Distinguishes legacy-int (no metadata) from JSON (full metadata) so callers can degrade gracefully.
Constants§
- DAEMON_
PID_ SCHEMA - Schema string written into every JSON pid file. Bumped if the pid-file shape ever changes incompatibly. Readers warn on unknown schema.
- LAST_
SYNC_ FILE_ SCHEMA - Schema string written into every JSON last-sync file. Bumped if the shape ever changes incompatibly. Readers tolerate any schema string + fall back to “unknown last_sync” when they don’t recognize it.
Functions§
- claim_
daemon_ singleton - Claim the daemon-pid singleton by writing this process’s pid +
metadata to the pidfile. Callers should first check
daemon_singleton_holder()— if Some, bail rather than overwrite. - daemon_
liveness - Read the daemon pid file + pgrep in one shot, producing a snapshot
every caller can interpret identically. The point of this helper
is that three independent callers used to compute liveness three
different ways (#2): pidfile-pid-alive (cmd_status), pgrep-only
(early check_daemon_health), neither (check_daemon_pid_consistency).
Now all three flow through the same
DaemonLiveness. - daemon_
singleton_ holder - Inspect the daemon singleton state. Returns
Some(pid)iff the pidfile names a livewire daemonprocess — i.e., a singleton is currently held by another in-flight daemon. ReturnsNoneif the pidfile is missing, corrupt, or names a dead process. - daemon_
version_ mismatch - Check the running daemon’s version against the CLI’s CARGO_PKG_VERSION. Returns Some(stale_version) if they disagree, None if they match (or no daemon, or legacy-int pidfile without version info).
- ensure_
daemon_ running - Ensure a
wire daemon --interval 5process is alive. ReturnsOk(true)if a fresh process was spawned,Ok(false)if one was already running. - ensure_
notify_ running - Ensure a
wire notify --interval 2process is alive (OS toasts on every new verified inbox event). Returns true if newly spawned. - last_
sync_ age_ seconds - Convenience: the wall-clock age (in whole seconds) of the most recent
sync, or
Noneif no record exists / the timestamp can’t be parsed. Negative ages (clock skew between daemon + reader) are clamped to 0. - pid_
is_ alive - True iff
pidis currently a live OS process. Delegates to the platform-aware check (/procon Linux,kill -0on other Unix,taskliston Windows) so callers never disagree across OSes. The old localkill -0path false-negatived on Windows (nokill), makingwire status/doctorreport the daemon DOWN while it was alive. - read_
last_ sync_ record - Read the last-sync record. Returns
Noneif missing/corrupt — every caller should treat that as “unknown sync state, daemon may never have run” and surface it accordingly. - read_
pid_ record - Read a pid file, tolerating both JSON and legacy-int forms. Never
panics — corrupt input becomes
PidRecord::Corrupt. - write_
last_ sync_ record - Write the last-sync record. Called by
cmd_daemonafter each cycle (including –once). Best-effort: any error logs to stderr but does NOT abort the daemon loop — a wedged pidfile path shouldn’t take the sync loop down with it.