Skip to content

fix(discovery): prune stale entries by pid liveness, not shared-port reachability#45

Merged
runyourempire merged 2 commits into
mainfrom
fix/discovery-pid-liveness
Jul 1, 2026
Merged

fix(discovery): prune stale entries by pid liveness, not shared-port reachability#45
runyourempire merged 2 commits into
mainfrom
fix/discovery-pid-liveness

Conversation

@runyourempire

Copy link
Copy Markdown
Collaborator

The bug

victauri check / invoke / coverage return 401 Unauthorized against a running, auth-enabled app on any machine that has run more than one Victauri app.

Root cause: victauri-test's discovery (find_live_servers) inferred a server was alive purely from TCP reachability of its advertised port. But every Victauri app registers on the same default port (7373). So when a previous app crashes/exits, its stale <pid>/ discovery dir still advertises 7373 — and a different live app now holding 7373 makes that port probe succeed. The dead entry is never pruned, and its stale token is added as a "live server". Two entries then match port 7373, so unique_token_for_port returns None (ambiguous) → no token sent → 401.

This is why the MCP-bridge path (victauri-cli::bridge, which already gates on the pid) worked while check/invoke/coverage didn't — the exact asymmetry.

The fix

Gate liveness on the owning pid, not just port reachability: a dead owner is stale even when the shared port answers, so the entry is pruned and token selection stays unambiguous.

  • find_live_servers → testable find_live_servers_in(base, is_alive, is_reachable) + cross-platform is_process_alive (tasklist / kill -0), mirroring the audited helper in victauri-cli::bridge.
  • diagnose_discovery gates Live/Stale on pid liveness too (accurate classification on a shared port).
  • Also resolves victauri doctor reporting "server not running" in this case (it was the same failed discover()).

Tests

New regression test: two discovery entries on the shared port (one dead, one live) → only the live token is selected and the dead dir is pruned — the exact 401 scenario, now green. Plus a mid-rebuild case (live owner, unreachable port → pruned).

cargo test -p victauri-test 115/0, cargo clippy clean (deny-level workspace lints), rustfmt clean, and the repo pre-push gate passed.

Notes / not in scope

  • Reproduced live while introspecting a sibling app: 5 stale %TEMP%\victauri\<pid> entries all on 7373 caused the 401; pruning dead-pid dirs fixed it. Discovered via the stale installed CLI too (0.5.6) — unrelated; upgrading is the separate fix.
  • Doesn't change the shared-default-port design itself (7373 for all apps) — it just stops stale entries on that port from poisoning token selection.

🤖 Generated with Claude Code

…reachability

victauri-test's discovery inferred a server was alive purely from TCP reachability
of its advertised port. But every Victauri app registers on the same default port
(7373), so a crashed app's stale <pid> entry still looked "reachable" — a DIFFERENT
live app now holds 7373 — and was never pruned. Its stale token then collided with
the live server's in `unique_token_for_port` => None => `victauri check/invoke/
coverage` return **401 Unauthorized** on any machine that has run more than one
Victauri app. (The MCP-bridge discovery path already gated on the pid and so worked;
this brings the test/CLI path in line.)

Fix: gate liveness on the owning PID — a dead owner is stale even when the shared
port answers, so the entry is pruned and token selection stays unambiguous. Also
resolves `victauri doctor` reporting "server not running" in this case.

- find_live_servers -> testable find_live_servers_in(base, is_alive, is_reachable)
  + cross-platform is_process_alive (tasklist / kill -0), mirroring victauri-cli.
- diagnose_discovery gates Live/Stale on pid liveness too.
- Regression test: two entries on the shared port (dead + live) -> only the live
  token selected, dead dir pruned. 115 lib tests pass, clippy clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W3W4WofCBkV9TgYAzjTdM7
@runyourempire

Copy link
Copy Markdown
Collaborator Author

Note on the two red checks — pre-existing, not from this PR.

This change touches a single file (crates/victauri-test/src/discovery.rs) with no Cargo.toml/Cargo.lock changes, so it can neither cause nor fix dependency-advisory checks:

  • Security Audit — already failing on main (92e854d): RUSTSEC-2026-0185 (quinn-proto) + unmaintained atk (RUSTSEC-2024-0413), transitive deps.
  • Dependency ChecksRUSTSEC-2026-0190 (unsoundness in anyhow::Error::downcast_mut), a 2026 advisory published after this branch's base ran, so it newly trips cargo-deny's live advisory DB. Fixed by bumping anyhow — a separate dependency-hygiene PR.

The checks that validate this change are Check & Lint (fmt/clippy/cargo test) and E2E Integration; the regression test (dead_pid_sharing_a_live_port_is_pruned_so_selection_stays_unambiguous) covers the exact 401 scenario.

@runyourempire runyourempire merged commit d150955 into main Jul 1, 2026
21 checks passed
@runyourempire runyourempire deleted the fix/discovery-pid-liveness branch July 1, 2026 14:43
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.

1 participant