From b2462b8f365d3f482090bc0a50595311769889ef Mon Sep 17 00:00:00 2001 From: kisfenyo Date: Wed, 5 Aug 2026 10:49:23 +0200 Subject: [PATCH] CHANGELOG: hub v0.96.0 (R-204 item 4, hub half) --- hub/CHANGELOG.md | 69 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/hub/CHANGELOG.md b/hub/CHANGELOG.md index 2ef4b74..ec2ba76 100644 --- a/hub/CHANGELOG.md +++ b/hub/CHANGELOG.md @@ -1,3 +1,72 @@ +## v0.96.0 — the hub answers a rebuilt box's request (2026-08-05, R-204 item 4 / R-193 / R-192) + +The hub half of the last manual intervention the 2026-08-04 drill needed. The box declares +(controller v0.199.0); this answers. + +### `internal/offsiteheal` — the sibling of `pbsdrheal` + +Same shape, same restraint: **declared states only**, a debounce, **restage → escalate**, a distinct +audit event per remediation, and a healthy box is a pure no-op. It never blind-timer-reissues and +never re-runs a provisioning step. + +**The debounce is TWO distinct reports, derived rather than chosen.** The controller reports every +~15 minutes, so two distinct declarations mean the state survived a full report cycle — a restart, a +slow first report or a transient config read all resolve well inside one. One report would act on a +blip; three would leave a stranded customer waiting ~45 minutes for the one thing they cannot obtain +any other way. The reconciler's own tick (5 min) is deliberately faster than the report cadence so it +adds no latency of its own; the debounce counts **fresh evidence, not ticks**. + +**§8.4 — is there a deliberately-unhealed state, as `pbsdrheal` has in `verify_failed`?** Yes, and it +is excluded **by construction** rather than by a case in the switch — worth stating plainly, because +"there is nothing like that here" is usually wrong. The analogue is the **regressed** shape: a box +that had a working tier and lost its target while still holding its repository password. Re-arming a +credential would not help it. It cannot reach this reconciler at all, because the controller's +declaration predicate requires the repository password to be **absent**. + +### Restage before mint — and the fact it rests on + +`store.RestageOneTimeSecret` clears the consumed flag without touching the value, mirroring +`RestageHostPBSSecret`. **That the value survives a consume was established from this schema and from +`ConsumeOneTimeSecret` — which stamps `consumed_at` and nothing else — not inherited from the PBS +analogy.** The two secrets are different objects with different lifecycles, and assuming a shared +shape is how two earlier sessions confused the credentials. `TestRestageOneTimeSecret_ReArmsTheSameValue` +asserts the **same** value comes back, so a future "hardening" that cleared the column would fail +loudly instead of silently turning every rebuild into an external mint. + +### `reportHasOffsite` tightened — the ninth comment-vs-code entry, caught before it shipped + +Its comment asserted *"the report builder attaches `offsite` only when the box actually has an offbox +target configured, so presence == applied-on-the-box"*. Controller v0.199.0 breaks that premise +deliberately. Left as bare presence, this would have read a **request for help** as proof the tier was +**applied** — making `DeliveryStateFor` return `DeliveryApplied` for exactly the stranded boxes, and +suppressing their stuck event. It now requires `enabled:true`, which is **provably a no-op for every +report shape that existed before** (an attached object has always carried `enabled:true`), and the +equivalence is measured in `TestReportHasOffsite_EnabledOnly` rather than argued. + +### R-192's guard half — CLOSED BY REPLACEMENT, not repaired + +The delivery checker decided the shape by counting how many of the **oldest 500** reports after a +consume carried an offbox target. On demo-hp all 500 predated the rebuild, so it confidently reported +the *regressed* shape and declined to heal — for 108 reports, while the box sat stranded. A +declaration needs no window, no count and no inference, so it **outranks both inferred shapes** +(`shapeDeclared`), the stuck e-mail says what was declared instead of prescribing Re-issue, and +`maybeHeal` **stands down with a record** because `offsiteheal` owns the remediation. Two mechanisms +healing one customer would double-issue — and this one can only mint, so it would also skip the cheap +path. + +### What is deliberately NOT automated + +**No escrow ceremony is ever run or requested.** A credential is replaceable; the recovery code is +not, because only the customer holds it. **Credential automatic, key customer-present.** + +**On security, weighed rather than skipped:** a re-issued credential is transport only. Anyone able to +forge the declaration already controls the box, and the box already holds its own installed access +key. The data stays sealed under the recovery code throughout. + +### Rider — the pre-push hook refuses a clone outside the workspace + +One assertion, identical in all four repos. See the controller's v0.199.0 entry. + ## v0.95.0 — a re-issue no longer marks a healthy escrow stale (2026-08-05, R-196 / R-204 item 2) `ReissueCredentials` called `MarkEscrowStale` on every re-issue that found an escrow row. The mark was