--- paths: ["internal/proxmox/**", "internal/reconcile/**", "internal/signedjobs/**"] --- # Proxmox — the API contract, and how destructive work is gated `internal/proxmox/` is the API-first `Client` plus the fenced root-CLI `Privileged`. `internal/reconcile/` is the reconcile engine, reversibility gate, op journal and crash recovery. `internal/signedjobs/` holds the operator-signed destructive executors (wipe, decommission). ## A 200 on the POST is not success **Every mutating op is async**: it returns a **UPID**, and `WaitTask` must assert `exitstatus == "OK"`. Authorization can fail at *task execution* long after the HTTP call returned 200. Treating the POST's status as the result is how a failed destroy reads as a successful one. ## The privsep token gotcha A `--privsep 1` token's rights are the **intersection** of the backing user's permissions **and** the token's own ACLs. The role must be granted on **both** or every call 403s. The same intersection rule bites on PBS (`token ∩ user`). ## TLS **SHA-256 leaf-cert pinning** against the self-signed host cert. **No insecure default**, ever. The pin is the raw leaf-DER sha — the SAN is never checked, so a cert rotation changes the pin and the agent must be re-pinned. ## The destructive path — never the direct call | Do not | Because | Use | |---|---|---| | `Client.DestroyLXC` / `Vzdump` / `SetConfig` ad-hoc | skips classification, signature, per-guest serialization, crash recovery | `reconcile.Engine` paths / `RunSignedJob`; queue via `Queue.Submit` | | add a method to `proxmox.Privileged` | breaks the 3-exception root-CLI fence (`routing_test.go`) | `proxmox.Runner` + a new sudoers `Cmnd_Alias` + `validate.go`-style checks | | treat `ListLXC` output as "guests we own" | audit A1 — pre-v0.62.0 the stale-lock reaper did exactly this, contained only by the pool-scoped token | intersect with `Client.Pool` membership (`staleLockController.Guests()`); **fail safe on read failure** | Full trap table: `REUSE.md` §3. Every guest joins the `felhom` pool — `VM.Audit` comes from the `/pool` grant, not from a per-guest ACL.