Commit Graph

2 Commits

Author SHA1 Message Date
funman300 d7c0a5521d switch: refuse to arm at a dead target; watchdog: use a pidfile
Three times now the same sequence has broken the client path: a build guard
correctly refuses to start the Rust replacement, and the `switch on` that
follows in the same script arms anyway, because it never checked whether
anything was listening. The redirect then lands on a closed socket and the
working Python service is bypassed for no benefit.

`on` now refuses unless the target port is listening. ALLOW_DEAD_TARGET=1
overrides it for arming ahead of a service that is about to start, but that has
to be deliberate. Verified both ways: rc=2 and nothing installed against a dead
port, rc=0 and two rules with the override.

The watchdog now writes a pidfile. Stopping it by command-line match is unsafe
-- any shell whose arguments merely mention the script name matches too, which
has now killed the wrong process twice here (once via `pkill -f`, once via a
/proc/*/cmdline substring loop).
2026-08-11 05:13:40 +00:00
funman300 2ae90b1ea9 tooling: watchdog that rolls an armed switch back when the service stops answering
`openfut-switch.sh on` prints "the service MUST stay up" -- true, and useless
when nobody is at the terminal. An armed switch pointing at a dead port means
the client hits a closed socket with no fallback.

This turns the documented rollback into an automatic one, failing toward the
Python oracle. The worst case of a spurious trip is a gate needing re-arming;
it can never leave the client broken.

It only ever REMOVES a switch. It does not install one, restart the Rust
service, or touch Python, and it does not re-arm after tripping -- an
unexplained rollback should be a finding to read, not something hidden by
flapping the switch back on.

The probe goes through the switch and speaks TLS, because a bare TCP connect
would succeed against a process wedged mid-handshake.

Tested both directions, not just the happy path: quiet for 45s against a
healthy service, and against a stopped one it failed 3/3 in 9s, rolled back,
and left Python serving -- verified by re-reading all four tables and by which
implementation's log grew.
2026-08-11 05:11:41 +00:00