aa74294a7d
gates / gates (push) Successful in 8s
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.
104 lines
6.1 KiB
Markdown
104 lines
6.1 KiB
Markdown
# CLAUDE.md — `felhom-agent`
|
|
|
|
> Stable orientation only — **current state lives in `CONTEXT.md` and the top of `CHANGELOG.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 `FelhomAgent` token. Raw root-CLI is **fenced to exactly 3 exceptions**:
|
|
keyctl `pct create` (golden image), USB mount/fstab, SMART/sensors.
|
|
- **`Client` never shells out; `Privileged` never makes HTTP calls** — asserted by `routing_test.go`.
|
|
Adding a method to `proxmox.Privileged` breaks the fence; use `proxmox.Runner` plus a new sudoers
|
|
`Cmnd_Alias` and `validate.go`-style checks (`REUSE.md` §3).
|
|
- **Destructive ops go through the reconcile gate / signed-jobs path.** Never call
|
|
`Client.DestroyLXC` / `Vzdump` / `SetConfig` ad-hoc — that skips classification, signature,
|
|
per-guest serialization and crash recovery.
|
|
- **Ownership must be PROVEN, never assumed.** A raw `ListLXC` list is not "guests the agent owns";
|
|
intersect with `Client.Pool` membership 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).
|
|
|
|
<!--
|
|
WHY ONE ENTRY POINT (2026-08-02, R-29): a census of all gates across the four repos found every check
|
|
a CLAUDE.md names was passing, and two of the four nobody is told to run were failing. This repo was
|
|
the extreme case — nothing ran against it at all, and 90 cited paths were checked by no one.
|
|
-->
|
|
|
|
## 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`; `main` equals what is
|
|
deployed. Report-only artifacts (audits, findings, fixspecs) go to `felhom.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.md` in the same commit that adds, changes or deprecates a shared helper or pattern.
|
|
|
|
## End-of-session checklist
|
|
|
|
- **`CHANGELOG.md`** (cumulative, newest on top) and **`REPORT.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.md` first**, 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.
|