Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
7.7 KiB
CLAUDE.md — felhom-agent
Loads when Claude Code touches this repo. Stable orientation only — current state lives in
CONTEXT.mdand the top ofCHANGELOG.md, never here. Cross-repo orientation: workspace-roote:\git\CLAUDE.md.
What this repo is
felhom-agent is the operator-tier host agent that runs on each Proxmox host and owns 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.
- Renamed former
proxmox-controllerrepo. - Distinct from
felhom-controller— that is the in-guest controller (Docker-only, no Proxmox creds). Do not confuse them. - Control plane, not data plane: if the agent dies, apps keep serving; only management degrades.
Read before writing code
REUSE.md— canonical helpers, format-safety guards, traps, seams. Check it first; update it in the same commit that changes a shared helper or pattern.CONTEXT.md(current state + open threads) and the topCHANGELOG.mdentry (authoritative history).- Design doc:
felhom.eu/documentation/architecture/03-host-agent.md(locked). Platform facts:felhom.eu/documentation/proxmox-platform.md+tests/phase{0,1-2,3,4}-findings.md.
Layout (verified against the tree)
cmd/felhom-agent/ main + flags + --selftest modes + the daemon entry
cmd/felhom-opsign/ offline operator signing CLI (SSHSIG)
internal/authz/ operator signed-op verifier (SSHSIG) + durable FileNonceStore
internal/backup/ vzdump backup runner + restore-test scheduler + report store
internal/capability/ live sudo-policy capability probe (degradation visibility)
internal/config/ JSON config + FELHOM_AGENT_* env overlay; secrets redacted (Redacted())
internal/desired/ hub desired-state syncer (envelope observer)
internal/escrow/ PBS-key escrow (zero-knowledge recovery code)
internal/guesthook/ pre-start self-heal hookscript install
internal/hub/ daemon: HostReport collector + Bearer client + resilient Loop
internal/lanresolver/ split-horizon DNS on guest IP change (dnsmasq RESTART, not reload)
internal/localapi/ per-guest local API: token store, disks/format, guest binds, controller swap,
stale-lock recovery, pinned self-signed leaf
internal/log/ slog setup
internal/pbs/ PBS-API client (fingerprint-pinned) + verify maintenance loop
internal/provision/ guest bootstrap back-half (token mint → bootstrap.json → pct bind)
internal/proxmox/ API-first Client + fenced root-CLI Privileged + UPID WaitTask
internal/reconcile/ reconcile engine + reversibility gate + op journal + crash recovery
internal/signedjobs/ operator-signed destructive executors (wipe, decommission)
internal/storage/ storage observer + durable ids + role/claim classifiers + SudoHostOps + watchdog
Build / run
- Module
gitea.dooplex.hu/admin/felhom-agent; binaryfelhom-agent(cmd/felhom-agent/). - Pure Go stdlib +
golang.org/x/cryptoonly — no web frameworks.go.moddirective go 1.25.0; the build server (192.168.0.180) runs a newer upstream Go — build/run live tests there (same LAN as the demo host). - Version via
-ldflags "-X main.version=<v>";--versionflag. Bump on meaningful changes + CHANGELOG entry. - Full build/deploy/publish runbook: use the
felhom-build-deployskill. Summary:
| Step | Where | One-liner |
|---|---|---|
| Build | 180 | cd ~/git/felhom-agent && git pull && go build -ldflags '-X main.version=<v>' -o /tmp/... ./cmd/felhom-agent |
| Deploy | felhom-pve | backup .bak-<old> → install -m0755 → systemctl restart felhom-agent (non-root felhom-agent user, config /etc/felhom-agent/agent.json) |
| Ship configs | felhom-pve | sudoers (/etc/sudoers.d/felhom-agent) + guarded-mkfs wrapper WITH the binary when configs/ changed |
| Publish | local | scripts/publish-agent.sh <ver> <bin> (REGISTRY_* creds); hub Day-0 manifest vouch = operator follow-up |
| Verify | felhom-pve | felhom-agent --version + journal (clean ReassertGuestBinds, no capability degradation) |
Proxmox model (the load-bearing rules)
- 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). Keep that fence. - Every mutating op is async → returns a UPID →
WaitTaskassertsexitstatus == "OK". A 200 on the POST is not success; authorization can fail at task execution. - TLS: SHA-256 leaf-cert pinning (self-signed host cert). No insecure default.
- Privsep token gotcha: a
--privsep 1token's rights = intersection of the backing user's perms AND the token's ACLs — the role must be granted on both, or every call 403s. - Destructive ops go through the reconcile gate / signed-jobs path — never call
Client.DestroyLXC/Vzdump/SetConfigad-hoc (REUSE.md §3).
Demo host (for live tests)
Node demo-felhom, API https://192.168.0.162:8006. SSH alias felhom-pve (root@pam) —
available to CC; use MSYS_NO_PATHCONV=1 for pct commands. The agent pins the served leaf cert —
verify the fingerprint still matches before a live run. Selftest modes (run from 180, pointed at the
demo API): --selftest[=read|task|hub|storage|backup|restore-test|pbs-verify]; no flag = the daemon.
Conventions
Trunk-based — no branches
All shippable work commits directly to main; main equals what is deployed.
- Report-only artifacts (audits, findings, fixspecs) →
felhom.eu/documentation/(audits/,backlog/). - Risky/supervised fixes are spec'd, then implemented during the supervised session, on
main. - Unattended escape hatch: if a fix can't be cleanly verified/shipped, revert + report — never park on a branch.
In every repository where you make a change, update both files in that repo:
CHANGELOG.md— cumulative log, newest on top.REPORT.md— overwrite with the most recent implementation/validation summary only.Never write secrets into any committed file — reference them as "stored out-of-band".
- Code quality: verify generated code for bugs/edge cases; add debug logging; ask rather than guess when you'd otherwise invent input/output.
- Update
REUSE.mdif you added/changed/deprecated a shared helper or pattern (same commit). - Testing doctrine (non-hollow tests, red-proofs, seams): use the
felhom-testingskill. - Logging: the slog logger fans out to journald (configured level) + the always-DEBUG
applog.Ring(remote pulls) — English, keys-never-values, durations on outcomes; full rules infelhom.eu/documentation/runbooks/logging-conventions.md.
Live validation
Exercise the SERVER-SIDE PIPELINE a real user triggers, end-to-end. The forbidden shortcut is BYPASSING it (the F9 episode: raw guest-attach + hand-set state). Invoking the exact endpoint the UI invokes is an acceptable proxy when a browser isn't available — say which method was used. Low-level mechanism tests where the direct call IS the mechanism are exempt.
Workflow & artifacts
- Implement
TASK.md/TASK-*.mdspecs (when placed asTASK.mdor told to), then push + CHANGELOG + REPORT.md. RUNBOOK-*.md— an operational procedure. CC executes the steps it has access and capability for, including live validation on the demo Proxmox host (CC has root@felhom-pve SSH + the felhom-agent token). Mark a step HUMAN only when it genuinely needs physical presence, a real-world decision, or credentials CC truly lacks. Judgment still applies: confirm before irreversible ops on real customer data — demo scratch guests are fair game.