Files
OpenFUT/openfut-blaze-host/gate-evidence.sh
T
funman300 48aa955212 blaze-host: per-gate evidence capture, separating asserted from observed
For the live FIFA gates. Records switch rules, sidecar status, log, trace and
the Python contract result into a timestamped bundle, and reports CONFIGURED
and OBSERVED state as two distinct sections.

The separation is the whole point. 'blaze-switch.sh status = ON' is an
assertion produced by the same tooling that performs the switch, and that
tooling reported a successful rollback once when none had happened. The
observed half comes from an unrelated source: the sidecar's own record of
which peers connected to it. A non-loopback peer in that log proves the
client's Blaze traffic landed on Rust without depending on reading an iptables
rule correctly.

Verified both ways: loopback-only traffic reports 'a FIFA session did NOT land
here'; a non-loopback peer reports that it observably did.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-11 02:00:31 +00:00

128 lines
4.8 KiB
Bash
Executable File

#!/usr/bin/env bash
# Capture evidence for one live-FIFA gate.
#
# gate-evidence.sh <gate-label> [outdir]
#
# Records what the system was configured to do AND what it observably did, and
# reports them separately.
#
# WHY BOTH
#
# `blaze-switch.sh status = ON` is an assertion produced by the same tooling
# that performs the switch. If that tooling is wrong — and it has been once
# already, reporting a rollback that had not happened — the assertion is
# worthless. The observed half comes from a different source entirely: the
# sidecar's own record of which peers connected to it. A remote peer appearing
# in the sidecar log is proof the client reached Rust that does not depend on
# reading an iptables rule correctly.
#
# Run it after each gate. It never modifies anything.
set -uo pipefail
HERE="$(cd "$(dirname "$(readlink -f "$0")")" && pwd)"
ROOT="$(cd "$HERE/.." && pwd)"
RUNDIR="${OPENFUT_SIDECAR_RUNDIR:-${TMPDIR:-/tmp}/openfut-sidecar}"
LOGFILE="${OPENFUT_SIDECAR_LOG:-$RUNDIR/sidecar.log}"
TRACE="${OPENFUT_BLAZE_TRACE:-}"
LABEL="${1:-}"
OUT="${2:-$ROOT/gate-evidence}"
[[ -n "$LABEL" ]] || { echo "usage: gate-evidence.sh <gate-label> [outdir]" >&2; exit 2; }
STAMP="$(date -u +%Y%m%dT%H%M%SZ)"
DEST="$OUT/${STAMP}-${LABEL}"
mkdir -p "$DEST"
say() { echo "$@"; }
both() { echo "$@" | tee -a "$DEST/summary.txt" >/dev/null; echo "$@"; }
both "=== gate evidence: $LABEL ($STAMP) ==="
both ""
# ---------------------------------------------------- configured (asserted)
both "--- CONFIGURED (asserted by tooling) ---"
{
"$HERE/blaze-switch.sh" status 2>&1
echo
"$HERE/sidecar.sh" status 2>&1
} > "$DEST/configured.txt"
sed 's/^/ /' "$DEST/configured.txt" | tee -a "$DEST/summary.txt"
# Raw rules, straight from the kernel, not via our parser.
{ sudo iptables -t nat -S 2>/dev/null || true; } > "$DEST/iptables-nat.txt"
both ""
# ------------------------------------------------------ observed (measured)
both "--- OBSERVED (measured from the sidecar's own record) ---"
if [[ ! -f "$LOGFILE" ]]; then
both " no sidecar log at $LOGFILE — nothing observed"
else
cp "$LOGFILE" "$DEST/sidecar.log" 2>/dev/null
banner="$(grep -m1 'openfut-blaze-host v' "$LOGFILE" 2>/dev/null || true)"
both " build: ${banner:-<none>}"
if grep -q 'tree=DIRTY' <<<"$banner"; then
both " !! DIRTY BUILD — this run is NOT parity evidence"
fi
conns="$(grep -c 'CONNECT from' "$LOGFILE" 2>/dev/null || echo 0)"
both " connections accepted: $conns"
# THE INDEPENDENT ASSERTION: which peers actually reached this process.
peers="$(grep -o 'CONNECT from [0-9.]*' "$LOGFILE" 2>/dev/null | awk '{print $3}' | sort -u || true)"
remote="$(grep -v '^127\.' <<<"$peers" | grep -v '^$' || true)"
both " peers: $(tr '\n' ' ' <<<"$peers")"
if [[ -n "$remote" ]]; then
both " REMOTE peer(s) reached the Rust sidecar: $(tr '\n' ' ' <<<"$remote")"
both " => the client's Blaze traffic observably landed on Rust"
else
both " no remote peer connected — only loopback (or nothing) reached Rust"
both " => a FIFA session did NOT land here"
fi
# Session shape, straight from the log.
logins="$(grep -c 'Authentication::login .*REPLY' "$LOGFILE" 2>/dev/null || echo 0)"
notifs="$(grep -c 'UserSessions::<' "$LOGFILE" 2>/dev/null || echo 0)"
both " login replies: $logins UserSessions notifications: $notifs"
both " close reasons:"
grep -o 'CLOSE after [0-9]* frame(s): .*' "$LOGFILE" 2>/dev/null \
| sort | uniq -c | sed 's/^/ /' | tee -a "$DEST/summary.txt" || true
# Anything that looks wrong.
probs="$(grep -E 'DECODE FAILED|REJECT|FAILED|absurd' "$LOGFILE" 2>/dev/null | head -20 || true)"
if [[ -n "$probs" ]]; then
both " ANOMALIES:"
sed 's/^/ /' <<<"$probs" | tee -a "$DEST/summary.txt"
else
both " no anomalies in the log"
fi
fi
# ------------------------------------------------------------------ trace
both ""
both "--- TRACE ---"
if [[ -n "$TRACE" && -f "$TRACE" ]]; then
cp "$TRACE" "$DEST/rust-blaze.trace"
frames="$(grep -c '^conn-' "$TRACE" 2>/dev/null || echo 0)"
both " $TRACE -> $DEST/rust-blaze.trace ($frames traced frames)"
both " routes seen:"
grep -o '^conn-[0-9]* \(RX\|TX\) [^ ]* [A-Za-z]*::[^ ]*' "$TRACE" 2>/dev/null \
| awk '{print $2, $4}' | sort | uniq -c | sort -rn | head -20 \
| sed 's/^/ /' | tee -a "$DEST/summary.txt" || true
else
both " no trace configured (set OPENFUT_BLAZE_TRACE before starting the sidecar)"
fi
# ----------------------------------------------------------- python health
both ""
both "--- PYTHON BACKEND (must stay healthy throughout) ---"
if contract="$(cd "$ROOT/fifa17-recon" && timeout 120 python3 tools/test_fut_contract.py 2>&1 | tail -1)"; then
both " contract suite: $contract"
else
both " contract suite: FAILED TO RUN"
fi
both ""
both "evidence bundle: $DEST"