docs(samba): CHANGELOG v0.144.0 + REPORT + CONTEXT + README §16 + REUSE

Records the R-7 slice 1 ship, the four red-proof outcomes, live-validation
evidence, the two bugs live validation caught, and the Part-4 Step-1 enumeration
finding + the reported design fork (share data classified but not yet in a live
backup run — needs a Viktor ruling, suggested R-7b).
This commit is contained in:
2026-07-18 12:03:24 +02:00
parent b409f5eee2
commit a4c82a2651
5 changed files with 329 additions and 49 deletions
+61
View File
@@ -1,5 +1,66 @@
## Changelog
### 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
appears in Windows Explorer's Network view as `\\FELHOM`; opening a share and writing to it works, and
every SMB write lands as uid:gid 1000 so apps and both backup tiers see consistent ownership. SMB is
an **embedded controller feature**, not a catalog app — it needs host networking (the R-6 spike
verdict), its config is a generated share list, and its roots ride the backup classification.
- **New infra image `felhom-samba:1.0.0`** (`controller/infra-images/samba/`, built by
`controller/scripts/build-samba-image.sh`): pinned alpine 3.21 (`sha256:48b0309c…`) + smbd + **nmbd**
+ wsdd + tini. Dumb by design — `smb.conf` is bind-mounted READ-ONLY, nothing is templated inside,
no name/password is baked, passdb lives on a named volume. nmbd is REQUIRED alongside wsdd: the R-6
spike proved wsdd-only leaves the box *visible* but the Explorer double-click fails `0x80070035`
(no flat-name resolution). Anonymous pull verified from the guest.
- **Settings (`internal/settings/smb.go`, new):** `SMBSettings{Enabled, ServerName, UserSet}` +
`SMBShare{Name, Path, ReadOnly, Offsite, CreatedAt}` registry with NetBIOS-safe validation
(≤15 chars, no slash/dot) and case-insensitive name-collision refusal. **The household SMB password
is NEVER persisted** — only the `UserSet` boolean.
- **Renderers (`internal/infra/samba.go`, new):** pure `RenderSambaConfig` (hardened global block:
`server min protocol = SMB2`, `bind interfaces only = yes`, `interfaces = lo eth0`,
`disable netbios = no`, `map to guest = never`, per-share force-user block) and
`RenderSambaCompose` (`network_mode: host`, pinned image, config `:ro`, passdb volume, one bind per
share — `:ro` for read-only shares as defence in depth beside smb.conf). Exact smb.conf golden test.
- **Lifecycle (`internal/stacks/samba.go`, new):** `ensureSamba` joins `EnsureBaseStack` after
filebrowser, gated on `SMB.Enabled` (the cloudflared conditional-deploy precedent); `ReconcileSamba`
runs after every mutation. Idempotent — unchanged config + running container performs **zero**
compose calls. Config writes are atomic (tmp+fsync+rename). The password is applied via
`smbpasswd` on **STDIN** (never argv, never logged). Disable = `compose down`; the passdb volume and
every shared folder are KEPT. A share on a disconnected/decommissioned drive is rendered ABSENT from
smb.conf (never export a dead mountpoint) while its config is retained.
- **Protection:** `samba` is protected in CODE (`config.alwaysProtectedStacks`) because
`cfg.Stacks.Protected` comes from the golden-generated controller.yaml and predates it. This also
makes the app-backup loops correctly skip it (it is infrastructure, not a customer app).
- **UI (`internal/web/sharing_handlers.go` + `templates/sharing.html`, new):** a new top-nav category
**„Megosztás"** → **„Hálózati megosztás"**. Enable/server-name card, household password, shares table
(Név · Mappa · Írásvédett · Felhőmentés · Törlés — "a mappa és a fájlok megmaradnak"), and a create
flow (new folder under `<storage>/shares/` or an existing folder via the browse modal).
- **Picker security:** every customer-supplied path goes through `sharingResolvePath` — absolute →
`EvalSymlinks` → containment in a registered LIVE storage root → deny-listed system subtree →
is-a-directory. Refusals are **uniform** so the picker can never act as a filesystem oracle. The
deny-list is DERIVED from `stacks.ProtectedHDDPaths` (provably a subset, so it can only shrink,
never drift); the drive root is an exact-match denial so user-data folders under it stay shareable.
`sharingResolveStorageRoot` is a separate, strictly tighter check for the new-folder parent.
- **Backup classification [R4] (`internal/stacks/samba_classify.go`, new):** `ClassifiedBinds("samba")`
resolves from the shares registry instead of catalog metadata. Felhőmentés ON → `mandatory`
(offsite + tier-2); OFF → `optional` (tier-2 only); smb.conf/passdb never classified. Verified
through the real `ComputeCaptureSet` tier filter including the negative. **Zero backup-engine edits.**
- **KNOWN GAP (reported design fork, not improvised):** making that seam correct does NOT by itself put
share data into a live tier-2/offsite RUN. `backup.RunTier2` short-circuits on `os.Stat(unitDir)`
before it ever calls `GetStackClassifiedBinds`, and the offsite runner enumerates
`settings.GetOffboxApps()` — both are recovery-unit shaped, which a share-only infra stack has not.
Teaching them about one is more than an enumeration tweak, so per the task's STOP clause it is
reported rather than improvised. See `REPORT.md`.
- Live-validated end-to-end on demo guest 9201 through the REAL endpoints (curl against the exact
routes the UI posts to; the UI is password-gated so no browser leg): enable → password → create both
share kinds → guard refusals (appdata/backups//etc/drive-root all uniform 400) → smb.conf + container
+ `:ro` bind verified on the box → Windows 11 workstation: `Test-NetConnection 445` True, nbtstat
`FELHOM <00>/<03>/<20> Registered`, `ping FELHOM` resolves, SMB write/read byte-compare PASS, and a
**write to the read-only share refused with no effect**. SMB-written files land as `1000:1000`.
Explorer render is Viktor's remaining human leg.
### Build infra — build root relocated (2026-07-18)
- `controller/build.sh`: `REPO_DIR` + `WEBSITE_ASSETS_DIR` repointed `/home/kisfenyo/…`