This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user