0eba37d5cd
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UuFPHmHNrCJj1VhY6QdDMU
98 lines
6.2 KiB
Markdown
98 lines
6.2 KiB
Markdown
# REPORT — R-67: the NAS share appears in FileBrowser (controller v0.160.0, 2026-07-22)
|
||
|
||
**Spec:** the R-67 prompt (Phase-0 probe + implementation, controller-only, v0.159.0 → v0.160.0).
|
||
**Commit:** `59cd260` on `main` · image `felhom-controller:0.160.0` · deployed to **demo-felhom
|
||
9201** and **demo-hp 9201**, both healthy. No agent changes; MinAgent unchanged.
|
||
|
||
## 1. Phase-0 probe — GO
|
||
|
||
On demo-hp, with the Felhom-Share automount confirmed IDLE (autofs trigger present in the guest's
|
||
/proc/mounts, `timeout=60`, no cifs mount):
|
||
`docker run --rm -v /mnt/felhom-drives/Felhom-Share:/probe:rslave alpine ls -la /probe`
|
||
listed the REAL share content (demo-felhom's `teszt/` + macOS dotfiles) and left cifs mounted in
|
||
the guest. **An in-container access through an rslave bind wakes the idle trigger** — one
|
||
namespace further than the spike's in-guest proof. The design shipped exactly as specified; the
|
||
fallback fork was not needed.
|
||
|
||
## 2. What shipped
|
||
|
||
`syncFileBrowserMounts`' path loop extracted into the pure **`buildFileBrowserPaths`**
|
||
(`fbPathDeps`: mount probe / FS classifier / skeleton fn / logger — every edge seamed), returning
|
||
BOTH the mount lines and the config source set so a source can never render without its mount.
|
||
|
||
- **Network branch:** bind = the share ROOT, `…/<name>:/srv/<name>:rslave` (`:rslave`
|
||
load-bearing — automount wake / idle-unmount propagate into the RUNNING container). No
|
||
`EnsureUserdataSkeleton`, no userdata scoping, nothing written toward the NAS. The drive-absent
|
||
gate does NOT apply (idle is healthy — the old gate skipped an idle share forever); the gate is
|
||
the **stub** classifier verdict via the existing `Server.classifyFSPath` seam: stub ⇒ excluded
|
||
from mounts AND sources this pass + WARN (`namespace sees a local stub, not the NAS`). autofs /
|
||
network / unknown / nil-classifier include (fail open). **The stub rationale:** an exposed local
|
||
stub dir accepts uploads that the real mount later SHADOWS — the customer's files silently
|
||
vanish from view; exclusion-until-recovery is the only safe rendering.
|
||
- **Drives:** behavior byte-identical (userdata scoping, skeleton, drive-absent gate, always in
|
||
the source list) — asserted by test, and observed live (§4).
|
||
- **Triggers:** NAS add-success (`runNetAdd` done) + remove (`handleNetStorageRemove`) now call
|
||
`SyncFileBrowserMounts()` (same debounced/H5-mutexed path as the drive flows). Removal drops
|
||
source + mount next sync; F2 change detection forces the recreate.
|
||
- Renderers unchanged: `RenderFileBrowserCompose` already takes fully-formed mount lines
|
||
(propagation rides in the line), `RenderFileBrowserConfig` already names sources by `Label`.
|
||
|
||
## 3. Tests + red-proofs
|
||
|
||
`filebrowser_network_test.go`: **A** (drive line byte-identical with/without the share — the B′
|
||
invariant; share ROOT `:rslave` line exact; both sources in config; skeleton called for the drive
|
||
ONLY), **B** (stub ⇒ absent from mounts + sources, drive intact, warn logged), **C** (autofs /
|
||
network / unknown / nil classifier all include — idle is not a detached drive), **D** (removal
|
||
leaves no trace; `fbNeedsRecreate` sees the change).
|
||
|
||
**Red-proof A** (network paths routed through the drive branch): FAIL —
|
||
`skeleton calls = [/mnt/felhom-drives/hdd_1 /mnt/felhom-drives/Felhom-Share]` — the NAS path
|
||
visibly receiving a skeleton — plus the share line degrading to a `userdata` subtree without
|
||
`:rslave`. Restored, green.
|
||
**Red-proof B** (stub gate dropped): FAIL — the stub share leaked into mounts AND the source
|
||
list, and the warn disappeared. Restored, green.
|
||
|
||
Green gate (`go build ./... && go vet ./... && go test ./...`): clean. No template changes → no
|
||
template gates in play.
|
||
|
||
## 4. Live leg (real surfaces; methods named)
|
||
|
||
**demo-hp (the mounting side):**
|
||
- Startup sync recreated FileBrowser: compose carries
|
||
`/mnt/felhom-drives/Felhom-Share:/srv/Felhom-Share:rslave`, config carries
|
||
`path: "/srv/Felhom-Share"` named „Hálózati tárhely: Felhom-Share"; log:
|
||
`FileBrowser mounts synced (recreated) — 2 storage path(s)`; container healthy.
|
||
- **Content:** `docker exec filebrowser ls /srv/Felhom-Share` → demo-felhom's real content
|
||
(`teszt/` + dotfiles), uid/gid 1000, group-writable.
|
||
- **Upload round-trip:** wrote `r67-test.txt` as **uid 1000 inside the filebrowser container**
|
||
(the exact mount namespace + process identity FileBrowser uses) → the file appeared on
|
||
demo-felhom at `/mnt/felhom-drives/hdd_1/shares/Share/r67-test.txt` (19 B, uid 1000) → deleted
|
||
from demo-hp → gone on demo-felhom. **Residual:** FileBrowser's HTTP/auth layer was NOT driven —
|
||
its admin credential is customer/day-0-held (admin/admin and the operator demo password both
|
||
401); the browser click-through is the operator's residual, as is a look at the sidebar.
|
||
- **Dead-NAS:** samba stack stopped on demo-felhom → demo-hp's in-container access returned a
|
||
clean `ls: /srv/Felhom-Share: Host is down` in ~11 s wall (incl. ssh) — an error, not a hang
|
||
(the soft/retry=0 recipe's promise held). `docker compose start` on samba → demo-hp listed the
|
||
share again immediately, **zero intervention** (the `:rslave` + automount recovery working as
|
||
designed).
|
||
|
||
**demo-felhom (drives-only, the regression side):** startup sync logged
|
||
`no config/compose change, ensured running without recreate (1 storage path(s))` — the render was
|
||
byte-identical, FileBrowser untouched (Up 22 hours), zero `:rslave` lines in its compose. The B′
|
||
invariant held in production.
|
||
|
||
**Screenshots by description:** demo-hp's FileBrowser sidebar now has a second entry „Hálózati
|
||
tárhely: Felhom-Share" beside the NVMe drive source; entering it shows the `teszt` folder from
|
||
demo-felhom. During the dead-NAS minute the source errors instead of spinning.
|
||
|
||
**Access notes:** demo-hp via the hub-vaulted G1 break-glass again (file→file, shredded after,
|
||
incl. the hub-DB copy). Probe + all execs left nothing behind (`r67-test.txt` deleted; alpine
|
||
image remains in the guest's docker cache — inert).
|
||
|
||
## 5. Follow-ups
|
||
|
||
- Operator: FileBrowser click-through on demo-hp (sidebar + upload via the UI) — CC lacks the
|
||
FileBrowser admin credential by design.
|
||
- ROADMAP: **R-67 → SHIPPED**, coupled to R-64 (browsing was its missing UX half);
|
||
`network-storage-nas.md` gained the „Browsing (FileBrowser)" paragraph.
|