v0.275.0: a backup's data and its version travel together (R-696, 07 §6.6, D4 option A); R-695, R-691, R-694
gates / gates (push) Successful in 23s
gates / gates (push) Successful in 23s
The unit's data files are stamped with the versions that wrote them; the capture keeps the definition the data belongs to; a restore never starts data under another version's definition (unit restores refuse a mismatch; the off-site restore writes the snapshot's definition); every tier's time is its data's; the conversion-copy release needs a dump on the new engine. File-browser sync single-flight + no empty kept folder (R-695); the kept view joins the folder's owning group, language switch resyncs (R-691); a restore-generated login is not shown as the password (R-694). Red-proofs in felhom.eu/documentation/audits/version-travel-2026-09-26/. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
+17
-1
@@ -1337,9 +1337,25 @@ backups/primary/<app>/
|
||||
├── compose/ docker-compose.yml + .felhom.yml + app.yaml (0600 — CARRIES the portable secrets)
|
||||
├── db-dumps/ app-consistent DB dump(s)
|
||||
├── volume-dumps/ named-volume tars
|
||||
└── manifest.json image pins, secret NAMES, data_key names, portable NAMES, checksums, secret_source
|
||||
├── data-stamps.json per data file: when it was written, the pins and running ref@digest that wrote it (v0.275.0)
|
||||
└── manifest.json image pins (the app's CURRENT ones), secret NAMES, data_key names, portable NAMES, checksums,
|
||||
secret_source, and `data` — the time and versions of the data (v0.275.0)
|
||||
```
|
||||
|
||||
- **The data and its version travel together (v0.275.0, R-696, `07` §6.6).** Each backup leg stamps the
|
||||
file it writes (`stampDataFile`: size, mtime, the definition's pins, `installed_images` as `ref@digest`); the
|
||||
capture folds the stamps into the manifest's `data` block and **keeps the definition its data belongs to** —
|
||||
a refresh after an update no longer rewrites `compose/` until the next data run replaces the data. A restore
|
||||
(own unit, second drive, kept-data Load) starts the data under that definition and refuses, before touching
|
||||
anything, a unit whose `compose/` names other pins than its data or whose files were written by different
|
||||
versions (`ErrUnitVersionMismatch`); the off-site restore writes the snapshot unit's definition into the stack
|
||||
dir when its version differs from what runs. An app brought back at an older version is climbed by the normal
|
||||
guarded update; the restore page's first sentence says so (or, with no tested step from that version, that
|
||||
the box will not update it by itself). **Every tier's time is its DATA's**: Tier 1 `data.at` (else the newest
|
||||
data file, never the manifest, never a `pre-restore-*` undo copy), Tier 2 capped by the mirror's data time,
|
||||
Tier 3 capped by the data time recorded at push (`settings.offsite_data_at`). A unit without `data` (written
|
||||
before v0.275.0) restores as before, with a WARN.
|
||||
|
||||
- **The secret split (D5, schema 2, operator ruling 2026-07-30).** The unit was secret-free until
|
||||
v0.188.0, and that made "restore from the drive alone" false: the fast, local, customer-doable
|
||||
Tier-1/2 restore secretly depended on the slow, operator-driven whole-guest restore, because a
|
||||
|
||||
Reference in New Issue
Block a user