From 05f22a9ab429ecbe2912428558b749b65614b8e7 Mon Sep 17 00:00:00 2001 From: kisfenyo Date: Wed, 1 Jul 2026 13:01:29 +0200 Subject: [PATCH] docs: REPORT for agent v0.53.0 (pool-scoped restore; supervised swap validated) Co-Authored-By: Claude Opus 4.8 (1M context) --- REPORT.md | 138 ++++++++++++++++++++++++------------------------------ 1 file changed, 61 insertions(+), 77 deletions(-) diff --git a/REPORT.md b/REPORT.md index 6e8709b..9c63971 100644 --- a/REPORT.md +++ b/REPORT.md @@ -1,98 +1,82 @@ -# REPORT — operator-opt-in CPU/RAM cap for the provisioned guest (agent v0.52.0) +# REPORT — restore guests INTO the felhom pool (pool-scoped-ACL enabler, agent v0.53.0) -**Date:** 2026-07-01 · **Repo:** `felhom-agent` · **Class:** Implementation (additive CLI→spec plumbing) +**Date:** 2026-07-01 · **Repo:** `felhom-agent` · **Class:** Risky/supervised (agent code normal; the +live token re-scope was the supervised step — executed + validated on felhom-pve). -Colleague-safety batch #3 (agent half). New `-cores`/`-memory` flags let an operator cap the -provisioned guest's CPU cores + RAM **at provision time, before first boot**, so a trial appliance on a -colleague's SHARED production Proxmox doesn't pressure his existing guests. Pure CLI→spec wiring — the -reconcile engine already applied the cap. +Colleague-safety batch #4 phase b (agent half). Makes every restore allocate the guest INTO the `felhom` +pool so the agent token can be scoped to `/pool/felhom` + `/storage/` (blast-radius containment +on a shared host). Grounded by `felhom.eu/documentation/audits/SPIKE-pool-scoped-acl-2026-07-01.md` (PASS). -## 1. Confirmed baseline +## 1. Baseline → target -| Repo | `main` @ commit | Current | → Target | -|------|-----------------|---------|----------| -| felhom-agent | `1502ca6` | **v0.51.0** | **v0.52.0** | +felhom-agent `0573ec9` (v0.52.0) → **v0.53.0**. No engine/ACL logic beyond the restore param. ## 2. Files changed + commit -- `cmd/felhom-agent/main.go` — `-cores`/`-memory` flags; `Cores`/`MemoryMB` on `bringUpSizing`; passed - in both dispatch literals + both `BringUpSpec{}` builds (`runSelftestBringUp`, `runSelftestProvision`); - `-selftest` usage string updated; version `0.51.0`→`0.52.0`. -- `internal/reconcile/bringup_test.go` — new `TestBuildBringUpConfig_ResourceCaps`. -- `internal/reconcile/bringup.go` — **not changed** (mechanism already correct; used only for the red-proof, reverted). -- `CHANGELOG.md`, `README.md` — updated. -- Commit **`0573ec9`** on `main` (parent `1502ca6`). +- `internal/proxmox/mutate.go` — `RestoreLXCOptions.Pool` + `RestoreLXC` sends `pool=` when non-empty. +- `internal/reconcile/bringup.go` — `const DefaultPool = "felhom"`; `BringUpSpec.Pool`; bring-up restore + passes `Pool: spec.Pool`. +- `internal/reconcile/restoretest.go` — scratch restore passes `Pool: DefaultPool` (residual #2). +- `cmd/felhom-agent/main.go` — both `BringUpSpec` literals (bring-up/DR + provision) set `DefaultPool`. +- Tests: `internal/proxmox/restore_test.go` (`TestRestoreLXC_PoolParam`), `internal/reconcile/bringup_test.go` + (`TestRestoreSitesUsePool`). `CHANGELOG.md`, `README.md`. +- Commit **`7639ab5`** on `main` (parent `33e3443`). ## 3. Green gate -- **Local (Go 1.26.0, Windows)** and **build server 192.168.0.180 (Go 1.26.0, linux)**: - `go build ./...` OK, `go vet ./...` OK. -- `go test ./...` — **all packages pass EXCEPT a pre-existing flake** in `internal/escrow` - (`TestGenerateRecoveryCode_EntropyAndFormat`): the random recovery code occasionally contains a - hyphenated wordlist entry (e.g. "yo-yo"), so the test's `-`-split counts 11 "words" instead of 10. - Confirmed flaky by re-running 5×: **1 fail / 4 pass**. It is in a package this task did NOT touch and - is unrelated to the cap change (see Observations). My package `internal/reconcile` and everything else - pass deterministically. +`go build ./...`, `go vet ./...`, `go test ./...` — clean (local + build server 180). -## 4. Test + omit-when-zero red-proof (§10) +## 4. Tests + red-proofs (§10) -- `TestBuildBringUpConfig_ResourceCaps` **PASS**: `buildBringUpConfig(Cores:2,MemoryMB:4096)` → - `cores="2"`,`memory="4096"`; `buildBringUpConfig(Cores:0,MemoryMB:0)` → both keys **absent**. -- **Red-proof:** temporarily replaced the `if spec.Cores > 0 {…}` guard with an unconditional - `params["cores"]=strconv.Itoa(spec.Cores)` → the omit-when-zero assertion FAILED as designed - (`bringup_test.go:115: cores must be ABSENT when unset (golden default), got "0"`). Reverted; test - passes again; `git diff` shows `bringup.go` clean (no net change). +- `TestRestoreLXC_PoolParam` PASS: `Pool:"felhom"` → `pool=felhom` in the POST body; `Pool:""` → key + absent. **Red-proof:** unconditional `v.Set("pool",…)` → the empty case sent `pool=` → omit assertion + FAILED as designed; reverted. +- `TestRestoreSitesUsePool` PASS: the provision bring-up threads `spec.Pool` and the restore-test + hardcodes `DefaultPool` → both captured `RestoreLXCOptions.Pool == "felhom"`. **Red-proof:** removed + `Pool` from both sites → the provision assertion FAILED; reverted. -## 5. Deployed version + T-A scratch live proof (felhom-pve) +## 5. Published + hub manifest -- Built `felhom-agent 0.52.0` on 180 (`sha256 5bfc690c421b7799…`); deployed to **felhom-pve** - (backup `/usr/local/bin/felhom-agent.bak-0.51.0`, `install -m0755`, `systemctl restart`). Verified: - `felhom-agent --version` = **0.52.0**, `systemctl is-active` = **active**, journal clean (normal - host-report + sudo pct/docker activity). -- **T-A (Scenario A, cap pre-boot) — PASS.** Ran the SAFE scratch bring-up on a free vmid **9300**: - ``` - felhom-agent --config /etc/felhom-agent/agent.json --selftest=bring-up -mode provision \ - -archive local:backup/vzdump-lxc-9100-2026_06_27-11_42_51.tar.zst -vmid 9300 \ - -cores 1 -memory 1024 -keep - → bring-up record: Pass=true, Verified="boot+running", 18s - pct config 9300: - cores: 1 - memory: 1024 - rootfs: local-lvm:vm-9300-disk-0,size=32G - pct destroy 9300 → DESTROYED (confirmed gone) - ``` - The guest booted+ran with `cores:1`/`memory:1024` = the cap was written into the pre-start config PUT - (guest never ran uncapped). Scratch guest torn down; **guest 9201 untouched**. +- Gitea generic package **`admin/generic/felhom-agent/0.53.0/felhom-agent`**, sha256 + `9d1ef8298dcc5090936afa1b88f1612c0b5f03a62548ebbe0989d34d6622aa61` (publish did delete-then-PUT + + round-trip GET verify). +- **Hub artifact manifest — REQUIRED operator follow-up (mandatory for fresh installs):** the served + manifest still advertises agent **0.52.0**. A FRESH install now runs script v1.6.0 (scoped ACL + + `Pool.Allocate`), which is only correct with agent ≥ v0.53.0 (restore `--pool`); with 0.52.0 the guest + restores OUTSIDE the pool and the scoped token then 403s on it. **An operator must set the artifact + manifest to agent 0.53.0 / sha `9d1ef8298dcc5090936afa1b88f1612c0b5f03a62548ebbe0989d34d6622aa61`** in + the hub UI (now a dropdown, auto-sha — hub v0.29.0) before any fresh v1.6.0 install. The demo swap did + NOT depend on this (binary deployed directly). -## 6. Served/published + hub manifest +## 6. Supervised swap on felhom-pve — executed + validated (all gates PASS) -- **Gitea generic package published:** `admin/generic/felhom-agent/0.52.0/felhom-agent`, sha256 - `5bfc690c421b7799f4e0640f13559c41e5933ee60962bf80471eca7eae0e94cb`. Publish did delete-then-PUT + - a round-trip GET sha verify (fetchable + intact end-to-end). Re-fetched independently: 12 964 463 - bytes, sha `5bfc690c…`. -- **Hub artifact manifest — NOT fully bumped (required operator follow-up).** The env fallback on the - hub deploy was set to `ARTIFACT_AGENT_VERSION=0.52.0` / the new sha (live pod confirms). BUT the hub - seeds env **only into an empty DB field** (`cmd/hub/main.go` L164 `if m.AgentVersion == ""`), and the - stored manifest already holds **0.43.0**, which wins. `GET /api/v1/artifacts/{customer}` therefore - still returns agent **0.43.0**. The only in-app editor is the **password-gated** operator UI - (Configs → Day-0 artifacts / `handleSetArtifacts`), which I cannot submit; the alternative (a direct - write to the live operator-backend DB — no `sqlite3` in the pure-Go hub image) is beyond this task's - risk envelope. **ACTION REQUIRED before a colleague uses `--cores`/`--memory` on a fresh install:** an - operator must set the artifact manifest to **agent 0.52.0 / sha - 5bfc690c421b7799f4e0640f13559c41e5933ee60962bf80471eca7eae0e94cb** in the hub UI (the binary + env - fallback are already in place). +Sequence: `systemctl stop felhom-agent` → `felhom-host-install.sh --rescope-acl --vmid 9201` (added the +3 scoped roles+grants incl. `/storage/felhom-pbs`; removed the old broad `FelhomAgent` `/` grant + role) +→ deploy `felhom-agent 0.53.0` (backup `.bak-0.52.0`) → `systemctl start`. Validation, all under the +scoped token: + +| Gate | Result | +|------|--------| +| A selftest=read | ✅ OK — now sees only the pool guest (9201) + the 3 scoped storages (was 5+all guests) | +| B provision-into-pool | ✅ `--selftest=bring-up -mode provision -vmid 9300` → boot+running, 9300 landed in pool (restore-into-pool + SetConfig + start all scoped) | +| C **PBS under scope (residual #1)** | ✅ vzdump 9300 → `felhom-pbs` (200, task OK) + restore-from-PBS → 9301 pool=felhom (200, OK, landed in pool); cleanup 200 | +| D **restore-test into pool (residual #2)** | ✅ scratch 990000 restored into pool, boot+running, torn down, `pass=true` | +| E blast-radius | ✅ non-pool guests 9001/9100 → 403 (`VM.Audit`/`VM.PowerMgmt`); ungranted `felhom-usb` → 403 | +| F 9201 health | ✅ running; controller container Up (healthy) — agent↔guest channel intact; `--selftest=hub` OK; journal clean; pool = `[9201]` | + +Cleanup: scratch 9300/9301/990000 destroyed, PBS + local scratch snapshots freed, `.bak-0.52.0` rollback +binary retained. The live `felhom` pool holds only 9201. ## 7. NOT yet live-validated — awaiting supervised run -- A **capped real customer `--selftest=provision`** (full slice-8A chain with `-cores`/`-memory` against - a real customer/guest). The T-A scratch bring-up above is the safe mechanism proof; a real capped - customer provision is the supervised step (colleague's box). +- **The colleague's-box migration** (single-node, so no cluster nuance) — a later supervised repeat of + the §6 stop→rescope→deploy→start + gates, once the hub manifest serves 0.53.0. -## 8. Observations (noticed, NOT acted on) +## 8. Observations -- **Pre-existing flake** `internal/escrow/TestGenerateRecoveryCode_EntropyAndFormat` — the wordlist - contains hyphenated entries (e.g. "yo-yo"); the test derives word count via `strings.Split(code,"-")`, - so a hyphenated word inflates the count to 11 and the `== 10` assertion fails ~1/5 runs. Fix is a - test/wordlist concern in a package outside this task's scope; left untouched. -- The hub artifact manifest was already stale (0.43.0) before this task — fresh installs were fetching - an old agent regardless. Bumping it (item 6) also resolves that lag. +- Storage blast-radius is a bonus: the scoped token also 403s on ungranted storages (felhom-usb) — read + containment beyond guests. +- The agent's `ListLXC`/`ListStorage` now return filtered lists (pool guests / scoped storages) — the + host-report naturally scopes to Felhom's own footprint. Correct + intended. +- felhom-pbs is the offsite backup target; the `Store` grant on `/storage/felhom-pbs` is mandatory (was + the SPIKE's main unvalidated residual — now proven live).