diff --git a/CHANGELOG.md b/CHANGELOG.md index 10a94eb..3809f42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,75 @@ +## v0.200.0 — the recovery screen: unlocking, and only unlocking (2026-08-05, R-193) + +R-193's remaining half. The drill proved a customer's file comes back after a machine is destroyed; +the sessions since removed every step that needed the operator. What was left is that the customer had +no way to *begin* without a command line. + +### It unlocks, and only unlocks (operator ruling, 2026-08-05) + +The page explains the situation, takes the recovery code, opens the repository, and shows what is in +there — which apps, from when, how big. **It restores nothing.** Restore is already per-app and already +lives in the backups area; putting files back is a separate item and is not started here. A screen +that unlocks and then offers to overwrite is two decisions wearing one button. + +### One core, two callers + +`RecoverInstallCore` is split out of `RecoverAndInstall`. The CLI wrapper keeps its exit codes and +printed lines **byte-identical** — every pre-existing CLI test passes unchanged — and the web handler +drives the same function. Two implementations of the one operation that can permanently lose a +customer's data would drift, and only one of them would ever be tested. **Both directions are asserted +from source by AST** (comments dropped, so a commented-out call cannot satisfy them), plus a third test +that the routes and the landing-page interception exist at all. + +### When it appears — two facts, and a third shape the task did not name + +`backup.OffsiteRecoveryOffer` requires the hub to hold a sealed package **and** this box to be unable +to open what it protects. The second half has **two** shapes: + +- **(a) no repository password at all** — the pristine rebuilt box. This is the literal reading of + "the data area is fresh". +- **(b) a password exists but the inherited history will not open under it** (`RepoState == orphaned`). + +**Shape (b) had to be added, and the reason is load-bearing.** `WriteOffboxSecrets` AUTO-GENERATES a +repository password when none is present — that is precisely R-193's orphaning mechanism — and since +hub v0.96.0's credential self-heal the re-apply now happens by itself within ~15–30 minutes. Shape (a) +alone would have made this screen appear only inside a half-hour window that closes on its own, so the +customer who logs in the next morning — the actual customer — would never have seen it. + +Shape (b) is also the state the shipped move-aside requires, which is what lets "I do not want the old +data" reach the existing handler rather than needing a new one. + +**Claimed and behind the household password.** Added after a test caught the omission: a legacy-open +box (no password anywhere) reaches `ServeHTTP` through `RequireAuth`'s pass-through, so without an +explicit `authEnabled()` check the interception fired for an unauthenticated visitor. + +### Three ways out, and none of them is a dismiss button + +- **Recover** — the main path. +- **„Most nem"** — the full page stops interrupting. **The backups-area entry point stays, + permanently**: it is bound to `recoveryOffer`, never to the postpone flag, because the data is still + there whether or not anyone clicked and a one-shot notice a flustered person clicks past is a notice + that never happened. +- **„Nem kérem vissza a korábbi adatokat"** — the exceptional path, not an equal third button. Two + confirmations, the second naming exactly what happens, then the **shipped** `/backup/offbox/reset`, + which sets the store aside and never deletes. Offered only when that handler can actually run. + +### The recovery code is handled no more loosely than on the command line + +POST body only (`PostFormValue` — never a query string), never logged at any level, never persisted, +never echoed into any message, cleared on every path, the page served `no-store`, and the field +`autocomplete="off"`. **No lockout** (§8.4): the code is a ten-word EFF phrase, guessing is not the +risk, and locking a customer out of their own data for a typo is a worse failure than anything a +lockout prevents. Failed attempts ARE logged locally — without the code — so a box being probed is +visible in the debug ring. + +### Two defects the tests caught before they shipped + +1. **An unclaimed box would have been shown the page** (above). +2. **The inventory nil-dereferenced when no off-site target was configured** — which is exactly the + pristine rebuilt shape. It now returns a named error and the page says the key is back and the + listing will appear once the box has re-connected, rather than showing a failure the customer + cannot act on. + ## v0.199.0 — a rebuilt box asks for its credential back (2026-08-05, R-204 item 4 / R-193) The last of the four manual interventions the 2026-08-04 drill needed. A rebuilt box has no off-site