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.
6.1 KiB
CLAUDE.md — felhom-agent
Stable orientation only — current state lives in
CONTEXT.mdand the top ofCHANGELOG.md, never here. Cross-repo conventions (artifact taxonomy, access, clean-tree gate, secrets, CHANGELOG/REPORT): workspace-root/mnt/5_hdd/felhom.eu/git/CLAUDE.md. Path-scoped detail:.claude/rules/.
What this repo is
The operator-tier host agent, one per Proxmox host, owning all Proxmox interaction: provision/restore guests, host storage, backup/restore orchestration, the hub control loop, and a narrow per-guest local API. It is the most privilege-sensitive component in the system.
- Renamed from
proxmox-controller. - Distinct from
felhom-controller— that is the in-guest controller, Docker-only, holding no Proxmox credentials. Do not confuse them. - Control plane, not data plane: if the agent dies, apps keep serving; only management degrades.
- Pure Go stdlib +
golang.org/x/crypto. No web frameworks.
Doing X → read Y
| Doing | Read |
|---|---|
| writing any new code | REUSE.md — helpers, format-safety guards, traps, seams |
| needing current state / open threads | CONTEXT.md + the top CHANGELOG.md entry |
| Proxmox, reconcile or signed jobs | loads itself: .claude/rules/proxmox.md |
| local API, authz or guest hooks | loads itself: .claude/rules/localapi.md |
| backup, PBS or DR | loads itself: .claude/rules/backup.md |
| storage or escrow | loads itself: .claude/rules/storage.md |
| writing a health check | loads itself: .claude/rules/health-checks.md |
| release, build, publish, deploy, verify a version | the felhom-build-deploy skill — never hand-roll it |
| writing or reviewing a test, fixing a bug | the felhom-testing skill |
| host addresses, break-glass, node facts | felhom.eu/documentation/operations/nodes.md — never restate them |
| which box may I break | felhom.eu/documentation/runbooks/target-selection.md |
| what version is live anywhere | ask the hub (/hosts, /configs) or the box — never a doc |
| the authoritative design | felhom.eu/documentation/architecture/03-host-agent.md (locked) |
The root-CLI fence — API-first, exactly three exceptions
This is in the core because breaching it is how this component stops being auditable.
- API-first via a scoped
FelhomAgenttoken. Raw root-CLI is fenced to exactly 3 exceptions: keyctlpct create(golden image), USB mount/fstab, SMART/sensors. Clientnever shells out;Privilegednever makes HTTP calls — asserted byrouting_test.go. Adding a method toproxmox.Privilegedbreaks the fence; useproxmox.Runnerplus a new sudoersCmnd_Aliasandvalidate.go-style checks (REUSE.md§3).- Destructive ops go through the reconcile gate / signed-jobs path. Never call
Client.DestroyLXC/Vzdump/SetConfigad-hoc — that skips classification, signature, per-guest serialization and crash recovery. - Ownership must be PROVEN, never assumed. A raw
ListLXClist is not "guests the agent owns"; intersect withClient.Poolmembership and fail safe on a read failure (audit A1).
Gates — ONE entry point
Run python3 scripts/agent_gates.py from the repo root after ANY change here. It runs this
repo's gates — reuse_refs_check and instructions_gate, both the shared copies in
felhom.eu/scripts/, never copied into this repo (a copy recreates the drift they detect; an absent
sibling clone FAILS). --fast selects the gates touching no network and no container runtime; today
that is all of them. A missing gate is a FAILURE, never a skip.
The pre-push hook (.githooks/pre-push) runs it with --fast and refuses a failing push. It is
per-clone — switch it on once with git config core.hooksPath .githooks, and a manual run WARNS
when this clone is unarmed. git push --no-verify bypasses it deliberately; say so in the session
report when you use it — CI re-runs the same entry point on every push and emails the operator on
failure, so a bypass is noticed even though it is not blocked (R-168, CLOSED 2026-08-02).
Live validation — the fence
Exercise the SERVER-SIDE PIPELINE a real user triggers, end-to-end. The forbidden shortcut is BYPASSING it — the F9 episode was a raw guest-attach with hand-set state, and it proved nothing.
claude-in-chrome is NOT available on DooPlex. Invoking the exact endpoint the UI invokes is an
acceptable proxy — say which method was used. Low-level mechanism tests where the direct call IS
the mechanism are exempt.
Conventions
- Trunk-based — no branches. All shippable work commits directly to
main;mainequals what is deployed. Report-only artifacts (audits, findings, fixspecs) go tofelhom.eu/documentation/. - Unattended escape hatch: if a fix cannot be cleanly verified and shipped, revert and report — never park it on a branch.
- Logging: the slog logger fans out to journald (configured level) plus the always-DEBUG
applog.Ring(remote pulls). English, keys-never-values, durations on outcomes. Full rules:felhom.eu/documentation/runbooks/logging-conventions.md. - Update
REUSE.mdin the same commit that adds, changes or deprecates a shared helper or pattern.
End-of-session checklist
CHANGELOG.md(cumulative, newest on top) andREPORT.md(overwritten with this run only) — in every repo touched.CONTEXT.md— decisions, state, what is next.REUSE.md— if a shared helper or pattern moved.- A finding goes in
felhom.eu/documentation/backlog/OPEN-ITEMS.mdfirst, never only in a report or an audit. - Confirm your own last push's CI run went green, by run ID — CI mails on failure, which is a PUSH signal; this is the PULL check that catches a lost or unread mail. An unchecked green is an assumption, not an observation.