Restore-truth run (2026-10-25): D47 + D48 recorded (09 §3 decisions 256, 257; CONTEXT); 07 §6.5 (R-1271 cause + the future-date rule), §6.6 (D48 order, the R-698 sentence), §8.2d (D47), §8.2e (R-1270); register 217 -> 219 (R-1278, R-1279 opened; R-1269/1270/1271 VERIFY; notes R-1206, R-1243 dated check done read-only, R-232 moved one day); STATUS D49 + Monday list; evidence (9202)
gates / gates (push) Successful in 13m44s
gates / gates (push) Successful in 13m44s
This commit is contained in:
1 parent
67053335d1
commit
9647f1d158
58 files changed
+1834
-14
No files matched your search
@@ -937,6 +937,26 @@ holds no database, or **does not record its data version** (§6.6 — a unit wri
|
||||
a mixed or mismatched unit is refused by §6.6's own rule. Then a dated folder's files move back and the one unit restore
|
||||
runs. The downloaded copy is removed on every path. A failed download or a refusal leaves the kept files as they were.
|
||||
|
||||
**[FACT] 2026-10-25 — why the „another drive" check let another box's copy through (R-1271; catalog sweep 2026-10-24,
|
||||
Tester4: Paperless-ngx, Nextcloud, Immich).** The check (`KeptCopyAt`, `internal/backup/kept_load.go`) compares the
|
||||
unit's `app.yaml` `HDD_PATH` with the drive the kept files sit on. That is a mount PATH, and every box mounts its first
|
||||
data drive under the same name (`/mnt/felhom-drives/hdd_1`) — so a copy taken of another box's disk matches. It is a
|
||||
path check, not a physical identity, and it stays one: loading an old box's data on a new box is what off-site is for.
|
||||
What chose the wrong copy was the DATE: the rehearsal box's clock had been set to 2026-10-28, its off-site snapshot
|
||||
carried that time, this box had no data time of its own to cap it (`offsiteDataTime`), and „newer than every local
|
||||
copy" took it over the family's own copy of the same evening. Immich's 20 minutes of 404 were the ROUTING, not a half
|
||||
load: the unit brought the old box's `SUBDOMAIN` (`photos`), so the app answered at `photos.<domain>` and the probe
|
||||
asked `immich.<domain>`; its containers were healthy.
|
||||
|
||||
**[DESIGN] 2026-10-25 — a copy dated in the future is never „the newest" (R-1271; brief 2026-10-25 Part 4, the safest
|
||||
reading; controller `bd9f4f7`, unreleased, rides v0.343.0).** A copy whose date lies more than **10 minutes** past this box's clock (`KeptClockSkew`: NTP keeps a box
|
||||
within a second; ten minutes covers a box that synced late after a power cut) is future-dated. It never counts as the
|
||||
newest: the newest copy with a true date wins (a tie goes local); the household's own local copy wins over any
|
||||
off-site copy that is not newer by a true date. A future-dated copy is offered only when no other copy exists — then
|
||||
the question says its date is in the future and does not recommend it (the „use" button is not the primary one). The
|
||||
question always names the copy it will use. An off-site copy is never refused only because another box of the
|
||||
household made it.
|
||||
|
||||
**It is NOT backed up.** No tier captures `<drive>/kept` or a leftover `appdata/<x>` of a removed app; the page says
|
||||
so („Erről nem készül mentés." / "This is not backed up."). What brings it back into an app is the unit it carries
|
||||
(or the app's own unit on the drive), listed per row as „Visszatölthető innen" / "Can be loaded from".
|
||||
@@ -1027,13 +1047,28 @@ requires a database dump written after the conversion whose recorded engine is t
|
||||
image is re-pulled on restore. A version its maker has deleted cannot start. Measured 2026-09-26: the 42 ladder
|
||||
digests all resolve today (`audits/version-travel-2026-09-26/A7/`). **Accepted as a known limit — operator ruling
|
||||
2026-10-12 (S8, `09` §3 decision 203; R-698 closed).** What a household meets: a restore whose version the maker has
|
||||
deleted stops at the image pull, the app stays as it was, and the household uses another copy or a newer version.
|
||||
deleted stops at the image pull, the app stays as it was (since D48 by ORDER — the pull comes before the stop; before it,
|
||||
by the D22 put-back), and the household uses another copy or a newer version.
|
||||
The operator's follow-up — *„will need to mirror the apps we support later, just to be sure"* — is **R-933**: mirror
|
||||
the images of every catalog app we support into our own registry, before scale. Until it is built, this limit holds.
|
||||
**Since decision 53 (`09` §3, 2026-09-30 evening):** a box keeps only the image each app service runs and the one before
|
||||
it; older images of the app are deleted. A restore that needs an older version re-pulls it — as before; the limit
|
||||
above is unchanged, and kept data (decision 40) is not touched by the image clean-up.
|
||||
|
||||
**[DESIGN] 2026-10-25 — a restore downloads first (operator ruling D48, `09` §3 decision 257; controller `2d71270`,
|
||||
unreleased — rides v0.343.0).** „The app stays as it was" in the paragraph above is now true BY ORDER, not by the D22
|
||||
put-back. After the existing refusals and before the first stop or change, every restore path resolves every image the
|
||||
definition that will RUN names — present on the box, or pulled now — under the image-work lock held from the first
|
||||
check until the restore's last start (R-863: the image clean-up cannot delete a pulled image before the start). A pull
|
||||
that fails is a REFUSAL: the app is not stopped, its data is not moved, and the household reads which version, and
|
||||
why: „Ennek a mentésnek a verzióját (%s) nem lehet letölteni: a készítője már nem kínálja…" when Docker says the version
|
||||
does not exist (`manifest unknown`, `not found`, `pull access denied`), „…most nem lehet letölteni… Próbáld újra
|
||||
később…" for everything else (a busy or unreachable registry). The operator gets one `backup_run_failures` line (leg
|
||||
`restore-image-unavailable`). The paths: the local unit restore — and with it the second drive's unit restore, the
|
||||
second drive's whole restore (its `CheckOnly` pass carries the pull, so its file half never runs first), a kept-data
|
||||
load from a unit and from off-site, and a set-aside unit (D47) — the off-site restore, the second drive's file restore
|
||||
and the no-unit fallback. Not covered: the `.fab` import — it removes the running app's volumes before its definition is pulled (R-1279).
|
||||
|
||||
### 6.7 Why an off-site run failed — the failure classifier (R-571)
|
||||
|
||||
**[FACT, read from source 2026-10-05, felhom-controller `114ff27`]** When an off-site (restic) run fails, the
|
||||
@@ -1651,6 +1686,60 @@ removed the old container at start; a restore probe during the copy was refused;
|
||||
15-second test limit → the container removed, the partial copy stopped growing. Controller:
|
||||
`internal/backup/tier2_copy_lifecycle.go`.
|
||||
|
||||
### 8.2d An earlier install's unit is set aside, never rewritten [DESIGN, operator ruling D47, 2026-10-25 08:38; `09` §3 decision 256; R-1269 — controller `02daebe`, unreleased, rides v0.343.0]
|
||||
|
||||
**[FACT] Measured before (controller 0.342.0, which is `e1613ae` for these files):** the catalog sweep 2026-10-24
|
||||
(three times) and 9202 2026-10-25 (`audits/restore-truth-2026-10-25/before/`): Tandoor, one account, „Mentés most",
|
||||
remove keeping data and backups, install again. Within five minutes the periodic refresh rewrote ONLY the unit's
|
||||
`compose/app.yaml` (checksum `cbfcb538…` → `e19d2610…`) — the new install's database password — beside the OLD install's
|
||||
data. Its one „same definition" test was the image pins (`recovery_unit.go:194`, the R-696 `frozen` rule), and a
|
||||
reinstall of the same version has the same pins. A restore then paired the new password with the old data directory:
|
||||
`password authentication failed` ×44 (app) / ×23 (postgres), the app stayed `starting`, the account was gone from
|
||||
view. The next night's dump legs would have overwritten the old data as well, and the stale-drive prune deleted a unit
|
||||
on another drive whatever install it belonged to.
|
||||
|
||||
**[DESIGN] The rule.** A recovery unit's definition and its data are written only together, by a backup of the same
|
||||
install. Every install has an identity (`install_id` in its `app.yaml`): minted at a fresh install and at a `.fab`
|
||||
import, carried by every load-then-save, taken FROM the unit by a restore (the app then holds that unit's data), and
|
||||
minted once for an app installed before D47. The unit records it in its own `app.yaml`. Every writer of a unit asks
|
||||
first — the capture and its refresh, the night database dump, the pre-backup and the update's pre-backup dumps, the
|
||||
volume dump, the second drive's unit copy, the stale-drive prune. A unit of ANOTHER install (different identities; for
|
||||
a unit written before D47, a carried secret that differs — a reinstall mints new ones) is **moved whole** (a rename on
|
||||
its own drive; never a copy, never an edit) to `<namespace>/backups/set-aside/<app>/<UTC stamp>/` and recorded as a
|
||||
kept copy (§8.2a's record: kind `unit`, reason `earlier_install`); the new install then gets its own unit from its
|
||||
first backup. A move that cannot be done **refuses the write** and leaves the old unit byte for byte (an ERROR line
|
||||
names the app and both identities). Nothing moves a unit while that app is being restored.
|
||||
|
||||
**What the household sees.** The restore page lists the set-aside unit under „Megőrzött másolatok" („…· egy korábbi
|
||||
telepítés mentése") with **„Visszaállítás"** — the one unit-restore body, with the unit's own database password — and
|
||||
„Eltávolítás". Only those two, and the app's remove with „delete its backups too", ever remove it (the §8.2a / R-1252 H9
|
||||
rule). After a restore from it, the app is that unit's install; the app's own unit of the install it replaced is then
|
||||
set aside at the next backup — never lost.
|
||||
|
||||
**Known limit, stated.** A box that reinstalled and refreshed BEFORE this release already holds a rewritten unit; its
|
||||
old password is gone and nothing can bring it back. A unit from before D47 of an app with no carried secret has no
|
||||
signal: it is treated as this install's (the old behaviour) and gets the identity at its next write.
|
||||
**Why a volume app's reinstall asks no kept-data question:** the question (§6.5, decision 36) is about the app's drive
|
||||
FOLDER (`${HDD_PATH}` binds under `appdata/`); a volume app keeps nothing there — „remove, keep my data" removes its named
|
||||
volumes and keeps only its backups — so the install starts empty, and the kept backup is now the set-aside unit on the
|
||||
restore page. (The missing offer at install is its own row, R-1278.)
|
||||
|
||||
### 8.2e A restore whose app does not start is never „success" [DESIGN, R-1270, 2026-10-25 — controller `b680db8`, unreleased, rides v0.343.0]
|
||||
|
||||
**[FACT] Measured before:** the health check after a restore was a WARN on every path, so a Tandoor restore whose app
|
||||
stayed `starting` was recorded `ok: true`, „…visszaállítva — az alkalmazás újraindult." (catalog sweep 2026-10-24; 9202
|
||||
2026-10-25, `audits/restore-truth-2026-10-25/before/`). **[DESIGN]** Every restore path — the unit restore (so the
|
||||
second drive's unit and whole restore, a kept load, a set-aside unit), the off-site restore, the second drive's file
|
||||
restore and the no-unit fallback — returns „not started" when the app does not reach running within the health wait
|
||||
(90 s). The household reads „A(z) %s: az adatokat visszatöltöttük, de az alkalmazás még nem indult el. Várj pár percet,
|
||||
majd nyisd meg újra. Ha így marad, szólj nekünk." (the record `ok: false`, `not_started: true`); the operator gets one
|
||||
`backup_run_failures` line (leg `restore-not-started`). **The safest reading, the operator may change it:** a timeout
|
||||
does NOT pour the old data back — a slow first start (a database migration) is common, and a put-back would throw away a
|
||||
correct restore. The data stays restored and the restore's undo copy is KEPT — the same „kept for support" state a failed
|
||||
put-back leaves (§8 D20/D22), so no new retention rule. A watcher looks 30 more minutes: when the app runs, the record
|
||||
turns to success („…elindult: a visszaállítás kész.") and the copy is removed, as after any successful restore; if it
|
||||
never runs, the copy stays for support.
|
||||
|
||||
### 8.2c Decisions in code that no document recorded [DESIGN — undocumented, found 2026-10-24, review round 2 — the operator confirms them through one register row]
|
||||
|
||||
Read at `file:line` on `main` (controller `321df1f`, hub `44d19042`) by the main session:
|
||||
|
||||
@@ -1089,6 +1089,24 @@ its length, and both fixes cost something the household would notice — operato
|
||||
`plex: security fix CVE-2026-96652,CVE-2026-96654,CVE-2026-96655,CVE-2026-96656 taken` and ended `done` in 30.1 s
|
||||
after the update's second-drive copy and undo copy. Released as controller 0.341.0, global floor 11:24 UTC.
|
||||
|
||||
### 2026-10-25 (08:38) — D47: nothing changes an existing backup; D48: a restore downloads first (operator rulings by starting the run; R-1269)
|
||||
|
||||
256. **An existing recovery unit is never changed in part (D47).** A unit's definition (compose, pins, `app.yaml`) and
|
||||
its data are written only together, by a backup of the same install. A new install never adopts, rewrites or
|
||||
deletes a unit that an earlier install left; that unit is set aside whole and stays restorable. *Operator ruling
|
||||
2026-10-25 08:38, taken by starting the R-1269 brief.* Why: on 2026-10-24 the catalog sweep measured three times
|
||||
that a reinstall's own five-minute refresh wrote the new install's database password into the unit „remove — keep
|
||||
backups" had kept; a restore then could not open the old data (R-1269). Built: every install has an identity
|
||||
(`install_id`), the unit records it, and every writer asks first — `07` §8.2a „An earlier install's unit is set
|
||||
aside" (controller, unreleased; rides v0.343.0).
|
||||
257. **Every restore path resolves and pulls every image its definition names before the first change to the app; a
|
||||
failed pull is a refusal with the app untouched (D48).** *Operator ruling 2026-10-25 08:38, taken by starting the
|
||||
brief.* Why: the restore stopped the app and moved its data first and pulled only inside the database-only start;
|
||||
a version that could not be downloaded then ended in the D22 put-back, a full stop-and-pour-back for nothing.
|
||||
Built: `07` §6.6 „The limit — the image is not in the backup" (controller, unreleased; rides v0.343.0).
|
||||
**Confirmed, no change:** `07` §6.6 — a restore brings the app back at its data's version, then the guarded update
|
||||
climbs the ladder; R-698 (a deleted version cannot come back) and R-933 (our own image mirror) are unchanged.
|
||||
|
||||
### 2026-10-15 (07:48–08:20) — bake first; Plex through its own key; HSTS one day (recorded before the media build)
|
||||
|
||||
247. ~~**Bake first.** A controller version is RELEASED only when its golden is baked.~~ **REVERSED by decision 250,
|
||||
|
||||
Reference in new issue
Block a user