07-backup-architecture: rewrite as the recovery model (D1-D6); D5 BLOCKED by R-108
Replaces the 2026-07-14 DRAFT entirely - it was verified against controller v0.132.0, 51 versions stale, while being cited as authoritative. Now verified against controller 0.183.0 / agent 0.110.0 / hub 0.80.0 / catalog 4252121. NOT ratified. Every statement is labelled [DESIGN] (a decision from the architecture discussion) or [FACT] (file:line, live output, or a citation to _recovery-inventory-2026-07-28.md). Phase 0 verified D5's premise and it FAILED. Ten read surfaces were checked; nine are clean (SMB under both namespace shapes, FileBrowser for local drives, .fab import and download, /api/debug/*, all three ServeFile sites, registering the backup dir as a drive). One is not: FileBrowser binds a NETWORK share at its ROOT while local drives are userdata-scoped, and no IsNetwork() filter guards the deploy dropdown, the per-app migrate targets, or handleStorageMigrateApp. So an app namespace - and under D5 its plaintext secrets - can land somewhere a browsing surface serves with download:true. Not a leak today (the unit's app.yaml is secret-stripped); it becomes one under D5. D5 is therefore written as a BLOCKED target, not as settled. -> R-108 Core artifact is the failure -> recovery matrix (15 rows). RTO cells carry only measured durations; 12 are blank and each blank is listed explicitly as a finding. RPO cells carry the configured cadence that bounds RPO, labelled as such - no RPO has ever been measured from an incident and none is invented. The 7/53 vs 9/43/1 Tier-2 coverage counts are both recorded, unresolved; neither adopted. Coupling (S-1): capability map section C now cites the matrix instead of restating routes; ROADMAP gains R-102..R-108; OPEN-ITEMS gains only the in-flight R-108 plus C9-F1b/F3/F4 -> R-n cross-references; CONTEXT records D1-D6 as standing ruling S-3. No code, no version bump, no deploy, no box touched.
This commit is contained in:
+39
@@ -25,6 +25,45 @@ reader meets the warning before the content, not after acting on it. Origin:
|
||||
versions stale (live v0.173.0), and cited as authoritative throughout the R-80 diagnostic. Ratifying
|
||||
or retiring it is → **R-83**.
|
||||
|
||||
**S-3 — the recovery model: six decisions, 2026-07-28.** Taken in an architecture discussion and
|
||||
expressed in the `07-backup-architecture.md` full rewrite (which replaces the 2026-07-14 DRAFT
|
||||
entirely — that doc was verified against controller v0.132.0, **51 versions stale**, while being
|
||||
cited as authoritative). They are **decisions, not observations**; the rewrite labels every statement
|
||||
`[DESIGN]` or `[FACT]` so a reader can tell which is which.
|
||||
|
||||
- **D1 — two lanes, different owners.** *Customer, unassisted:* files and app data, through the
|
||||
„Visszaállítás" surfaces. *Operator:* LXC guest and host recovery. This is a deliberate product
|
||||
decision, not a limitation to apologise for.
|
||||
- **D2 — encryption follows the boundary, not the tier.** Data on the customer's own drives is
|
||||
**plaintext**, deliberately: someone who can take the second drive can take the first, local
|
||||
encryption adds a key-loss path that turns a working backup into a brick, and it would break
|
||||
FileBrowser and SMB browsing, which is a feature. Data **leaving the premises** is encrypted
|
||||
(restic, PBS) — the provider must not be able to read it.
|
||||
- **D3 — recovery is a chain, not a set of tiers.** Every app-tier restore is conditioned on the
|
||||
whole-guest tier: Tier-1 needs the guest's secrets (`restore_unit.go:17-22`, *"Regenerate
|
||||
NOTHING"*), and Tier-2/Tier-3 need the app still deployed. **3-2-1 describes copies; it does not
|
||||
describe recovery.** This is the reason the rewrite exists.
|
||||
- **D4 — three-part model.** *Recipe* (scaffolding, hub) · *Escrow* (identity key + restic repo
|
||||
password, hub, R-wrapped) · *Bytes* (the tiers). Losing one is a different problem from losing
|
||||
another, and the failure→recovery matrix is organised around that.
|
||||
- **D5 — R's role narrows. CONDITIONAL, and currently BLOCKED.** The target is app secrets travelling
|
||||
with the LOCAL recovery unit, so Tier-1/Tier-2 restore work without the guest and without R,
|
||||
leaving R needed for offsite recovery and host identity only. **Not adopted:** the premise
|
||||
(the backup tree unreachable from every browsing/download surface) was tested and failed — see
|
||||
**R-108**. Every other surface verified clean; the exposure is FileBrowser's root-bind of network
|
||||
shares combined with the absence of an `IsNetwork()` filter on the deploy and per-app-migrate
|
||||
paths.
|
||||
- **D6 — the trust model, stated honestly.** The operator holds root SSH on every box; *"the
|
||||
operator cannot read customer data"* was never the actual security property and no document may
|
||||
imply it. What R **does** provide, and must keep providing: **the hub alone is not enough** — a
|
||||
compromised hub yields blobs nobody can open, *provided the operator's key is never stored in the
|
||||
hub*. That proviso is why escrow custody is an open decision (`07` §11-A).
|
||||
|
||||
Five decisions were deliberately **left open for the operator** and are recorded, unanswered, in
|
||||
`07` §11: escrow custody · lost-R policy · per-scenario RTO/RPO targets (**none have ever been
|
||||
stated**) · Hetzner as a single failure domain · and `local` vzdump sharing a physical device with
|
||||
the guest it backs up. Gaps minted the same session: **R-102 … R-108**.
|
||||
|
||||
- **2026-07-27 — R-85: the DR tier is restore-tested UNATTENDED, and its failure is HEARD.** agent
|
||||
v0.104.0 + hub v0.77.0. R-82 proved a PBS restore round-trip **by hand**; three defects kept the
|
||||
unattended path from covering it. (a) `buildRestoreTestScheduler` built ONE runner on
|
||||
|
||||
Reference in New Issue
Block a user