Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UuFPHmHNrCJj1VhY6QdDMU
6.2 KiB
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(:rslaveload-bearing — automount wake / idle-unmount propagate into the RUNNING container). NoEnsureUserdataSkeleton, 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 existingServer.classifyFSPathseam: 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 (
runNetAdddone) + remove (handleNetStorageRemove) now callSyncFileBrowserMounts()(same debounced/H5-mutexed path as the drive flows). Removal drops source + mount next sync; F2 change detection forces the recreate. - Renderers unchanged:
RenderFileBrowserComposealready takes fully-formed mount lines (propagation rides in the line),RenderFileBrowserConfigalready names sources byLabel.
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 carriespath: "/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.txtas 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 downin ~11 s wall (incl. ssh) — an error, not a hang (the soft/retry=0 recipe's promise held).docker compose starton 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.mdgained the „Browsing (FileBrowser)" paragraph.