Skip to content

fix(core): don't mistake a stale daemon PID file for a running daemon (V2-1655) - #235

Open
Nic-dorman wants to merge 3 commits into
mainfrom
nic/v2-1655-ant-node-daemon-infostart-trust-a-stale-pid-file-report
Open

Nic-dorman wants to merge 3 commits into
mainfrom
nic/v2-1655-ant-node-daemon-infostart-trust-a-stale-pid-file-report

Conversation

@Nic-dorman

@Nic-dorman Nic-dorman commented Oct 21, 2026 •

Copy link
Copy Markdown
Member

Community report: after an unclean daemon exit, ant node daemon info said the daemon was running (with a console URL that refused connections), status said it wasn't, and start refused to start a new one.

Cause. A daemon that dies without cleaning up (a reboot, an OOM kill, SIGKILL) leaves daemon.pid and daemon.port behind. info and start decided "running" from kill(pid, 0) alone. Once the PID was reused by an unrelated process, they treated it as the daemon. status asks the daemon over HTTP, so it disagreed, and its "Start it with: ant node daemon start" hint went nowhere. Only stop checked that the PID really is the daemon.

Fix.

  • info and start classify the PID file through one helper (recorded_daemon): live and identifiable as the daemon, which is the check stop already applies before signalling.
  • start removes stale PID/port files before spawning. Otherwise its port poll reads the dead daemon's port straight back as the new daemon's.
  • macOS gets a real identity check (executable name via proc_pidpath, as Windows does), where it previously trusted any live PID. This also tightens stop on macOS.
  • info's help text and README no longer say its output is "always JSON". It has been human-readable unless --json is passed since 920f872, which is the convention for every command. The e2e skill already uses info --json.
  • status and stop logic are unchanged.

Every distribution (install.sh, install.ps1, npm, release archives, the Linux .deb) names the binary ant/ant.exe, which is what the identity check requires.

Linear issue

Closes V2-1655

Risk tier

  • T0 — docs / tooling / CI / pure UX-output. Repo CI only.
  • T1 — client-only, no network-facing behavior change. CI + prod compat smoke.
  • T2 — node/client logic with behavioral surface, no protocol/format/economics change. Dev testnet + ADR.
  • T3 — protocol / storage format / payments / routing. T2 evidence + adversarial testing.

Compatibility

  • Wire: none
  • Storage: none (same daemon.pid / daemon.port files; stale ones are now removed by start)
  • API: none. No public ant-core items added or changed; DaemonInfo.running is now false for a stale PID file, which is what it documents.

Semver impact

  • breaking
  • feature
  • fix

Test evidence

  • Unit tests (cargo test -p ant-core --lib node::daemon::client): 12/12 pass on Windows and Linux. The new tests use the test process's own PID as the "reused" PID. The two tests for the reported case (info_with_a_reused_pid_is_not_running, start_clears_files_left_by_a_dead_daemon) fail with the old PID-only check restored, and pass with the fix.

  • Lint: cargo clippy --all-targets --all-features -- -D warnings clean on Linux; cargo fmt --check clean.

  • Reporter's scenario on Linux, real binaries, isolated XDG_DATA_HOME: start a daemon, kill -9 it, write a live unrelated PID into daemon.pid.

    origin/main e47ea7a this branch
    info on a genuine daemon running: true running: true
    info after kill -9 + reused PID running: true running: false
    status running: false running: false
    start already_running: true, pid = reused PID new daemon, new port
    info / status afterwards still disagree both running, same PID
  • macOS is not run locally; the proc_pidpath path is compiled and tested by CI's macos-latest jobs.

  • The e2e skill gains step 5.5, "Stale PID file recovery", which covers the same scenario.

  • Prod compat smoke (2026-10-23), re-run at the rebased head da95adb5 against main ccd4106d. Run on Linux (dev2, stable 1.99.1) with --locked release builds and isolated XDG_DATA_HOME/XDG_CONFIG_HOME.

    • The scenario in the table reproduces on main and is fixed here:
      • On main, after kill -9 and a reused PID, info reports the decoy sleep PID as the daemon. start prints already running (PID <decoy>) and status says not running.
      • On this branch, info says not running and start launches a new daemon. info and status then agree on its PID, and stop exits cleanly.
    • stop on main already catches the reuse: PID … is alive but does not appear to be the ant daemon (possible PID reuse). Stale PID file removed. This PR gives info and start the same check.
    • Mainnet: ant file download of a public canary file (3cba93c8…, 5,277,908 B) with this build succeeded in 14.6 s, and the byte count matches.

