175 -> 99 effective lines. New .claude/rules/{proxmox,localapi,backup,storage}.md alongside the
existing health-checks.md. The release section points at the felhom-build-deploy skill rather than
restating a table that drifts from the script; the layout section's per-package annotations moved
into the rule file for their area instead of being deleted.
Kept in the core because it is the only part re-injected after /compact: the root-CLI fence and its
three exceptions, the destructive-op gate, prove-ownership (audit A1), the gate entry point, the F9
live-validation fence, and the checklist.
health-checks.md overlaps localapi.md and storage.md on three globs -- deliberate, both load,
stated in each file. Go build/vet/test green and unchanged.
2.4 KiB
paths
| paths | |||
|---|---|---|---|
|
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.