v0.62.0 — A1: pool-membership ownership check for the stale-lock reaper

staleLockController.Guests() = ListLXC ∩ GET /pools/felhom members (ownership
PROVEN via the pool registry, never assumed from enumeration scope); pool-read
failure fail-safes the whole recovery through the existing guest-list guard.
New Client.Pool read (needs Pool.Audit — host-install v1.9.0; Pool.Allocate
does NOT satisfy it, spike T2). Composed pve:pool-read capability (non-critical)
+ --selftest pool-read line. Red-proofed negative tests drive the REAL
controller over a broad-token-shaped fake.

Per SPIKE-a1-pool-membership-read-2026-07-03.md; audit A1
(AUDIT-blast-radius-hostroot-localapi-2026-07-02).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
This commit is contained in:
2026-07-03 13:25:05 +02:00
parent 3e760a559a
commit 3f37c5fc23
8 changed files with 386 additions and 26 deletions
+9
View File
@@ -35,6 +35,15 @@ func (c *Client) ListLXC(ctx context.Context) ([]Guest, error) {
return gs, c.get(ctx, "/nodes/"+c.node+"/lxc", &gs)
}
// Pool returns GET /pools/{name} (the pool's membership — the stale-lock reaper's ownership
// registry, audit A1). Requires `Pool.Audit` at `/pool/{name}` — NOTE: `Pool.Allocate` does NOT
// satisfy the read (spike SPIKE-a1-pool-membership-read T2: the live 403 named Pool.Audit with
// Allocate already granted). Host-install v1.9.0+ grants it in the FelhomAgentGuest role.
func (c *Client) Pool(ctx context.Context, name string) (PoolInfo, error) {
var p PoolInfo
return p, c.get(ctx, "/pools/"+url.PathEscape(name), &p)
}
// GuestStatus returns GET /nodes/{node}/lxc/{vmid}/status/current. The API body
// has no vmid field (it is in the path), so it is set from the argument.
func (c *Client) GuestStatus(ctx context.Context, vmid int) (Guest, error) {