controller v0.92.0: NAS network storage Part A2 (registry + UI + per-share health)

Controller-side of NAS network storage, proxying to agent A1 /netstorage/*. Distinct
'network' storage kind (no drive lifecycle); add/list/remove + per-share health UI;
unreachable NAS is a recoverable warning, never the drive missing/stop cascade; SMB
creds pass through to the agent, never persisted.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HxLA1mZurFq9kt8hneFeCs
This commit is contained in:
2026-06-30 14:40:47 +02:00
parent 9391f4ea71
commit 364dc50794
13 changed files with 837 additions and 1 deletions
+26
View File
@@ -1,5 +1,31 @@
## Changelog
### v0.92.0 — NAS network storage Part A2: registry + UI + per-share health (2026-06-30)
The controller side of NAS network storage, proxying to the validated agent foundation (felhom-agent
v0.50.0 `/netstorage/*`). An operator can add a customer's NAS share and point a media app at it — all via
the UI. A NAS is a **distinct storage kind** (NOT a drive): no enroll/eject/decommission/migrate/wipe/SMART.
- **`internal/agentapi/client.go`:** `AddNetStorage`/`ListNetStorage`/`RemoveNetStorage` + the
`NetworkMountStatus` mirror (health `ok|idle|unreachable`; `idle` is benign, only `unreachable` degraded).
The SMB credential is passed STRAIGHT THROUGH to the agent (which writes the 0600 file) — **never persisted
by the controller**.
- **`internal/settings/settings.go`:** `StoragePath.Kind` discriminator (`""`/`drive` | `network`) + the
network descriptors (Protocol/Server/Export/MappedUID/MappedGID — **no password**); `IsNetwork()` +
`IsNetworkStoragePath()`; `NetworkMountRoot`.
- **`internal/web/netstorage_handlers.go` (NEW):** `POST /api/storage/netstorage/{add,remove}` +
`GET /api/storage/netstorage`; registers/deregisters a Kind=network `StoragePath`; merges the agent's live
per-share health for the UI.
- **Kind-gating (the safety centerpiece):** `refuseNetworkLifecycle` blocks the drive ops
(eject/decommission/migrate/wipe) on a network path server-side; the drive-absent **gate**
(`planDriveGates`) and the **missing-storage** surface now SKIP network paths — so an `unreachable` NAS is a
**recoverable warning**, never the drive "missing → stopped" cascade (Scenario C). `networkStorageWarnings`
drives a distinct "Hálózati tárhely nem elérhető" app-card badge.
- **UI (`settings.html`):** a "Hálózati tárhely (NAS)" section — add form (NFS/SMB, server/export, uid,
SMB creds), per-share health badges, remove. Network shares are auto-selectable as a media app's `HDD_PATH`
(they register Schedulable). Hungarian, minimal emoji.
- Tests: agentapi round-trip (creds forwarded, health states); registry Kind-gate **companion** (drive
lifecycle refuses a network path; a drive path is not gate-refused); `unreachable``missing` **companion**
(the drive gate stops an absent drive but NOT a network path). `go build/vet/test ./...` green.
### v0.91.0 — F2: alert on born/persistent-down channel (not only transitions) (2026-06-29)
- **What:** closes F2 from the full-stack testrun — a channel failure present at **startup/reseed**
(e.g. the controller boots right after a leaf regen → first observation is `pin_mismatch`) was