hub v0.72.0 — R-70 + R-71c: offsite delivery-state detector, card, stuck event, R-39(a)-guarded self-heal restage

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NKSN3gSg4TKVBBqkwW2djR
This commit is contained in:
2026-07-23 12:59:04 +02:00
parent c801cee647
commit 1133aade73
13 changed files with 1179 additions and 3 deletions
+42
View File
@@ -1,5 +1,47 @@
# Felhom Hub — Changelog
## v0.72.0 — R-70 + R-71(c): the offsite last mile becomes visible, and burned credentials heal themselves (2026-07-23)
Origin: `documentation/audits/DIAG-f10-demo-hp-offsite-2026-07-23.md` — demo-hp sat 2 days with the
customer card claiming "Provisioned … delivered to the controller once" (static copy) while the box
had NOTHING: the day-0 managed update killed the apply-bridge after password-consume, and the hub —
holding both signals (`one_time_secrets.consumed_at`, 153 offbox-less reports) — read neither.
**One detector, four consumers:**
- **Detector** (`internal/offsite/delivery.go`, `DeliveryStateFor`): per-customer
`OffsiteDeliveryState` from the secret row × report offsite-presence — `applied` (precedence:
the box's own report wins) / `consumed_awaiting_apply` (the burned-credential shape) /
`staged_awaiting_consume` / `no_secret`. The applied+stale-staged edge (demo-felhom's live
shape: key-auth-first never consumes) stays `applied` PLUS a visible stale-staged flag. New
store reads: `GetOneTimeSecretInfo` (timestamps only, value never selected),
`LatestReportOffsitePresence`, `CountReportsOffsiteSince`, `LastEventAt`, + a test-only
timestamp back-dater (the PBSDR seam pattern).
- **Customer card** (`config_form_body.html` + `deliveryViewFor`): the static "delivered once"
claim is GONE; the card renders the derived state with its age (badge idiom `n-ok/n-warn/
n-neutral`; consumed goes amber past 30 min; the stale-staged info line names the specimen's
age). Render test per branch (the v0.70.1 template-gate rule).
- **Loud event**: `offsite_delivery_stuck` (WARNING → operator email; severity gate untouched —
explicit tests pin that info would be silent) when consumed_awaiting_apply persists ≥ 1 h;
per-customer 24 h cooldown, DURABLE via `LastEventAt` over the events table (a hub restart
neither floods nor resets).
- **Self-heal (R-71c)**: `OffsiteDeliveryChecker` (shared 60 s ticker) invokes the EXISTING
Re-issue path (`web.Server.ReissueOffsiteForCustomer` behind the narrow `monitor.OffsiteReissuer`
interface — the pbsdrheal precedent; armed only when the provisioner is configured) when the
shape is unambiguous: consumed ≥ 1 h + ≥ 4 consecutive offbox-less reports since consume + zero
offbox evidence. One restage per customer per 24 h (durable); every firing surfaces as
`offsite_credential_restaged` (WARNING → operator) — repeats are repeating events, never a
silent retry loop. **The R-39(a) guard**: `SaveOneTimeSecret` clobbers by design (Re-issue
depends on supersede), so the heal RE-READS the secret row immediately before acting and refuses
unless it is still a consumed row — the TOCTOU (operator Re-issue landing mid-tick) is the shape
the guard kills. Red-proofs run and recorded (guard removed → the operator's fresh secret gets
clobbered, test fails on `reissue calls = 1`; both rate limits removed → duplicate fire).
Companion: felhom-controller v0.161.0 (the box-side truthful empty state). The self-heal ships
unit-proven + red-proofed, NOT live-fired — no broken box exists and none was broken for it; it
arms on the next natural occurrence (or a staged drill). R-71(a) (day-0 ordering) stays open —
separate spec.
## v0.71.0 — paired recovery mails, prefs seeding at claim, priority headers, operator test leg (2026-07-22)
Origin: `documentation/audits/AUDIT-power-outage-recovery-2026-07-22.md` F11 (recovery is silent),