diff --git a/CHANGELOG.md b/CHANGELOG.md index aa5402e..4c8fc79 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,53 @@ +## v0.198.0 — the four steps a customer would have hit alone: two of them closed (2026-08-05, R-204 items 1 & 3) + +The 2026-08-04 recovery drill (R-201) passed — and it only passed because a person was there. Four +manual interventions stood between "the key is recoverable" and "the file is back". None of them is in +any design document. Two of the three defects are in this repo. + +### Item 1 — a freshly minted reset code now works on the first attempt + +`--print-reset-code` runs as a **separate process** (`docker exec`): it loads settings itself, mints a +code, persists it and exits. The running server's cache was never told, so it kept validating against +the previous hash. **The code the customer was told to type was refused until the controller +restarted, and nothing said so.** During the drill that cost two failed attempts with an operator +present; a customer alone stops there. + +`effectiveClaimCode` now READS THROUGH to the persisted state (`settings.ReloadClaimCode`) before +applying the settings-vs-config precedence. **The precedence rule is unchanged and deliberate** — the +defect was the freshness of the settings value, not which source wins. + +- **Read-through, not a watcher, a signal handler or a TTL.** A TTL is worse than the bug being fixed: + it opens a window in which a SUPERSEDED code still works. That is the mutation + `TestClaimCode_SupersededByASecondMint_RefusedImmediately` exists to kill, and its red-proof is + exactly that TTL — demonstrated failing with "the SUPERSEDED code was accepted". +- **Fail closed.** An unreadable persisted state keeps the gate up, refuses the claim and logs why. An + ABSENT file is not an error (a box before its first save falls back to the controller.yaml bake). +- Cost: one small file read per request **only while the box carries no password** — `claimGateActive` + returns on `authEnabled()` before touching it, so a claimed box never reads. + +### Item 3 — a restore now says what it did NOT restore + +The default restore (`mode=unit`) recovers the recovery unit: the app's definition, its configuration +and its database dumps. It does **not** recover the customer's own files — `RestoreOffboxScratch` +passes `--include `, and the userdata that is in the same snapshot is excluded by it. The +old outcome was one sentence for both modes and named neither scope, so on the last step of a disaster +recovery the customer was told „visszaállítva" after the thing they were looking for had not been. + +- `restoreScratchOutcomeMsg` (pure, unit-testable) now states, for a unit restore: what came back, that + the customer's own files did NOT, and the next step that gets them. The full case says the files came + with it — otherwise the absence of the warning would be the only difference, and an absence is not a + statement. +- The wizard's intent card 1 states its scope **before** the choice, not only in the outcome. +- **The full-restore size gate is untouched** — still compute, reveal, confirm, re-check at execution. + Pinned by `TestOffboxRestore_FullPathUnchanged`, which asserts no restore runs before the confirm. +- **The default stays `unit`.** All three wizard forms set `mode` explicitly, so the `mode==""` fallback + is reachable only by a hand-crafted POST: changing it would alter nothing the customer sees while + silently changing that POST's behaviour. The defect was silence, and silence is what was fixed. + `TestOffboxRestore_DefaultModeGetsTheScopedOutcome` pins the mode-less POST to the scoped wording. + +Item 2 of R-204 (a re-issue marking a healthy escrow stale) is the hub's half — felhom.eu v0.95.0. +Item 4 (a rebuilt box cannot obtain an off-site credential unaided) is R-193 and remains open. + ## v0.197.0 — the app and its backup look in the same place, and "ok" means it (2026-08-04, R-203) Found when the R-201 drill halted at its pre-wipe backup rather than wiping a machine: the run