CENSUS (read-only, hub store, tester's machine not contacted): no machine that is not ours can be in the state that cost demo-felhom its history. The hub holds escrow for three hosts; both demo boxes lost their pre-fix key in the same four hours on 2026-08-04; peti-felhom and david have no host row and no escrow at all. A control ran FIRST and had to pass -- the query returned "present (572 bytes)" for a host known to have material and "absent (NULL)" for one known not to. Corrected my own instrument on the way: a date-only comparison mislabelled both losses as after the fix, so the in-force moment is now pinned from the hub's first post-fix escrow row (11:11:37Z), which independently agrees with the register. PART 1 ESTABLISHED. The prune is recorded inside R-267 -- the row about the Configuration page being slow -- because pruning artifacts is what made that page fast. Arithmetic checks (23+7=30, plus three versions that only surfaced after the first thirty moved them onto page one = 33) and the PAGINATED listing shows both generics at exactly ten. R-291's blocking condition is released: the operator was being asked to establish something already written down. And my counter-argument yesterday was wrong in exactly the way R-267 warns about -- "containers hold 19" came from an unpaginated query; paginated they hold 270 and 169. RECEIPTS: three restored (drives.enrol, backup.tier1, fail.lost-recovery-code), each citing the document that walked it; the map already read PROVEN-LIVE for all three, so this follows the map rather than raising a status in the view. NINE HONEST GREYS. fault.selfheal's best hit argues against it -- an incident recording self-heal's absence through a 1h15m outage. THE DECAY RULE FIRED FOR THE FIRST TIME. backup.restore-proof has a receipt from 28 July and is superseded anyway: demo-hp's restore-test failed 5 August and the box has since been rebuilt. A claim about a continuing behaviour cannot rest on an old observation. The capability map still reads PROVEN-LIVE and is now the thing out of step -- recorded, not silently rewritten. PART 4 specified, not implemented. The orphan card promises restorability the box rendering it cannot evaluate: the discriminator is on the hub and no wire field carries it. A conditional promise the system cannot evaluate is the same defect as an unconditional false one, so the copy stops promising, says what happens, and names a route. Ships with the next controller change so one bake covers both.
5.6 KiB
SPEC — the orphan card must stop promising what it cannot know (2026-08-10)
Specification only. Nothing here is implemented, deliberately — see §5.
Why this exists. When a machine's off-site history is set aside, the card tells the customer — in
Hungarian, at the moment they have just lost that history — that the old copies may be restorable
later with their recovery code. For anything set aside before 2026-08-04 that is false and
unfixable, and on 2026-08-10 it was said to a real machine in exactly that state (demo-felhom).
Register: R-202.
1. The surfaces, at file:line
All in felhom-controller:
| # | file:line | what it says today |
|---|---|---|
| 1 | controller/internal/web/templates/backups_remote.html:98 |
"A távoli tárhelyen lévő mentések egy korábbi, már nem elérhető kulccsal készültek…" — the orphan explanation. Accurate; keep. |
| 2 | controller/internal/web/templates/backups_remote.html:101 |
"A régi előzmény félretéve marad (nem törlődik), és a hozzá tartozó helyreállítási kóddal később visszaállítható lehet." — the false promise. |
| 3 | controller/internal/web/templates/layout.html:143 |
the 14-day abandon countdown — "a korábbi távoli mentéseidet {{.RecoveryAbandonDays}} nap múlva véglegesen töröljük". Accurate; keep, but see §3. |
| 4 | controller/internal/settings/settings.go:337-338 |
schema comment on OrphanedRenamedTo: "so the card/log can name where the old history was set aside." Accurate; keep. |
The 00-capability-map.md restorability wording was searched for and not found — the map does not
make this claim, so there is nothing to correct there. (Stated because the task expected one.)
2. What is actually true, in three cases
| case | truth |
|---|---|
| material set aside before hub v0.93.0 (in force 2026-08-04 ~11:11Z) | Not recoverable, by construction. The restic password lives only in the identity bundle (escrow/identity.go:39, read by escrow/recover.go:91); for these rows identity_blob is NULL. No code and no recovery code can produce it. |
| material set aside after the fix | Recoverable in principle — the blob is retained and the customer's code unwraps it. Never yet demonstrated end to end on a superseded row. |
| today's population | Both cases that exist are the first one. Census 2026-08-10: two hosts hold a supersession, both identity_blob ABSENT, both superseded ~4 h before the fix. |
3. THE DECIDING FACT: neither the box nor the customer can tell which case they are in
- The hub can tell — it holds
host_escrow_superseded.identity_blob(store/store.go:393). - The box cannot. It caches only
HubEscrowIdentityPresent(controller/internal/settings/settings.go:71), which is about the current escrow, not a superseded one. No field on the report or ACK wire carries superseded-blob retention. - The box is what renders the card.
So the card is making a conditional promise its renderer cannot evaluate. That is the same defect as an unconditional false promise, in a longer sentence. The wording below therefore stops promising and starts stating.
4. The replacement copy
Surface 2 — backups_remote.html:101. Replace:
A régi előzmény félretéve marad (nem törlődik), és a hozzá tartozó helyreállítási kóddal később visszaállítható lehet.
with:
A régi előzmény félretéve marad a tárhelyen — nem töröljük. Új, üres tárolót hozunk létre, és a következő mentés oda készül.
A félretett mentések megnyithatóságát itt nem tudjuk megígérni. Ez attól függ, megvan-e még a hozzájuk tartozó kulcs, és ezt ez a gép nem tudja megállapítani. Ha szeretnéd, hogy utánanézzünk, írj nekünk — a félretett másolat addig is a helyén marad.
Three properties, each deliberate: it states what happens (set aside, not deleted, fresh store); it declines the claim it cannot evaluate, and says why rather than going vague; and it names a route the customer can take — writing in — instead of leaving them nowhere.
(Surface 3's countdown stays, but the two must be read together: the card must not simultaneously say "we cannot promise these are openable" and "we will delete them in N days" without the second making clear it is the set-aside copy being deleted. Recommend re-reading the pair once implemented.)
5. The test that should pin it
controller/internal/web/ render test, one per branch of the orphan card:
- Orphan card rendered → the string
visszaállítható lehetdoes not appear anywhere in the output. That is the regression guard: it fails if the promise returns in any form. - Orphan card rendered → the "write to us" route is present. A refusal that names no route is a defect in this project; pin the route, not only the absence.
- A render test per branch of the gate that shows the card at all, per the seam-wiring rule — a handler test that POSTs proves nothing about reachability.
6. Why it is not implemented tonight, and what it should ship with
The change lands in felhom-controller, and a controller release is not delivered until a golden
carries it — which costs one image bake and one operator approval (R-242 is the row about exactly
that gap). The next session changes the controller anyway. One bake and one approval should cover
this copy change together with that work, rather than spending two of each.
Do not let this decouple: if the next controller release ships without this, the promise is still being made to the next customer who loses a history.