v0.113.0 — E-2a: guarded backup-target wrapper + POST /backup/target
The agent cannot create a PVE storage (Datastore.Allocate at /storage) or grant an ACL (Permissions.Modify) -- it holds neither by design, and widening the role would trade the whole blast-radius containment model for one feature. The privileged half therefore lives in a new fenced shim behind a literal FELHOM_BACKUPTARGET sudoers alias, following the mkfs/pbs-apply pattern. The wrapper enforces the two laws E-1 paid for on live hardware so no caller can forget them: F-1 the path must BE the drive's own mountpoint, F-2 is_mountpoint 1 is hardcoded rather than a caller flag. It refuses a root-device target, has NO storage-removal path of any kind (the pbs-apply no-delete law, grep-assertable), is idempotent for the same path, and REFUSES to repoint an existing id. POST /backup/target drives it in a fixed order: create -> grant -> config. Reversed, a config pointing at an ungranted storage 403s every backup on first run -- exactly E-1 finding F-3. A failed grant leaves the config untouched. It deliberately does NOT restart the agent: restarting with a backup in flight cancels the wait and records a spurious tier failure for a backup that actually succeeded (E-1 did this to a real felhom-pbs run). It returns restart_required and the caller restarts behind its own immediate in-flight check. Config rewrite preserves unknown keys verbatim and writes in place, since /etc/felhom-agent is root-owned while agent.json is agent-owned 0600. Green gate: build + vet + test rc=0 (29 packages), run separately from this commit.
This commit is contained in:
@@ -1,3 +1,40 @@
|
||||
## v0.113.0 — E-2a: the guarded backup-target wrapper + POST /backup/target (2026-07-29)
|
||||
|
||||
**The agent cannot do this itself, and that is the point.** Creating a PVE storage needs
|
||||
`Datastore.Allocate` at `/storage`; the ACL grant needs `Permissions.Modify`. The agent holds
|
||||
**neither** — its token is scoped per storage path for blast-radius containment, and
|
||||
`Permissions.Modify` would let it rewrite its own authority. Widening the role to make the move
|
||||
possible would trade the whole containment model for one feature. So the privileged half lives in a
|
||||
new fenced shim, `configs/felhom-backup-target-apply`, behind a literal `FELHOM_BACKUPTARGET`
|
||||
sudoers alias — the felhom-mkfs-guarded / felhom-pbs-apply pattern.
|
||||
|
||||
The wrapper enforces the two laws E-1 paid for on live hardware, so they cannot be forgotten by a
|
||||
caller:
|
||||
|
||||
- **F-1** the path must BE the drive's own mountpoint (`mountpoint -q`), or the target reports
|
||||
`disconnected` forever and its durable id degrades off the filesystem UUID;
|
||||
- **F-2** `--is_mountpoint 1` is HARDCODED, not a caller flag. Without it an unplugged drive leaves a
|
||||
bare directory on the root fs and vzdump writes onto the system drive while PVE reports `active`.
|
||||
|
||||
It also refuses a target backed by the root device, and carries **no storage-removal path of any
|
||||
kind** (the felhom-pbs-apply no-delete law; grep-assertable). `create` is idempotent for the same
|
||||
path and REFUSES to repoint an existing id at a different one — silently moving a live backup target
|
||||
is the failure this arc closes.
|
||||
|
||||
`POST /backup/target` drives it in a fixed order — **create → grant → config**. Reversed, a config
|
||||
pointing at an ungranted storage 403s every backup on first run, which is exactly E-1's finding F-3.
|
||||
A failed grant therefore leaves the config untouched (tested).
|
||||
|
||||
**It deliberately does NOT restart the agent.** The tiers are built once at daemon start, so the move
|
||||
needs a restart — but restarting with a backup in flight cancels the wait and records a SPURIOUS
|
||||
tier failure for a backup that actually succeeded, which E-1 did to a real `felhom-pbs` run. The
|
||||
handler returns `restart_required: true` and the caller restarts behind its own immediate in-flight
|
||||
check. Tests assert the handler never issues a restart itself.
|
||||
|
||||
Config rewrite preserves unknown keys verbatim (`map[string]json.RawMessage`, the
|
||||
`pbsdr.seedEscrowStorageID` discipline) and writes in place, because /etc/felhom-agent is root-owned
|
||||
while agent.json is agent-owned 0600 — a rename is impossible for the non-root agent.
|
||||
|
||||
## v0.112.0 — E-2: GET /disks flags the backup-target drive (2026-07-29)
|
||||
|
||||
Additive field `backup_target` on each `/disks` entry, true for the drive backing the PRIMARY tier.
|
||||
|
||||
Reference in New Issue
Block a user