# REPORT — v0.43.0: rebuilt storage management (guided init/attach/eject on the agent disk model) **Repo:** `felhom-controller` · **Version:** 0.43.0 · **Date:** 2026-06-11 **Pushed commit:** `29a9dcd` · paired with `felhom-agent` v0.22.0 (`4734d4a`, exposes `durable_id`) + golden rebake. ## What shipped After the 8C de-privileging the storage UI's buttons pointed at deleted routes (all 404); only manual "add already-mounted path" survived. The agent already owns disk execution + the data-bearing signature gate, and the controller already had the `agentapi` client + `/api/disks/*` proxies + the `StoragePath` registry. This is a **controller-only UI/orchestration layer** over those — the controller holds **no destructive authority**. - **Storage overview** (`settings.html`, `GET /api/disks`): the agent's live disk view — name/type/state/ device/mount/class + the **`data_bearing` badge** + a "registered?" cross-reference. - **Guided init** (`/settings/storage/init` + `POST /api/storage/init`): format → resolve the new fs UUID from the re-listed disks → assign (mount) → register the `StoragePath`. **A data-bearing device is REFUSED** by the agent; the UI surfaces the exact `felhom-opsign …` command and **stops** — no force-format. - **Guided attach** (`/settings/storage/attach` + `POST /api/storage/attach`): non-destructive — resolve the existing fs UUID → assign → register. - **Eject** (`POST /api/storage/eject`): benign unmount + deregister, surfacing the agent's dependent-guest warning. - **`agentapi`**: `DiskInfo.DurableID` + `FSUUID()` (the assign key — strip `uuid:`); `FormatResult.PendingOp` + `OpsignCommand()`, now parsed from the agent's 403 body (the old client discarded it). - **Honest buttons**: init/attach wired; migrate (drive + per-stack, both places) disabled "Hamarosan" — **no 404s**. - **Phase 3 (de-priv template debt)**: removed the dead `CrossDrive*` blocks in `deploy.html` (the "2. mentés" form + 3 JS fns) and `backups.html` (run buttons + 2 JS fns) — they referenced fields the de-privileged handlers no longer provide. ## Security invariant — held, proven live The UI **never** bypasses the agent's data-bearing gate; there is **no force-format**. A refusal surfaces the `felhom-opsign` command only. Unit-tested (`runStorageInit` on a data-bearing refusal performs **zero** assign/register) **and** proven live on 9201's real `sdb`: `POST /api/storage/init {device:/dev/sdb1}` → **HTTP 409**, `refused:true`, `registered:false`, `opsign: felhom-opsign -op storage_wipe -host demo-felhom-01 -durable-id byid:wwn-0x5000039ddb108568-part1`. No format, no mount, no registration. ## Live validation (guest 9201, real 1TB USB `sdb` = `felhom-usb`) - `/api/disks` now carries `durable_id`; `felhom-usb` → `/dev/sdb1`, `data_bearing:true` ("device is mounted"), `durable_id:uuid:277a2179-…`. Overview badge maps correctly. - **Init on sdb (data-bearing) → 409 + opsign, gate held** (the spec's passing gate test — sdb holds data). - Pages render (no 404/500): `/settings`, `/settings/storage/init`, `/settings/storage/attach`, `/stacks//deploy` (deploy.html — CrossDrive removed), `/stacks`, `/monitoring`. No dead storage links. - Tests: refusal-surfaces-opsign-and-does-NOT-mount/register; success assigns with the resolved UUID + registers the expected `StoragePath`; UUID resolution; a **template-parse test** guards every page. ## Deferred / flagged (NOT in this slice) - **Phase 2 — migration (controller-side rsync):** intentionally its own slice (the migrate buttons are disabled "Hamarosan", not dead). The controller still has `/mnt:/mnt:rw`, so it can rsync app-data between mounts + update `app.yaml`'s `HDD_PATH` (stop→rsync→verify→start) — no agent endpoint needed. - **`/backups` still 500s on PRE-EXISTING restic debt (NOT this change, NOT CrossDrive).** The page references ~30 dead restic-tier fields (`.Backup.RepoStats`, `.SnapshotHistory`, `.ResticSchedule`, `.Retention`, `.LastBackup`, `.NextBackup`, `.LastCheckOK`, …) that 8C removed from the backend — the whole restic snapshot tier + repo stats + snapshot history + restic-password UI is dead. That's a **backups-page de-priv rebuild** (a design slice: what the page shows in the app-data-only model), well beyond the CrossDrive cleanup this spec scoped (the spec listed "backups.html (5)" = the CrossDrive refs, which I removed). `/backups` was already 500'ing before this task. **Recommend it as the next slice.** ## Notes - No agent disk-subsystem or gate changes; the only agent change is the read-only `durable_id` exposure (v0.22.0) the user approved (without it the de-privileged controller can't learn the fs UUID `assign` needs). Golden rebaked with controller 0.43.0 so fresh provisions get the rebuilt UI.