docs(shares): R-7b v0.145.0 — CHANGELOG, CONTEXT, REUSE, README; caveats cleared

The samba_classify.go SCOPE NOTE and the README KNOWN GAP both described a gap that
R-7b closes; both now describe the sibling-shares-source execution instead.
This commit is contained in:
2026-07-18 13:06:41 +02:00
parent 900c870212
commit 3b70a9e9ab
5 changed files with 146 additions and 11 deletions
+72
View File
@@ -1,5 +1,77 @@
## Changelog
### v0.145.0 — R-7b: share data enters the live backup runs (Model B) + samba liveness (2026-07-18)
**The „Felhőmentés" toggle on the Megosztás page is now true.** Before this release a customer could
switch a share to „Felhőmentés: bekapcsolva" and the page would render exactly that while the files
dropped on it were in **no backup at all**`backup.RunTier2` short-circuits on `os.Stat(unitDir)`
before the classification seam, and the offsite runner enumerates `GetOffboxApps()`. A share-only
infra stack has neither a recovery unit nor an offbox toggle, so it fell through both engines. R-7b
closes that with a **sibling shares source** in each tier.
**Model B (Viktor's ruling, 2026-07-18) and its invariant.** Share data enters the runs through
NEW, ADDITIVE job/leg code that reuses the proven primitives — the tier-2 mirror seam, the restic
wrappers, the soft-quota/enlargement gate, the status recorders — while leaving **every per-app engine
path byte-identical**. Not Model A (a synthetic recovery unit breaks on multi-drive shares and wraps
1 KB of JSON in dump machinery) and not engine-loop surgery. The invariant is enforced by test, in
both tiers, with red-proofs.
- **Payload (`internal/backup/shares_payload.go`, new):** a staging dir holding
`_shares-manifest.json` (the share definitions, sorted → **byte-deterministic** for an unchanged
registry, so a no-op run gives the mirror nothing to rewrite) plus a **best-effort** `passdb.tar`
captured from the samba container. A restore therefore returns the files, the share configuration
AND the SMB password hash — not just bytes on a disk. The credential copy is SECRET-BEARING: 0600,
never logged at INFO, never in a report or a committed file. A down container degrades to
manifest-only and KEEPS any previously captured copy (a stale credential beats none for DR).
- **Tier-2 shares job (`internal/backup/tier2_shares.go`, new):** runs after the per-stack loop in the
same orchestrator run. Shares are grouped **by source drive** — a household's shares can span disks
and each group needs its own cross-drive target — into
`backups/secondary/_shares/<sourceDriveKey>/<share>` with the payload at `_payload/` and the layout
marker written **LAST**. Reuses `selectTier2TargetFrom` (a narrow source-drive seam extracted from
`selectTier2Target`; the headroom math is untouched), `tier2ReconcileRoots` (a pure extraction),
`tier2SafeRemove` and the `recordTier2*` helpers.
- **Offsite shares leg (`internal/backup/offbox_shares.go`, new):** ONE additional
`restic backup --tag felhom-offbox --tag _shares` carrying the manifest staging dir plus every
MANDATORY share folder, hooked in AFTER the per-app loop and BEFORE retention — so
`forget --group-by host,tags` covers the `_shares` group with **no flag change**. Same enlargement
arithmetic as the per-app gate. **Degradation contract:** a quota-blocked push falls back to the
MANIFEST ONLY, never to nothing — definitions protection must not regress because the files stopped
fitting.
- **Restore „Megosztások" (`internal/backup/shares_restore.go`, new):** siblings of the per-app
scratch/place pair. Files are merged **missing-only** (never overwriting) and every destination is
**prefix-asserted** against registered LIVE storage roots — a snapshot is untrusted layout input, so
a path that no longer sits under a live root is refused rather than created. Definitions merge with
**existing-wins** (a restore must never silently flip a live share's settings; skipped ones are
named in the flash). Then `ReconcileSamba` re-renders smb.conf, and the credential goes back into
the named volume best-effort. Routes `POST /backup/shares/{restore,place}`.
- **Samba liveness (the fold-in):** `monitor.EffectiveProtected` gains a settings-backed dynamic
extra, so a dead sharing service raises the same protected-container issue → alert → Hungarian
degradation e-mail as a dead traefik — but only while sharing is ON. It watches the **container**
name (`infra.SambaContainerName`), which is deliberately NOT the stack name. **Finding: the
alert/e-mail pipeline needed no further change** and no new event type is introduced, so the
`allowedEventTypes` gotcha does not apply.
- **UI truth-up:** the Megosztás page states per-tier status (2. mentés / távoli mentés, amber only on
deviation) and links to the restore page. The reserved `_shares` key is mapped to „Megosztások" at
the notification and Hungarian-prose boundaries ONLY — the persisted `EnlargedBlocked` set, the
restic tag and the dest path keep the raw key, because templates index by it.
- **RESERVED-NAME FINDING (the task's assumption was false):** `settings.nbNameRe` begins with
`[A-Za-z0-9_]`, so „_shares" **was an accepted share name** — the underscore namespace was not in
fact reserved. `ValidateSMBShareName` now refuses a leading underscore (on ADD only, so existing
shares are never retroactively invalidated), and `RunAllTier2`/`RunOffboxBackup` additionally skip a
`_shares` STACK loudly as defense in depth.
- **`infra.SambaContainerName` / `SambaPassdbVolume` / `SambaPassdbMount`** become the single source of
truth for the samba container identity — the compose renderer interpolates them, and stacks, backup
and monitor all read them instead of repeating string literals.
- **Bug found by test:** `shareSourceDrive` returned a slash-normalised path, which made the target
selector's source-drive equality check miss — a share group could have targeted its own source
drive (a same-disk copy pretending to be tier 2). Fixed; POSIX-only in effect, but real.
- **Tests:** 24 new/extended cases in `internal/backup` + 2 in `internal/monitor`. **Six red-proofs
run and reverted, all fired:** (1) shares leg appending into the app's argv → B isolation FAILS;
(2) mandatory→offsite mapping inverted → Scenarios A+B FAIL; (3) manifest-only degradation dropped →
Scenario C FAILS; (4) prefix-assert removed → place-guard traversal FAILS; (5) dynamic samba extra
removed → Scenario E enabled-case FAILS; (6) shares destBase dropping the reserved segment → tier-2
isolation FAILS.
### v0.144.0 — „Megosztás": LAN SMB file sharing (R-7 slice 1) (2026-07-18)
The customer turns on network sharing, sets ONE household SMB password, and exports folders. The box