5b8bee286c
During the Rust production cutover four stale openfut-switch nft rules were
still redirecting production-facing traffic to staging (42127->42227,
8081->8281, 8094->18094, 8099->18106). They matched `ip daddr 10.10.0.120`, so
every server-side probe via 127.0.0.1 or the container IP passed while the
CLIENT was refused. That cost a full false-negative acceptance round: a retail
quick-sell landed on staging while production sat untouched, and the launcher
reported the server "not answering".
The failure mode is mechanical, so the check is:
* openfut-switch.sh status
* nft rules on OpenFUT ports, split into REDIRECT (interception) and DNAT
(docker publishing, expected -- reporting those as problems would train the
reader to ignore the tool)
* the actual point: loopback vs the ADVERTISED address per port. A redirect
keyed on the LAN IP is invisible to loopback, which is exactly why the
cutover probes all passed.
Verdict is CLEAN / INTERCEPTION_PRESENT with exit 0/1/2. Both branches
observed: it reports CLEAN now, and reported INTERCEPTION_PRESENT on a
loopback/advertised disagreement before :4216 was excluded.
:4216 is excluded from the verdict because LSX runs on the game machine --
compose publishes the port but OPENFUT_SERVERS omits lsx, so "published but not
served" is its normal state. It is still printed, marked as expected.
READ-ONLY by design: it never deletes a rule. Clearing interception stays a
deliberate operator act via `openfut-switch.sh off --name <id>`.
Run before production acceptance, client repoints, migrations and retail
protocol tests.