15f7529a1c
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4.3 KiB
4.3 KiB
REPORT — felhom-agent v0.23.0: device-ROLE classification + tiered storage-wipe gate
What this implements
The agent half of the storage-authorization redesign (CC SPEC, Part A). Destructive storage wipes are now tiered by the device's protection ROLE, classified by the agent from its own inspection — never the controller's or hub's claim.
A1 — Device-ROLE classification (authoritative)
- New
internal/storage/role.go:DeviceRole=system|backup|user-data. SystemDisks(host)resolves the OS whole-disks (the disks backing/,/boot,/boot/efi) from the mount table + world-readable/devsymlinks (root-free; the root-CLI fence is untouched).RoleForStorage(typ, backingDevice, …)tiers a storage-view target;RoleForRawDevice(device, …)tiers a raw device (a fresh disk in the init flow, not yet a PVE storage).pbs→ backup;lvmthin/ builtinlocal/ nfs / cifs / unknown → system;usb/local-diron a non-system external device → user-data.- Fail-safe: any ambiguity → system (most-protected) — consistent with destructive-on-ambiguity.
GET /disks→ eachDiskInfonow carriesrole.
A2 — Gate tier (internal/reconcile)
- New
CustomerConfirmabledisposition +StorageRole{System,Backup,UserData}(string mirror ofstorage.DeviceRole, kept in reconcile to avoid a storage→reconcile import edge). Gate.AuthorizeStorageWipe(StorageWipeAuthz, signed):- user-data → allowed iff
ConfirmedAND the confirmation's durable id matches the device's agent-re-resolved durable id (ReasonCustomerConfirmed); unconfirmed →ReasonPendingConfirmation; mismatch / missing durable →ReasonBindingMismatch. Audited with the durable id. - system/backup/unknown → the standard operator-signature path (
pending_signature);Confirmedis ignored — refused by role.
- user-data → allowed iff
AuditRecord.DurableIDadded (the customer-visible "who/what/when + durable id").
A3 — Endpoint plumbing (internal/localapi)
POST /disks/formatacceptsconfirmed+durable_id. The data-bearing path:- resolves the device's authoritative role (
deviceRole— prefers a matching storage target's role so PBS is recognized as backup, else raw classification), re-resolves the durable id, and calls the tiered gate. - user-data confirmed →
mkfs(200); user-data unconfirmed/mismatch → 403 withneeds_confirmation- the durable id (no opsign); system/backup → 403 with the operator-signature pending op.
- blank devices stay benign
mkfs(unchanged).
- resolves the device's authoritative role (
StorageGateinterface widened:AuthorizeWipe(WipeRequest) WipeDecision. Adapter inmain.gobridges toGate.AuthorizeStorageWipe.- Eject stays benign (unmount, data preserved) — its customer confirmation is a controller-side
UI concern, not a gate tier (per the hard rule: confirmation walks the gate ONLY for user-data
storage_wipe).
Tests (non-hollow)
internal/storage/role_test.go— demo-storage mapping (local/local-lvm/felhom-pbsprotected,felhom-usbon/dev/sdb1user-data) + fail-safe when system disks are unknown.internal/reconcile/storage_wipe_test.go— aconfirmed:truewipe on system/backup is refused (falls to pending_signature, no exec); user-data durable-id mismatch refused; missing durable refused; unconfirmed → pending_confirmation; unknown role fails safe.internal/localapi/disks_test.go— format-handler branches: user-data confirmed → mkfs; user-data unconfirmed → 403 needs_confirmation with no operator-signature pending op; confirmed-but-protected → still refused (role beats confirmation); blank → mkfs without the gate.
go build ./... && go vet ./... && go test ./... all green (Go 1.26, local).
Version
v0.22.0 → v0.23.0 (version var in cmd/felhom-agent/main.go).
Notes / residuals
- The one accepted residual (per spec): a compromised controller could
mkfsa user-data drive it could alreadyrm -rf— same data loss, no new reach. System/backup remain operator-only. - LVM-root caveat: when
/is on device-mapper, the OS whole-disk is pinned via/boot//boot/efi(a raw ESP partition on the OS disk on standard Proxmox/Debian installs); the fail-safe default covers anything that cannot be resolved.