77 lines
5.9 KiB
Markdown
77 lines
5.9 KiB
Markdown
# REPORT — felhom-agent v0.89.0 (agent train: R-22 self-grant + escrow live-reload + poke listener)
|
|
|
|
**Date:** 2026-07-16 · **Baseline:** v0.88.0 (`c040c18`) → **v0.89.0** (`a659e5d`, pushed)
|
|
**Green gate:** `go build ./... && go vet ./... && go test ./...` — all pass (full suite, local go1.26).
|
|
**Design contract for item 3:** `felhom.eu/documentation/audits/SPIKE-immediate-sync-transport-2026-07-16.md`.
|
|
|
|
## Items + landmarks as found at source
|
|
|
|
### Item 1 — pbsdr self-grant (R-22, closes F4 from tests/VALIDATION-n100-baremetal)
|
|
- **Where the pre-check lives:** `internal/pbsdr/manager.go` `Apply` → `m.px.StorageEntry(ctx, id)`
|
|
(GET `/storage/<id>`, token-auth). On a non-default storage id the agent token has no ACL there
|
|
yet → **403**; pre-fix the generic `if err != nil { … return }` aborted BEFORE the root-run wrapper
|
|
`grant` (only reached via `adopt`/create) that creates that very ACL — a permanent self-deadlock.
|
|
- **Fix:** on an `errors.As(err, *proxmox.APIError)` with `IsForbidden()` ONLY, run
|
|
`felhom-pbs-apply grant <id>` now (root, no secret, no pre-existing entry — `pveum acl modify` on a
|
|
path is unconditional; verified in `configs/felhom-pbs-apply` `grant)`), re-read once, then flow the
|
|
normal adoption/create path. Every other error stays transient. Grant-OK-but-still-403 →
|
|
`verify_failed` loudly. The pre-check is KEPT (the spec's "don't drop it" — it short-circuits the
|
|
happy path once the ACL exists). A benign extra `grant` on the adopt path is idempotent.
|
|
- **Red-proof (run-fail-revert):** `TestSelfGrant_PreCheck403DoesNotAbortBeforeGrant`. Pre-fix
|
|
(forced `if true` abort): `self-grant never ran … calls=[]` → **FAIL**. Fixed: self-grant runs,
|
|
converges (adopted), no secret consumed, 2 StorageEntry calls (403 + post-grant re-read) → **PASS**.
|
|
|
|
### Item 2 — escrow config live-reload
|
|
- **Two ends:** the pbsdr bridge seeds `escrow.pbs_storage_id` into agent.json on convergence
|
|
(`manager.go` `finishConverged`→`seedEscrowStorageID`); the reader is
|
|
`internal/localapi/escrow_ceremony.go` `handleEscrowPreflight`, which read a **daemon-start
|
|
snapshot** (`EscrowCeremonyConfig.PBSStorageID`) → stayed red until a service restart.
|
|
- **Reload strategy chosen (stated per source shape):** *the preflight reads current disk state*, via
|
|
a new late-bound `CurrentPBSStorageID func() string` (mirrors the existing `DRConfigured func() bool`
|
|
seam). `cmd/felhom-agent/main.go` wires it to `config.Load(cfg.SourcePath).Escrow.PBSStorageID` —
|
|
**exactly what the ceremony subprocess itself loads** — so the row reflects the true state the bare
|
|
`--selftest=escrow-create` one-liner will see. Falls back to the boot snapshot on read error /
|
|
all-env config. Preferred over an in-memory push (no cross-component coupling, single source of
|
|
truth = disk).
|
|
- **Red-proof:** `TestEscrowPreflight_PBSStorageIDLiveReload` — seed after boot, same process; pre-fix
|
|
(static field) stays red → **FAIL**; fixed flips green + reports the seeded id → **PASS**.
|
|
|
|
### Item 3 — agent-plane poke listener (Direction-2a)
|
|
- **Trigger fired:** the hub control loop's out-of-band report trigger (`internal/hub/loop.go`
|
|
`SetTrigger`), the SAME cap-1 non-blocking channel the storage watchdog uses — fan-in, coalescing.
|
|
An immediate collect→report cycle carries the advanced envelope generation → the desired-state
|
|
syncer fetches → pbsdr/wg consumers apply.
|
|
- **New `internal/poke`** — `Listener` binds a contentless UDP socket EXCLUSIVELY to the box's WG /32
|
|
(`wgtunnel.LoadAssignedAddr`, new helper reading `registered.json`; never 0.0.0.0, never LAN) on
|
|
the fixed **port 51822**; payload ignored; leading-edge debounce (`DebounceWindow`) coalesces a
|
|
burst into ≤1 tick; enabled whenever `wg_tunnel.enabled`. Wired in `main.go` (goroutine + errc 7→8).
|
|
- **Red-proofs (run-fail-revert):** `TestBindConfinement` (wildcard bind → bound to `::` /
|
|
`IsUnspecified` → **FAIL**; fixed → the specific WG addr, datagram fires the trigger) and
|
|
`TestDebounceCoalescesBurst` (guard removed → 10 fires for 10 pokes → **FAIL**; fixed → 1, then
|
|
fires again after the window). Port registered in REUSE.md as a shared cross-repo constant.
|
|
|
|
## Deviations from the spec's landmark guesses (source wins)
|
|
- The 403 pre-check is `StorageEntry` (GET `/storage/<id>`) exactly as ROADMAP R-22 states — no
|
|
deviation; `StorageActive` would also 403 but the self-grant at the top fixes both.
|
|
- (Hub side, cross-repo) the MinAgent-floor save does not itself bump per-host desired generation —
|
|
noted in the hub REPORT.
|
|
|
|
## Live validation (Phase 5 — CC-ownable done; remainder operator-gated)
|
|
- **Built + published** agent 0.89.0 to Gitea (sha256 `3969fd91…2609fb1a`, round-trip verified) — the
|
|
binary the floor-driven train (Scenario 4) fetches.
|
|
- **Scenario 4 / Scenario 1 live leg / Scenario 3 latency** require the demo agent to self-update to
|
|
0.89.0 via the floor train, which needs the operator-password-gated Day-0 manifest vouch + floor
|
|
raise (CC cannot drive that UI). Demo agent is still **0.88.0**. Direct binary replace is forbidden
|
|
by the spec (the floor train IS the validation), so these legs are the operator follow-up.
|
|
- **Scenario 1 rollback recorded FIRST (as required):** current ACLs on the demo's
|
|
`/storage/felhom-offsite` are `FelhomAgentStore` for `felhom-agent@pve` (user) and
|
|
`felhom-agent@pve!agent` (token), propagate=1. Re-grant rollback =
|
|
`pveum acl modify /storage/felhom-offsite --users felhom-agent@pve --roles FelhomAgentStore` +
|
|
the `--tokens 'felhom-agent@pve!agent'` twin; the REVOKE that arms the deadlock is the same two with
|
|
`--delete`. NOT run on 0.88.0 (pre-fix → no self-heal, would break the demo's pbsdr). The ep0 poke
|
|
leg IS validated live — see the hub REPORT.
|
|
|
|
## R-13
|
|
Item 3 is the FIRST concrete slice of the OOB/mutual-repair arc (the listener + trigger only). R-13
|
|
stays open; nothing beyond the poke channel was built.
|