3.9 KiB
REPORT — most recent implementation
v0.140.0 — Direction-2 immediate-sync: hub→box wait channel client (2026-07-16)
The reverse half of the immediacy arc (Direction 1 = v0.139.0 box→hub trigger). An operator action
on the hub now reaches the box in seconds instead of on the next ~15-min cycle. Pairs with hub
v0.58.0 (GET /api/v1/wait + the in-memory operator-intent notifier). Grounding:
felhom.eu/documentation/audits/SPIKE-immediate-sync-transport-2026-07-16.md (option b).
What shipped
internal/report/waiter.go(new)report.Waiter. Holds a hanging authenticatedGET {hub}/api/v1/wait?gen=N(same hub URL + key as the pusher — no new config keys). Its ownhttp.Clienthas no overall Timeout (a held GET must stay open for the hub's ~240 s hold), with connect/TLS/ResponseHeaderTimeoutdeadlines; a per-request context bounds a black-holed connection. On a completion whose generation differs from the last seen, it fires the v0.139.0report.Trigger— and nothing else; the report ACK delivers config/escrow/claim/floor through the UNCHANGED machinery (zero new delivery logic; the box pulls even the wake-up).- First observation records, never fires (no restart-echo report).
- Same-generation timeout fires nothing (not interval-shortening).
- Heartbeat newlines tolerated; only the
{"gen":N}line is read (contentless wake). - Any error — transport, 404 from a pre-v0.58.0 hub, malformed body — backs off 5 s→5 min (reset on success), ONE WARN per state change, and the 15-min cycle keeps reconciling. Exits promptly on ctx cancel (even mid-hold).
cmd/controller/main.go. Waiter constructed + started beside the Direction-1 trigger, gated on the SAMEhubPusher != nil && cfg.Hub.Enabled(strict no-op when hub reporting is off).- Copy soften.
backups_remote.html+backups_escrow.html: "ez általában néhány másodperc, legfeljebb 15 perc" (the 15-min bound stays as the honest worst case; escrow grace window unchanged).
Tests + red-proof
Non-hollow waiter_test.go (httptest fake hub): fires-once-on-change, same-gen-timeout-no-fire,
first-observation-records-no-fire (RED-PROOF: disable the baseline branch → first poll fires;
run-fail-reverted), heartbeats-tolerated, malformed-completion-no-fire-no-panic, 404-as-error,
Bearer+gen wire check, ctx-cancel-mid-hold-returns-promptly. Full controller suite green
(go build/vet/test ./...); design gates pass (template_id / emoji / native_confirm / offbox_rename
/ mojibake).
Deploy + live validation (guest 9201, customer demo-felhom)
Image felhom-controller:0.140.0 built + pushed on 180; deployed via the bootstrap flow; container
Up (healthy); log [report] hub wait channel active (hold ≤240s); startup report sent.
- Completion-on-difference path: box-side curl with a mismatched
?gen=999999→{"gen":0}in 0.047 s through the real ingress. - No-annotation hold proof: box-side curl at
?gen=0held cleanly for 240.04 s (HTTP 200 →{"gen":0}, 10 heartbeat newlines); the controller's own Waiter logged no wait-channel errors over the window (holds + reconnects cleanly past nginx's 60 s default). No ingress annotation applied. - Scenario C (hub
rollout restartmid-hold): the Waiter logged exactly one WARN (unexpected EOF — backing off), reconnected cleanly, and fired 0 out-of-cycle reports (gen 0→0, no storm). The "recovered" INFO is deferred to the next hold completion (pollOnceblocks) — cosmetic. - Save→applied via the operator UI needs an operator login (CC lacks the password by standing constraint); the mechanism is unit-proven and the delivery chain is the already-live v0.139.0 / config-refresh path.
Coupling
Immediacy needs hub ≥ v0.58.0; against an older hub the wait 404s and the box degrades to the 15-min
cycle. No agent coupling, no MinAgent. The agent-plane ep0-poke stays parked in the OOB arc.