Report-only spike + one live throwaway FORMAT experiment on /dev/sdc. No production code, no version bump. - Q1: raw-disk discovery gap confirmed — Observe AND localapi GET /disks are both built only from PVE storages (ListStorage/NodeStorage); a not-yet-storage disk is invisible. proxmox client has no /disks/* method. - Q3: safety classifier (RoleForRawDevice/SystemDisks/RoleForStorage) already protects sda/felhom-pbs live, but needs two hardening checks: whole-disk-granularity enrolled-subtraction + LVM-PV membership. - Q6: FORMAT path proven end-to-end live — pvesh disks/directory create -> agent Observe -> controller "Regisztralas" enroll. sdc left as the clean enrolled felhom-flash B3 migration target. sda/sdb untouched. - Premise correction: felhom-pbs is remote (no local PBS disk). - Fork list for the build spec in the audit doc + REPORT.md.
6.0 KiB
felhom.eu — task reports
Overwrite this file with a summary of the most recent task only (uniform with the other repos; not cumulative). The cumulative hub history lives in hub/CHANGELOG.md.
REPORT — SPIKE: self-serve raw-drive plug-and-enroll (GATE) (2026-06-15)
Type
GATE spike — report-only + one live throwaway experiment on /dev/sdc. No production code, no version bump, no service restart. Build spec is gated on the fork picks below.
Full doc: documentation/audits/SPIKE-raw-drive-enroll-2026-06-15.md.
One-screen summary
The plug-and-enroll flow is mostly assemblable from primitives that already exist. The genuine net-new is (a) a raw-disk discovery source — the agent's Observe and localapi GET /disks are both built only from PVE storages (ListStorage/NodeStorage), so a disk that isn't already a PVE storage is invisible — and (b) a create-storage call (the agent creates no PVE storages today). The safety classifier already exists (RoleForRawDevice, SystemDisks, RoleForStorage in internal/storage/role.go) and correctly protects the live system disk, but needs two hardening additions before it can drive a discovery list.
Validated live on felhom-pve:
- sda (ESP + sole LVM PV
pveVG holding root, local-lvm, guest 9201's disks) → PROTECTED (system); pinned via/boot/efi=sda2. - sdb (
felhom-usb, ext4/mnt/felhom-usb) → PROTECTED (enrolled data). - sdc (removable junk iso9660+EFI+hfsplus) → OFFERABLE.
- Premise correction:
felhom-pbsis a remotepbs:storage (server 192.168.0.180) — no local PBS disk exists; it's protected at the storage tier, not a raw-disk tier.
Live FORMAT experiment (serial-fenced to sdc): pvesh create …/disks/directory --name felhom-flash --device /dev/sdc --filesystem ext4 --add_storage 1 did partition+mkfs+mount+register in one call (fs-UUID mount unit, is_mountpoint 1, /mnt/pve/felhom-flash). It immediately appeared in the controller's agent-drive view as user-data / data-bearing / "Nem regisztrált" with a one-click Regisztrálás — no agent restart. sdc left as the clean enrolled felhom-flash B3 migration target.
The two safety-gate hardening gaps (must close)
- Whole-disk-granularity enrolled-subtraction —
deviceRolematches a registered storage by exactBackingDevice==device; a discovery flow asks about whole disk/dev/sdbwhilefelhom-usbbacks/dev/sdb1, so the match fails and sdb would be offered for format. Compare at whole-disk granularity. - LVM-PV / guest-image membership —
SystemDisksresolves the system disk from the mount table only; a non-boot disk holdinglocal-lvmPVs / guest images would be missed. Add adisks/lvmPV-membership check.
Fork list (operator decides)
- Discovery source:
disks/list+disks/lvm+mount-table+Observe (recommended) ±lsblkfor display. - Safety signals: mount-table-only (ships, but can offer a non-boot LVM disk) vs hardened (the 2 checks above — recommended).
- Mount path: PVE default
/mnt/pve/<name>vs/mnt/<name>viaEnsureMount+pvesm add dir(recommended, matches felhom-usb). - Format-vs-enroll-as-is: single supported-fs partition → as-is; else format; format on ambiguity.
- Endpoints:
GET /disks/raw+POST /disks/init+POST /disks/attach-existing,withGuest-scoped, agent re-derives the gate. - v1 scope: format-only first (proven live) + enroll-as-is fast-follow (recommended) vs both in v1.
- Normalize
content(vs PVE all-types default); typed proxmox-client create method (vs host-sidepvesh).
REPORT — Slice 10D (hub half): DR capstone — recovery mode + re-enroll + directive serving (hub v0.11.0) (2026-06-10)
Type
TASK (CC-implemented). The hub half of the slice-10 DR capstone (closes slice 10). Pairs with
felhom-agent v0.18.0 (identity escrow + restore-mode consumption).
What changed (hub)
The hub ORCHESTRATES recovery but holds no usable secret and no Cloudflare write-power — a
compromised hub can at most hand out opaque blobs (they need R, which the hub never has) + rotate
its own per-host credential. It cannot hijack a customer's tunnel (the destructive rotation is the
operator's job).
API
PUT/DELETE /admin/hosts/{id}/recovery-mode(global key) — arm/disable recovery mode with a bounded TTL (clamped [60s, 4h], default 30m → auto-expires). Directive + re-enroll are served ONLY while active.POST /hosts/{id}/re-enroll— gated ONLY on recovery mode (the lost box has no old key). Rotates the host's API key to the new box's key (old box revoked) + returns the directive + opaque blobs.GET /hosts/{id}/restore-directive(re-enrolled key, recovery-gated) — re-fetch.- The slice-7 escrow upload now also accepts the identity blob + non-secret directive (additive).
Store
hosts.recovery_mode_until;host_escrow.identity_blob+directive_json. Methods:SetRecoveryMode/ClearRecoveryMode,RotateHostAPIKey,SaveHostDRBundle/GetHostDRBundle.
Tests (green)
- re-enroll refused without recovery mode (403); recovery-arm is global-key-only; re-enroll rotates + revokes (old key→401, new key→200); directive served only in recovery mode + expires; clear disables re-enroll.
Docs
- Doc 03 §9 (10D done → SLICE 10 CLOSED) + the host-loss DR flow with the operator-side rotation model (hub orchestrates + read-only verifies; the operator deletes the stale connector + rotates the tunnel/PBS token from a trusted environment).
Deferred (non-blocking, per the locked model)
- The Config DR/Recovery web UI (functional today via the recovery-mode admin API) + a small operator rotation CLI. No Cloudflare write-credential is in the hub by design.
Pending
- Build + deploy hub v0.11.0 + agent v0.18.0; run the operator-in-the-loop DR drill (throwaway identity).