diff --git a/REPORT.md b/REPORT.md index 4e11d00..a57b193 100644 --- a/REPORT.md +++ b/REPORT.md @@ -1,32 +1,30 @@ -# REPORT — Impl-1: format-safety foundation (agent v0.54.0) +# REPORT — Impl-2a: raw-device discovery + registry-sourced drive tracking (agent v0.55.0) -**Date:** 2026-07-01 · **Repo:** `felhom-agent` · **Class:** Risky/supervised (destructive `Format`/mkfs -path + sudoers). Built/tested/deployed normally; live validation proves the guard REFUSES dangerous -targets (safe) and formats only the throwaway `/dev/sdd`. +**Date:** 2026-07-01 · **Repo:** `felhom-agent` (backend only) · **Class:** Risky/supervised (re-sources +the drive health/detach path — a regression re-creates the 3b-fix false-detach class). No mkfs here. -Impl-1 of the drive-enrollment series (SPIKE-drive-enrollment-2026-07-01), built BEFORE the enrollment -feature to harden the existing format path. +Impl-2a of the drive-enrollment series (SPIKE-drive-enrollment §SQ1/SQ4/SQ5). Makes raw (non-PVE-storage) +drives discoverable for enrollment AND health-tracked without being a PVE storage. The controller wizard +(Impl-2b) + the shared-box operator gate (Impl-3) are separate. ## 1. Baseline → target -felhom-agent `7639ab5` (v0.53.0) → **v0.54.0**. +felhom-agent `5209830` (v0.54.0) → **v0.55.0**. -## 2. Root cause + files changed +## 2. Files changed + commit -`Format` delegated authorization to its caller and only checked `DataBearing` (has-data) — insufficient: -the OS disk is data-bearing yet catastrophic; and the sudoers permitted `mkfs.* /dev/*`. - -- `internal/storage/claim.go` (new) — the **unclaimed-disk filter**: `classifyClaim` (pure) + - `gatherClaimFacts` (SystemDisks + lsblk member-FSTYPE + foreign-mount + RO + authoritative pvs/zpool; - fail-safe → CLAIMED). -- `internal/storage/hostops.go` — mandatory guard at the top of `Format` (refuse unless unclaimed); - `Binaries.MkfsGuarded`; `Format` now execs the guarded wrapper; `SudoHostOps` gains a `HostReader`. -- `configs/felhom-mkfs-guarded.sh` (new) — the root wrapper (below-the-agent gate). -- `configs/felhom-agent.sudoers` — `FELHOM_FORMAT` drops raw `mkfs.*`, allowlists only the wrapper; - read-only `pvs`/`zpool status -P` added to `FELHOM_DISK`. -- `internal/storage/claim_test.go` (new), `hostops_disk_test.go` (updated), `internal/capability/manifest.go` - (mkfs sample → wrapper). `CHANGELOG.md`, `README.md`, version. -- Commit **`5209830`** on `main` (parent `05f22a9`). +- `internal/storage/candidates.go` (new) — `ListCandidateDisks` (enumerate `/sys/block` whole-disks → + Impl-1 `deviceUnclaimed` → probe: size/model/FS/data-bearing/durable-id; mountable ext4/xfs split). +- `internal/storage/registry_known.go` (new) — `RegistryKnownTargets` (watchdog `Known()` from intent + registry + Felhom `.mount` units) + `ReconcileExistingDrives` (idempotent migration). +- `internal/localapi/disks.go` — `GET /disks/candidates` handler + `DiskOps.ListCandidateDisks`; + `handleDisks` additive+deduped registry-drive union. `internal/localapi/server.go` — route + + `DriveTargets` option. +- `cmd/felhom-agent/main.go` — swap watchdog `KnownTargets` source → `RegistryKnownTargets`; run the + migration at start; thread `driveKnown` into the local-API. +- Tests: `internal/storage/registry_known_test.go`, `internal/localapi/disks_candidates_test.go`, + `disks_test.go` (fake). `CHANGELOG.md`, `README.md`, version. +- Commit **`91f6a26`** on `main` (parent `066e3bf`). Watchdog/HostLiveness/Remounter unchanged. ## 3. Green gate @@ -34,49 +32,39 @@ the OS disk is data-bearing yet catastrophic; and the sudoers permitted `mkfs.* ## 4. Tests + red-proof -- `TestClassifyClaim` (pure, table-driven): every claim signal (system/LVM-PV/ZFS/mdraid/member-FSTYPE/ - foreign-mount/RO/undeterminable) → CLAIMED; fail-safe (gather error) → CLAIMED; clean disk + our-own- - drive-re-init → unclaimed. **Red-proof:** the "system/OS disk (non-data-bearing)" case — removing the - `isSystem` check flips it to `unclaimed=true` → the test FAILS (proves the guard adds safety beyond - `DataBearing`, which would call a blank OS disk benign). Verified fail, reverted. -- `TestFormatGuard_{RefusesSystemDisk,RefusesLVMMember,AllowsUnclaimed}`: the guard is wired into - `Format` — refuses claimed devices (no mkfs), allows unclaimed (invokes the wrapper). -- `TestParseLsblkNodes`; capability manifest coverage test updated + green. +- `TestRegistryKnownTargets`: enrolled drive tracked; `new`-intent excluded; ejected still tracked; + non-Felhom unit ignored; KnownTarget fields correct (DurableID `uuid:`, MountBacked, MountPath). +- **`TestRegistryVsObserve_RedProof`:** with NO PVE storage, the OLD `Observer.Known()` returns empty + (drive invisible — the 3b-fix class) while `RegistryKnownTargets.Known()` tracks it. Proves the + decoupling adds the coverage. +- `TestReconcileExistingDrives_Idempotent`: a mounted Felhom-unit drive → enrolled; re-run no-op; an + UNMOUNTED unit is NOT auto-migrated. +- `TestDiskCandidates_Split`: initialize = all unclaimed; attach = mountable-FS subset. (Candidate + fail-safe carries through Impl-1's `classifyClaim`, unit-tested there.) -## 5. Deploy + live validation (felhom-pve — SAFE, only /dev/sdd formatted) +## 5. Deploy + live validation (felhom-pve, agent v0.55.0) -Published Gitea `felhom-agent/0.54.0` (sha `0500dd9bd15b03e67e8c93c0ac7aa8008d7413c335e6f852aeaf074d3b364eda`). -Deployed to felhom-pve in order: install wrapper (0755) → swap sudoers (visudo-validated) → agent binary -(backup `.bak-0.53.0`) → restart. (Note: my manual scp of the Windows working-copy sudoers had CRLF and -failed `visudo`; fixed live by CR-stripping + re-install. The committed repo file is LF and the -host-install `fetch_raw`-from-Gitea path is unaffected.) +Published Gitea `felhom-agent/0.55.0` (sha `b8122511460413489ae0f5323ad34ebeaa00cfd091e91cba06f00398128bb577`); +deployed (backup `.bak-0.54.0`, restart). All four scenarios proven: -| Proof | Result | -|-------|--------| -| **P-1** wrapper on `/dev/sda` (OS) via agent sudo path | ✅ REFUSED "system/OS disk (/dev/sda backs /boot/efi)", exit 1, no mkfs | -| **P-2** wrapper on `/dev/sda3` (the LVM PV) | ✅ REFUSED (system), no mkfs | -| **P-3** raw `sudo mkfs.ext4 -F /dev/sda` as felhom-agent | ✅ DENIED ("a password is required" — not allowlisted; Part B) | -| **P-4** wrapper on `/dev/sdd` (unclaimed throwaway) | ✅ ACCEPTED → mkfs.ext4 → `blkid /dev/sdd` = `TYPE="ext4"` | -| agent-guard live reads (as felhom-agent) | ✅ `sudo pvs` / `sudo lsblk ` / `sudo zpool status -P` all OK → the guard can gather on the live host | -| post-deploy health | ✅ agent 0.54.0 active; `--selftest=read` = 5 storages (3b-fix intact); 9201 running | +| Scenario | Result | +|----------|--------| +| **C — no false detach** | ✅ post-deploy: 5 storages (Observe intact), both drives mounted, both intents `enrolled`, no detach/error in the journal, 9201 running. Migration a no-op (drives already enrolled). | +| **B — registry-sourced tracking (definitive)** | ✅ controlled `systemctl stop mnt-felhom\x2dflash.mount` (app-less drive) → watchdog **detected** `felhom-flash attached→disconnected` (`durable_id=uuid:81a26531…` = the registry key) → **self-healed** (`re-mounting enrolled drive` → gate benign → `ensured mount` → remounted in 5s). Proves the Known set is registry+units-sourced, NOT silently empty. | +| **A — discovery** | ✅ `GET /disks/candidates` (real local-API endpoint) returns **only `/dev/sdd`** (size 63.8G, model, fstype ext4, mountable, `durable_id uuid:950d8ad0…`) under both `initialize` and `attach`; **sda/sdb/sdc excluded**. | +| **D — fail-safe** | ✅ carried through the reused Impl-1 filter (`classifyClaim` exclude-on-error; unit-tested). | -**Part A (agent guard)** is unit-tested (logic, incl. red-proof) and its live host reads are confirmed -working as the agent; **Part B (wrapper)** is live-proven on real devices (sda/sda3 refused, sdd -accepted) + raw-mkfs denied. +## 6. NOT yet done -## 6. NOT exercised / deferred - -- The `/disks/format` local-API endpoint was not invoked live (needs a guest local-API token). The - guard it calls is identical to the unit-tested code, and its live reads + the wrapper below it are - proven on real devices — so the stack is validated, just not through the HTTP entrypoint. -- **Impl-3 residual:** a raw disk passed through to ANOTHER VM looks unused to the host — a host-level - filter can't detect it; the shared-box operator gate (Impl-3) closes that. Impl-1 closes everything - host-visible (a strict improvement over the prior no-guard state). +- **Impl-2b:** the controller wizard rewiring (consume `GET /disks/candidates`) + the enroll + orchestration UI + a live end-to-end raw enrollment (format via the Impl-1 guarded path → mount → + bind → intent). **Impl-3:** the shared-box operator gate for self-serve format. +- The controller settings storage-section screenshot for Scenario C (agent-side evidence above is + definitive; the UI still shows the drives via Observe dir-storages + the registry union). ## 7. Observations -- The guard's mount rule ALLOWS Felhom's own drives (mounted under `/mnt/felhom-drives`) for re-init; - their DATA is protected by the separate DataBearing wipe-confirm, not this guard. So the spec's "refuse - sdb/sdc" was not applicable to the guard (they're re-initable Felhom drives) — I did not mkfs them - (customer data), per the STOP. -- The throwaway `/dev/sdd` is now ext4 (was ntfs); unmounted, not enrolled. Safe to unplug. +- The existing drives are still PVE dir-storages (not removed — later cleanup, per the spike). They now + ALSO track via the registry; `/disks` dedups by mount path (no doubles). Retiring the dir-storages is + optional and out of scope here. +- `/dev/sdd` (the Impl-1 throwaway, now ext4) is a live discovery candidate; unmounted, not enrolled.