docs: v0.31.0 CHANGELOG + REPORT + CLAUDE banner (F9/F20-BUG2/BUG3 live-validated)
This commit is contained in:
+29
-1
@@ -3,7 +3,35 @@
|
||||
All notable changes to **felhom-agent** are recorded here. Update on every code
|
||||
change that gets pushed.
|
||||
|
||||
## v0.30.0 — AGENT-001: anti-retarget re-resolution for inline customer-confirmed wipe (2026-06-13)
|
||||
## v0.31.0 — live-drive F9 + F20-BUG2 + F20-BUG3 (disk bind/wipe) (2026-06-14)
|
||||
|
||||
The last live-drive findings, all disk/`localapi`-side, implemented + deployed on `felhom-pve` and
|
||||
validated live on guest 9201 (approach: attach-to-existing, no re-provision — see the audit fixspec).
|
||||
|
||||
- **F9 — guest data-drive bind survives a re-provision** (`4cd1d02`). The in-guest bind (`pct set -mpN`)
|
||||
is config state a destroy+re-provision drops, and nothing restored it → a re-provisioned guest came up
|
||||
with its enrolled HDD unattached. New `GuestBindStore` (durable-id-keyed, per guest, recorded at
|
||||
guest-attach) + `ReassertGuestBinds` on agent startup re-adds any bind a guest is missing — only when
|
||||
the durable-id still resolves to a present drive (a swapped/absent disk is never auto-bound), idempotent.
|
||||
Plus `DiskInfo.GuestAttached` — the missing "bound into THIS guest" signal (vs mere host presence;
|
||||
resolves the F2 `hdd_configured` disagreement). **Live-proven:** dropped the bind, restarted the agent
|
||||
(real trigger) → re-attached with no manual call; reboot activated it; an HDD app then deployed onto
|
||||
the drive with data on `/dev/sdb1`.
|
||||
- **F20-BUG2 — one wipe durable-id scheme** (`a2a76e7`). `/disks` advertised only `durable_id` (`uuid:`,
|
||||
used for assign), but the wipe gate resolves `byid:`/`byuuid:` → confirming a wipe with the advertised
|
||||
id was a `binding_mismatch`. New `DiskInfo.WipeDurableID` via a shared `s.deviceDurableID` seam used by
|
||||
BOTH the list and the gate, so the id the customer copies is the id the gate accepts. **Live-proven:** a
|
||||
confirmed wipe using `/api/disks`'s `wipe_durable_id` is accepted (no mismatch).
|
||||
- **F20-BUG3 — format runs detached; survives a request deadline AND an agent restart** (`4777f8a`). mkfs
|
||||
ran under the HTTP request context, so a client deadline SIGKILLed it mid-write → corrupt disk. Now mkfs
|
||||
runs off `s.baseCtx` via a persisted `formatJob` record; the handler still returns the synchronous
|
||||
result (backward-compatible) but a dropped request no longer kills it. New `GET /disks/format/status`;
|
||||
`RecoverFormatJob` on startup re-runs an interrupted durable-id-bound format (re-resolved; anti-retarget
|
||||
— a blank/path-bound or unresolvable job is not auto-re-run). **Live-proven on the 916 GB felhom-usb:** a
|
||||
2 s client timeout left a ~30 s mkfs running to a clean ext4 (the live-drive corruption is gone); an
|
||||
agent restart mid-format was recovered + completed to a clean fs.
|
||||
|
||||
|
||||
|
||||
**Security fix (from the 2026-06-13 deep-sweep audit).** The inline customer-confirmed wipe in
|
||||
`internal/localapi/disks.go` `handleDiskFormat` inspected and gate-bound the device by its durable id
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
- **Pure Go stdlib + `golang.org/x/crypto` only** — no web frameworks.
|
||||
- `go.mod` directive **go 1.25.0**; dep `golang.org/x/crypto v0.52.0` (declares go 1.25, will NOT build on Go 1.24). The **build server (192.168.0.180) runs go1.26.0** (upstream Go on PATH, backward-compatible). Build/run the agent there for live tests (same LAN as the demo host).
|
||||
- Version: `version` var in `cmd/felhom-agent/main.go`, overridable via `-ldflags "-X main.version=<v>"`; `--version` flag. Bump on meaningful changes + add a CHANGELOG entry. **See `CHANGELOG.md` (top only) for the authoritative current state.**
|
||||
- **Current: v0.30.0** (2026-06-13; deployed on demo host `felhom-pve`). Recent state (summarized from the CHANGELOG top — verify there, not here):
|
||||
- **Current: v0.31.0** (2026-06-14; deployed on demo host `felhom-pve`). Recent state (summarized from the CHANGELOG top — verify there, not here):
|
||||
- **v0.31.0** — live-drive disk fixes: **F9** guest data-drive bind survives a re-provision (`GuestBindStore` + `ReassertGuestBinds` on startup, durable-id-matched; `DiskInfo.GuestAttached` reporting); **F20-BUG2** one wipe durable-id scheme (`DiskInfo.WipeDurableID` via a shared `deviceDurableID` seam used by list + gate); **F20-BUG3** mkfs runs detached off `baseCtx` with a persisted `formatJob` + `GET /disks/format/status` + `RecoverFormatJob` startup recovery (survives a request deadline + an agent restart). Live-validated on 9201 + the 916 GB felhom-usb.
|
||||
- **v0.30.0** — **AGENT-001** security fix: the inline customer-confirmed wipe (`localapi handleDiskFormat`) re-resolves the confirmed durable id → current device, re-derives+matches, re-inspects, and formats the **re-resolved** device (never the mutable `req.Device`), closing a classify→mkfs TOCTOU. At parity with `signedjobs.WipeExecutor` (`internal/localapi/wipe_reresolve.go`).
|
||||
- **v0.29.x** — OS / Docker-data storage **split** (golden bakes split rootfs + Docker-data volume; provision) + `lanresolver` split-horizon DNS fix (RESTART, not reload, dnsmasq on a guest IP change).
|
||||
- **v0.28.0** — backup re-target to offsite **`felhom-pbs`** (DR) + operator-signed **decommission**.
|
||||
|
||||
@@ -1,45 +1,40 @@
|
||||
# REPORT — AGENT-001 anti-retarget wipe fix: merge + deploy (v0.30.0)
|
||||
# REPORT — live-drive disk fixes F9 + F20-BUG2 + F20-BUG3 → agent v0.31.0 (2026-06-14)
|
||||
|
||||
**Date:** 2026-06-13 (supervised deploy session)
|
||||
**Outcome:** SUCCESS — merged to `main`, built v0.30.0, deployed to the demo Proxmox host, verified non-destructively. No rollback.
|
||||
The last live-drive findings, all agent disk/`localapi`-side. Implemented trunk-based on `main`, each
|
||||
its own commit + regression tests; built v0.31.0, deployed to `felhom-pve`, and **validated live** on
|
||||
guest 9201 + the 916 GB felhom-usb. Supervised, phased; every phase verified. Controller side reflected
|
||||
in v0.63.0 (two passthrough fields).
|
||||
|
||||
## What shipped
|
||||
## Phase 0 (gate) — approach
|
||||
- 9201 ran controller **v0.62.0** (not v0.61.0 — stale premise); no golden template exists; `build-golden.sh`
|
||||
bakes controller **0.43.0** by default ⇒ a re-provision would REGRESS the controller + lose apps.
|
||||
- F9's bind is **agent runtime** `pct set -mpN` (`guestbind.go AttachBind`), NOT golden-baked ⇒ **no golden
|
||||
rebake needed**. Operator-confirmed approach: **attach-to-existing 9201** (no re-provision), with the
|
||||
bind auto-re-assert validated via a **real trigger** (agent restart), not unit-tests + a manual call.
|
||||
|
||||
`v0.30.0` — AGENT-001 fix (from the 2026-06-13 deep-sweep audit). The inline customer-confirmed wipe in
|
||||
`internal/localapi/disks.go` `handleDiskFormat` previously ran `mkfs` on the caller-supplied mutable
|
||||
`/dev` path (`req.Device`); a USB re-enumeration between inspection and `mkfs` could wipe the wrong
|
||||
disk (classify→mkfs TOCTOU). Now it re-resolves the confirmed durable id → current device, requires a
|
||||
re-derived exact match, re-inspects data-bearing, and formats the **re-resolved** device — at parity
|
||||
with `signedjobs.WipeExecutor`. New `internal/localapi/wipe_reresolve.go` (`antiRetargetResolve`,
|
||||
injectable `reresolveWipe` seam); refusals → `409`, no `mkfs`. Merged from
|
||||
`fix/agent-001-wipe-durable-reresolve`.
|
||||
## Fixes (each a commit + tests)
|
||||
| Finding | Commit | What | Live proof |
|
||||
|---|---|---|---|
|
||||
| **F9** | `4cd1d02` (+`a2a76e7` reporting) | `GuestBindStore` + `ReassertGuestBinds` (startup, durable-id-matched) restore a bind a re-provision dropped; `GuestAttached` reporting | dropped bind → agent restart auto-re-attached (no manual call) → reboot activated → HDD app deployed, data on `/dev/sdb1`; `guest_attached=True` end-to-end |
|
||||
| **F20-BUG2** | `a2a76e7` | shared `deviceDurableID` seam → `/disks` `WipeDurableID` is the gate's scheme; one id for the wipe-confirm | confirmed wipe with `/api/disks`'s `wipe_durable_id` accepted (no `binding_mismatch`) |
|
||||
| **F20-BUG3** | `4777f8a` | mkfs detached off `baseCtx` + persisted `formatJob` + `GET /disks/format/status` + `RecoverFormatJob` startup recovery (anti-retarget) | 2 s client timeout left a ~30 s mkfs running to a clean ext4; agent restart mid-format recovered + completed clean — **the live-drive corruption is gone** |
|
||||
|
||||
## Deploy
|
||||
## Verification (per phase)
|
||||
- **P1**: `go build/vet/test ./...` green on the build server incl. new tests (BUG2 scheme + gate-binding; F9 re-assert restores/skips-absent/skips-bound + store persist; BUG3 detached-persist + status + recover re-runs/skips-blank/skips-unresolvable).
|
||||
- **P2**: agent v0.31.0 active; local API reachable; 9201 controller online; **AGENT-001 refusals still hold** (no-confirmation → 409; wrong durable_id → 409).
|
||||
- **P3 / P4**: as above — all live-proven on 9201 + felhom-usb.
|
||||
|
||||
- Build server (192.168.0.180, go1.26.0): green gate on the branch (`build`/`vet`/`test` clean; `TestAntiRetargetResolve` incl. `retarget-mismatch-refused` PASS). Built `v0.30.0` (ldflags `-X main.version=0.30.0`).
|
||||
- Demo Proxmox host (`felhom-pve`): binary verified by sha256 at each hop; backed up the running binary to `/usr/local/bin/felhom-agent.bak-0.29.1`; installed the new binary; `systemctl restart felhom-agent.service`.
|
||||
- **Rollback target was v0.29.1** (not needed).
|
||||
- Pin check before the live run: served PVE leaf-cert SHA-256 prefix matches the pinned value (`BA:7C:99:7D:45:D0…`).
|
||||
- Scope: **agent-only**. No golden rebake, no re-provision, no controller change. Guest 9201 controller stayed online through a brief local-API blip.
|
||||
## Backward-compat + safety notes
|
||||
- BUG3 keeps the **synchronous** format response (the live v0.62.0 controller's `agentapi.FormatDisk` is sync) — the detached mkfs just can't be killed by a dropped request; `/disks/format/status` is additive.
|
||||
- The controller has **no automated SSD↔HDD data-migration feature** (removed in de-privileging) — F9 unblocks HDD-app deployment + data-on-HDD, which is proven; an app-data mover would be a separate feature.
|
||||
- The `GuestBindStore`/intent durable-id is the storage-view `uuid:` (fs UUID), which CHANGES on a reformat — so after a wipe a drive must be re-enrolled (auto-re-assert won't match a new fs identity). Correct for the F9 re-provision case (drive unchanged); noted for the reformat edge.
|
||||
|
||||
## Verification (non-destructive)
|
||||
## Rollback
|
||||
Baseline preserved at `/usr/local/bin/felhom-agent.bak-0.30.0`. Not needed — all verifications passed.
|
||||
|
||||
| Check | Result |
|
||||
|---|---|
|
||||
| Service active + version | `felhom-agent.service` active/running; `--version` = **0.30.0**; clean startup logs (local-api on :8443, hub desired gen=2, PBS verify ok) |
|
||||
| Local API reachable (full chain) | controller→agent `/disks` proxy returns the disk list (vmid 9201); guest 9201 controller healthy on the hub |
|
||||
| Regression A — data-bearing device, NO confirmation | **REFUSED** (HTTP 409, `needs_confirmation:true`, `formatted:false`); no `mkfs` |
|
||||
| Regression B — confirmed + WRONG durable_id | **REFUSED** (HTTP 409, `formatted:false`); a non-matching confirmation did not authorize a wipe; no `mkfs` |
|
||||
| Data safety | `felhom-usb` (`/dev/sdb1`, RomM data) still mounted, `used_bytes` unchanged — untouched |
|
||||
| Blank-device benign-format | SKIPPED — no disposable blank device available on the host |
|
||||
| Happy-path destructive wipe | SKIPPED — HUMAN-only (never wipe real data to verify); covered by the unit test |
|
||||
## Follow-up queued (separate task — NOT folded into this)
|
||||
**Bump the golden's default controller tag (`build-golden.sh:43`, currently `:0.43.0`, 20 versions stale) to current + validate the full destroy→provision→first-boot path.** That path *does* warrant a supervised destroy+re-provision (it's the real customer-onboarding flow).
|
||||
|
||||
Note: `reresolveWipe`'s post-gate refusal runs only when the gate ALLOWS (a correctly-matching
|
||||
confirmation), which on a real device leads to the destructive success path — so it is not safely
|
||||
live-triggerable and is verified by the unit test `retarget-mismatch-refused`. Live, both the
|
||||
no-confirmation (A) and wrong-confirmation (B) gate refusals confirm no un-gated destructive path.
|
||||
|
||||
## Branch / commits
|
||||
|
||||
- `main` merge + version bump pushed. Branch `fix/agent-001-wipe-durable-reresolve` left in place (per the runbook) until the operator confirms.
|
||||
- felhom.eu audit record (`documentation/audits/deep-sweep-2026-06-13.md`) updated: AGENT-001 → MERGED + DEPLOYED (agent v0.30.0).
|
||||
## Final demo state
|
||||
Agent v0.31.0; controller v0.63.0; 9201 healthy. felhom-usb wiped clean by the BUG3 test, then re-enrolled
|
||||
+ re-attached (`guest_attached=True`); romm redeployed onto the HDD (`/dev/sdb1`); all apps healthy.
|
||||
|
||||
Reference in New Issue
Block a user