v0.6.0: slice 6 Phase B — PBS offsite tier (verify + PBS-API client + reporting)

Spike-proven that backup/restore-to-PBS reuse Phase A unchanged; the only new code is
the verify capability, a small PBS-API client, and PBSSnapshot reporting.

- internal/pbs: fingerprint-pinned, token-authed PBS-API client (Verify/Snapshots/
  TaskStatus, node-from-UPID; secret read from /etc/pve/priv/storage/<id>.pw at runtime,
  never logged) + the verify maintenance loop (own cadence, default 6h, NOT gated/journaled,
  like the watchdog) + SnapshotStore.
- hub: PBSSnapshot filled (namespace/type/id/time/size/owner/protected/encrypted/
  verify_state/verify_upid); PBSReporter collector seam; cross-repo golden + bidirectional
  key-set tests; hub handler parses pbs_snapshots + logs a failed-verify WARN.
- backup: report the ACTUAL vzdump mode (parsed from the task log; PVE may downgrade
  snapshot->stop). proxmox.Storage.Username. config PBSVerifyCadence/secret-dir.
  --selftest=pbs-verify. Backup/restore-to-PBS unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-09 16:53:04 +02:00
parent faba8e4ff7
commit 766500dfc3
20 changed files with 1082 additions and 37 deletions
+42
View File
@@ -3,6 +3,48 @@
All notable changes to **felhom-agent** are recorded here. Update on every code
change that gets pushed.
## v0.6.0 — slice 6 Phase B: PBS offsite tier (verify + PBS-API client + reporting) (2026-06-09)
Completes slice 6. The PBS spike (felhom.eu phase5-pbs-spike-findings.md) proved backup-to-PBS
and restore-from-PBS reuse Phase A UNCHANGED (PBS is just a storage target + a volid), and the
operator token needs no widening. So the only new agent code is the **verify capability + a
small PBS-API client + PBSSnapshot reporting**. Escrow + host-loss DR stay slices 7/10.
### Added
- **`internal/pbs` — the PBS-API client** (the agent's SECOND privileged external surface,
slice-1 discipline): TLS **fingerprint-pinned** to the PBS leaf cert (a spoofed PBS →
rejected, mirroring the PVE pin), **token auth** (`PBSAPIToken=<id>:<secret>`; id from the
storage `username`, secret read at runtime from `/etc/pve/priv/storage/<id>.pw` — referenced
by location, never logged/committed), typed, no shell. Methods: `Verify` (POST
`/admin/datastore/<ds>/verify` → UPID), `Snapshots` (incl. the `verification` field),
`TaskStatus`/`WaitVerify` (node extracted from the UPID — `localhost` returns "unknown", the
spike B4 gotcha), `NodeFromUPID`.
- **The verify maintenance loop** (`pbs/verify.go`) — the cheap, key-free, ciphertext-level
integrity check (§8) on its OWN cadence (default 6h, the 5th daemon goroutine). It is a
reporting/maintenance task like the slice-5 watchdog: it does NOT go through the reconcile
gate/journal. Each cycle: trigger verify → poll task → re-list snapshots → record
per-snapshot `verify_state`. A failed verify is logged loudly.
- **`PBSSnapshot` reporting** — filled the stub (`namespace`/`backup_type`/`backup_id`/
`backup_time`(RFC3339)/`size_bytes`/`owner`/`protected`/`encrypted` (from `files[].crypt-mode`)
/`verify_state` (ok|failed|**none** until verified)/`verify_upid`). New `PBSReporter`
collector seam + an in-memory `SnapshotStore`. Cross-repo golden (both repos, byte-identical)
+ bidirectional key-set tests; hub `handler.go` parses `pbs_snapshots` and logs a **failed
verify `[WARN]`** (loudest offsite-DR signal).
- **Truthful backup mode** (`backup/runner.go`) — `Backup.mode` now reflects the ACTUAL vzdump
mode read from the task log (`backup mode: <x>`), since PVE may downgrade snapshot→stop for a
stopped guest (spike B1); falls back to the requested mode if unparseable.
- **proxmox**: `Storage.Username` (parsed from the pbs storage config — the token id).
- **config** `BackupConfig.{PBSVerifyCadenceSeconds, PBSSecretDir}` (cadence 0→6h, <0 disabled).
- **`--selftest=pbs-verify`** — discover pbs storages → verify each → print the PBSSnapshot
records (covers the runbook's verify + list). Standalone on the host.
### Notes
- Backup/restore-to-PBS reuse Phase A with no change (the restore-test runs with
`source_tier="pbs"` when fed a pbs volid). Zero-knowledge holds: verify is ciphertext-level,
the encryption key is never read here, and the PBS server has no client key (spike B6).
- Daemon runs cleanly with no pbs storage / verify disabled. `go test -race` covers the new
goroutine. Slice-3/4/5/6A surfaces, goldens, and adversarial tests intact.
## v0.6.0-rc1 — slice 6 Phase A: backup + the self-restore-test (local target) (2026-06-09)
Phase A of the backup/restore slice (doc 03 §8) — the agent's guest-level backup layer and