New dependency

none (libc is already a dependency; proc_pidpath is from it)

ADR

n/a

Mitigation / rollback

Revert the commit. If a renamed ant binary fails the identity check, the impact is limited to info/start seeing its daemon as stale; stop has always refused such a daemon.

🤖 Generated with Claude Code

Nic-dorman and others added 2 commits October 23, 2026 19:52
… (V2-1655)

A daemon that dies without cleaning up (a reboot, an OOM kill, SIGKILL)
leaves daemon.pid and daemon.port behind. Once the PID is reused by an
unrelated process, `daemon info` and `daemon start` saw a live PID and
treated it as the daemon: `info` reported running while `status` (which
asks the daemon over HTTP) said not running, and `start` refused to start
a new daemon, so the hint `status` prints led nowhere.

`info` and `start` now classify the PID file through one helper that also
requires the process to be identifiable as the daemon, the check `stop`
already applied before signalling it. `start` removes stale files before
spawning; otherwise its port poll would read the dead daemon's port
straight back. macOS now checks the executable name via proc_pidpath
instead of trusting any live PID.

The `info` help text and README no longer claim its output is always
JSON: it has been human-readable unless --json is passed since 920f872,
which is the convention for every command.

Adds unit tests for the reused-PID case and an e2e skill step (5.5).

Closes V2-1655

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
rustdoc's private_intra_doc_links lint fails the Documentation job under
-D warnings. Describe the check inline instead of linking to
`recorded_daemon`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Nic-dorman
Nic-dorman force-pushed the nic/v2-1655-ant-node-daemon-infostart-trust-a-stale-pid-file-report branch from c714134 to da95adb Compare October 23, 2026 18:52

@MaidSafe-Hermes MaidSafe-Hermes left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

REQUEST_CHANGES — 2/2 seats reviewed (sol-1 gpt-6.1-sol xhigh; glm-1 z-ai/glm-5.3). The seats disagree; I confirmed the platform-identity gap below against the frozen head. CI checks were all passing at review time.

  • P2 — ant-core/src/node/daemon/client.rs:496-516 (also Windows :519-545): the macOS and Windows validators accept any live process whose executable basename is ant/ant.exe; unlike the Linux validator, they do not verify daemon-specific arguments. A stale PID reused by another ant CLI process therefore classifies as Live, so info can again report running: true and start can refuse recovery. Please verify that the process is running the daemon command (or use another daemon-specific identity signal) on these platforms, and add a regression test using a same-executable, non-daemon process. The current test only uses the test harness executable, so it does not cover this case.

Coverage: 2/2 seats reviewed. sol-1 returned CHANGES_REQUIRED; glm-1 returned APPROVE but noted the executable-name limitation. The process-identity issue is reproducible from the platform checks in the diff; it blocks approval.

…d Windows too

Review of #235: the macOS and Windows checks accepted any live process whose
executable is named `ant`. A stale PID reused by another `ant` command, such as
a long `ant file upload`, was still reported as the running daemon, and `start`
still refused to start.

Each platform now requires the process to be `ant` running `daemon run`, the
arguments `daemon start` passes to the detached child. macOS and Windows read
the arguments through `sysinfo`, which ant-core already uses for node
adoption. The executable-name checks stay as they were: they work for another
user's process and for an elevated one. Where the arguments can't be read,
`sysinfo` reports none, and the name check alone decides. Linux keeps its
/proc reading but uses the same rule, which is stricter than the previous
"any `daemon` argument".

New tests copy the test binary as `ant` and run it as `node daemon run`
(accepted), as `file upload` (rejected), and as `node daemon status` behind a
reused PID (`info` reports it not running).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants