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.
45 lines
2.1 KiB
Markdown
45 lines
2.1 KiB
Markdown
---
|
|
paths: ["internal/localapi/**", "internal/authz/**", "internal/guesthook/**"]
|
|
---
|
|
|
|
# Local API, authz and guest hooks — the per-guest blast radius
|
|
|
|
`internal/localapi/` is the narrow per-guest local API: token store, disks/format, guest binds,
|
|
controller swap, stale-lock recovery, pinned self-signed leaf. `internal/authz/` is the operator
|
|
signed-op verifier (SSHSIG) plus the durable nonce store. `internal/guesthook/` installs the
|
|
pre-start self-heal hookscript.
|
|
|
|
> **Overlap note:** `health-checks.md` also matches `internal/localapi/**` and
|
|
> `internal/guesthook/**`. That is deliberate — both rules apply there and both load. Neither
|
|
> supersedes the other.
|
|
|
|
## Scoping is the whole security property
|
|
|
|
This API is reachable **from inside a customer guest**. Every route must be scoped to the guest that
|
|
called it — a route that can name another guest's id has escaped its blast radius. Fail **safe to
|
|
protected**: an unrecognised or unresolvable caller gets less access, never more.
|
|
|
|
## Replay protection must survive a restart
|
|
|
|
**`authz.MemoryNonceStore` on a real host is a defect** — replay protection dies on restart. Use
|
|
`authz.FileNonceStore`. The memory store exists for tests.
|
|
|
|
## The token is a hash on disk, plaintext only at mint
|
|
|
|
The store keeps **hashes**. The plaintext token exists in exactly one place, `bootstrap.json` on the
|
|
PVE host — so a "read the token" step means reading that file, and a lost token is re-minted, never
|
|
recovered.
|
|
|
|
## Binds can brick guest boot
|
|
|
|
| Do not | Because | Use |
|
|
|---|---|---|
|
|
| `GuestBinder.AttachBind`/`DetachBind` (per-drive `pct set -mpN`) | legacy model; a missing bind source can **brick guest boot** (C1) | `AttachDrive`/`DetachDrive` (intermediary model) |
|
|
| `isHostMountpoint` to reconcile bind state | a boolean cannot converge stacked double-binds (the `/mnt` doubling bug) | `countHostMounts` normalization inside `AttachDrive` |
|
|
|
|
<!--
|
|
Why fail-safe-to-protected rather than fail-closed: this API also carries the recovery paths. A hard
|
|
refusal on an unresolvable caller would make a half-broken guest unrecoverable through the very
|
|
interface built to recover it. Less access, never none.
|
|
-->
|