R-108: network storage may not host an app's data namespace (v0.187.0)

This is D5's precondition and it is now met.

An app's namespace root IS its backup root: namespaceRoot returns a non-system
drive path as-is, so the recovery unit lands at <HDD_PATH>/backups/primary/<stack>/.
On a NAS that sits inside the share, which FileBrowser binds WHOLE — share root,
:rslave, download:true.

The bind was NOT narrowed, and establishing why inverted the fix. The share-root
:rslave bind is load-bearing (a 2026-07-22 probe proved an in-container access
through it wakes the idle automount trigger), and scoping is undefinable anyway:
apps on a share store at <share>/<app>, there is no userdata/ layer, and creating
one would write Felhom convention onto a customer's own NAS, which R-67 forbids.
So the browsing surface cannot be narrowed and the backup tree must never be
placed under it. Operator ruling: refuse the placement, keep the browse bind.
Tier 2 already refuses network targets for this reason (F-6C-1).

Nothing stranded: zero apps on network storage across all six hub customers
including Peti. R-67's browse capability is byte-identical.

FIVE surfaces, not the four the register named — settings.RefuseAsAppNamespace is
the single predicate. The deploy POST is the real boundary (it accepts any
caller-supplied HDD_PATH; DeployStack validates only os.Stat). Surface 4,
handleStorageDecommission mode=migrate, guarded only its SOURCE, so a whole
namespace could be decommissioned ONTO a NAS — that one is not in the register.

Fails closed: /mnt/felhom-drives holds both kinds, Kind exists only on a
registered path, so an unregistered path under that root refuses.

Supersedes README's "NAS backup locality — decision A" (v0.118.0).

9 tests, all non-effect (nil stackMgr, so a guard that misses panics rather than
passing). 4 red-proofs, each mutation asserted to have landed.
Suite rc=0, 27 packages, 0 FAIL. vet rc=0. Template + emoji gates OK.
This commit is contained in:
2026-07-30 14:10:20 +02:00
parent b331f18424
commit 2f27a363d5
10 changed files with 693 additions and 84 deletions
+18 -6
View File
@@ -744,12 +744,24 @@ The nightly backup has two phases that run sequentially. All paths are **per-dri
> drive. Guarded: only for a DEPLOYED app whose CURRENT drive differs from the dir's drive; never the
> current-drive dir (the live restore point) or an undeployed app's dir; strictly under `backups/primary/`.
> **NAS backup locality (v0.118.0, CAMPAIGN-3 Part 4 — decision A).** A NAS-resident app's tier-1
> artifacts live **on the NAS itself** (`nas-media/backups/primary/<app>`), beside the data. During a
> NAS outage both the app data AND its freshest tier-1 dump are on the dead device — the **tier-2**
> cross-drive copy to a local drive is the off-NAS leg that saves them (and only after it has run).
> This locality is deliberate (kept in the fork over retargeting tier-1 to a local drive); the tier-2
> copy is the mitigation. Stated here so the outage window is never a surprise.
> **~~NAS backup locality (v0.118.0, CAMPAIGN-3 Part 4 — decision A)~~ — SUPERSEDED by R-108
> (v0.187.0, 2026-07-30).** Decision A said a NAS-resident app's tier-1 artifacts live **on the NAS
> itself** (`nas-media/backups/primary/<app>`), beside the data, with the tier-2 cross-drive copy as the
> off-NAS mitigation. That locality is exactly what made a `backups/` tree reachable through
> FileBrowser's share-ROOT bind (`download: true`), and it is why architectural target **D5** — app
> secrets in the local recovery unit — could not be adopted.
>
> **An app's data namespace may no longer live on network storage at all** (operator ruling
> 2026-07-30), so the case decision A described can no longer arise: no app on a NAS ⇒ no
> `backups/primary/` on a NAS. `settings.RefuseAsAppNamespace` is the single predicate; every
> placement surface consults it (deploy POST, per-app migrate, decommission-with-migrate). The NAS
> keeps its **browse** capability unchanged — the share-root `:rslave` bind is load-bearing for
> automount wake (R-67) and was deliberately NOT narrowed; scoping it is undefinable anyway, since
> apps on a share store at `<share>/<app>` and creating a `userdata/` layer would write Felhom
> convention onto a customer's own NAS.
>
> The NAS-outage window decision A warned about is therefore also gone: an app's tier-1 artifacts are
> always on a local drive now, because the app itself always is.
**Drive layout (v0.26.0):**
```