fix(fleet): dial the registry address so a stale ssh_config can't hide a live box#1315
Conversation
…ssh_config can't hide a live box The fleet probes (version/doctor/fleet-ping) called sshExecAsync(target.name), letting ssh resolve the bare name through ~/.ssh/config — where a hand-written 'Host <name>' block with a DHCP-drifted LAN IP silently shadows the correct entry, times out, and (buffered behind the slowest probe) hangs fleet status ~60s while a reachable box renders as 'probe failed'. The stats probe already dials the registry address (sshTargetFor), which is why it reached the same nodes the doctor probe couldn't. Fix: new fleetDialTarget(device) prefers the registry Tailscale dnsName/IP (falls back to the bare name for address-less manual devices), and all three fleet probes dial it. Also fail fast — skip the 15s+30s version+doctor for a device the stats probe already found unreachable. Verified live: yosemite-m1..m6 went from 'probe failed / ssh: connect to host 192.168.1.101' to full healthy doctor rows with NO change to ~/.ssh/config. 4 unit tests for fleetDialTarget. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Code ReviewerVerdict: Ready to merge Build: ✅ Read Changes that work well
Things to verify manually
Reviewed by Code Reviewer — actually ran the build and tests on this branch. |
…on fresh stats - Move fleetDialTarget into lib/devices/connect.ts (next to sshTargetFor) so it's shared, not command-local; tests move to connect.test.ts. - Extend the fix to agents check --devices (check.ts probeDeviceCheck), which had the identical bare-name bug prix flagged — now dials the registry address too. Verified live: 'check --devices' reaches all 12 (drift 12 of 12) where m1-m6 previously errored on the stale ~/.ssh/config IP. - Gate the fail-fast skip on forceRefresh (--refresh/--live): loadFleetStats is cache-first, so on a default run a box that came back online since the last daemon warm would be skipped to 'unreachable' without a live try — a false negative prix flagged. Now default runs always do the (now-fast) live probe. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Thanks — both notes addressed in 1f56675 (even though the verdict was ready-to-merge, both were worth doing):
Build clean, 16 tests pass (3 new |
|
Merging on green CI (8/8) + non-author review. prix-cloud verdict was 'Ready to merge' on the core; its two non-blocking notes were then addressed in 1f56675 (gate fail-fast on --refresh; extend the fix to |
Code ReviewerVerdict: Ready to merge Build: Read Changes that work well
Things to verify manually
Reviewed by Code Reviewer — actually ran the build and tests on this branch. |
Why
agents fleet statushangs ~60s and shows reachable devices asprobe failed / ssh: connect to host <old-ip>when the user's~/.ssh/confighas a hand-writtenHost <name>block with a DHCP-drifted LAN IP. Root cause: the fleet probes dial the bare device name, lettingsshresolve it through~/.ssh/config— where the stale block shadows the correct entry. The registry already stores the drift-proof Tailscale address (dnsName/IP), and the stats probe already uses it (viasshTargetFor) — which is why stats reached the exact nodes the doctor probe couldn't.This affects any user with Tailscale + a stale ssh_config entry, not one fleet.
What
fleetDialTarget(device)(pure, tested) — prefer the registry TailscalednsName/IP; fall back to the bare name for address-less manual devices (never worse than before).--version,doctor --json,fleet ping) now dialtarget.dialTargetinstead oftarget.name— a stale~/.ssh/configalias can no longer shadow the known-good address.reachable: falseis skipped straight to an unreachable row instead of eating a 15s+30s version+doctor timeout — one dead box can't stall the matrix.Verification
~/.ssh/configchange:yosemite-m1..m6(all had drifted LAN IPs) went fromfleetDialTarget(dnsName preference, IP fallback, no-user, address-less fallback).tsc --noEmitclean; existing suite unaffected.Follow-up (not in this PR)
agents devices synccould re-resolve + warn when a stored address goes stale (drift detection), andfleet statuscould stream rows as they arrive rather than buffering behind the slowest probe. Both are additive on top of this.