Skip to main content

Module ensure_up

Module ensure_up 

Source
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§

DaemonLiveness
Snapshot of daemon liveness state read through ONE consistent view. Consumed by wire status, wire doctor’s daemon check, and daemon_pid_consistency so all three surfaces agree by construction — issue #2 root cause was three call sites that each computed liveness independently and disagreed for 25 min.
DaemonPid
Versioned daemon pid record — the JSON form written by 0.5.11+.
DaemonPidGuard
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.
LastSyncRecord
Versioned record written by wire daemon after each successful sync cycle. Readers (wire status, mcp__wire__wire_status, mcp__wire__wire_send annotations) inspect it to surface “is the sync loop alive RIGHT NOW?” — distinct from “is there a process with wire daemon in 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 reports status:"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 live wire daemon process — i.e., a singleton is currently held by another in-flight daemon. Returns None if 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 5 process is alive. Returns Ok(true) if a fresh process was spawned, Ok(false) if one was already running.
ensure_notify_running
Ensure a wire notify --interval 2 process 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 None if 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 pid is currently a live OS process. Delegates to the platform-aware check (/proc on Linux, kill -0 on other Unix, tasklist on Windows) so callers never disagree across OSes. The old local kill -0 path false-negatived on Windows (no kill), making wire status/doctor report the daemon DOWN while it was alive.
read_last_sync_record
Read the last-sync record. Returns None if 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_daemon after 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.