8abc1b8852
DR recipe slice (agent half), grounded in SPIKE-dr-recipe-2026-06-16. Additive `dr_recipe` host-report section = the non-secret reconstruction scaffolding the operator must rebuild before PBS bytes can land. Built by pure BuildDRRecipeHostHalf from facts the report already collects (no new reads): guests[] sizing, drives[] (user-data by durable_id/role/mount/intent), pve_storage[] (storage.cfg), pbs coordinates. BOUNDARY (Phase-1 lesson): every field is an identifier/intent/size/coordinate — never a key/password/token/hash/ENC:. PBS key stays in escrow; restic password stays in escrow; the recipe names only the coordinates the restore targets. Tests: BuildDRRecipeHostHalf selection, NoPBS, NoSecrets (boundary mirror), dr_recipe key-set in the cross-repo golden contract test. recipe_version=1, ignore-unknown on read. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
57 lines
3.2 KiB
Markdown
57 lines
3.2 KiB
Markdown
# REPORT — felhom-agent v0.38.0 (DR recipe: agent storage/guest/PBS half)
|
|
|
|
**TASK — DR recipe slice (agent half).** Emit the secret-free reconstruction-recipe scaffolding the
|
|
agent already owns, as an additive host-report section. Grounded in `SPIKE-dr-recipe-2026-06-16.md`.
|
|
|
|
## What
|
|
|
|
The recipe is the non-secret **re-provision plan** that complements escrow (keys) and PBS/restic
|
|
(bytes): guest sizing, drive inventory (durable-id → role → mount → intent), PVE storage defs, and PBS
|
|
coordinates — the host/guest/storage scaffolding the operator must rebuild on new hardware *before* the
|
|
PBS bytes can land. The agent owns every fact (`StorageTarget`, `GuestSpec`, `PBSSnapshot`), so it emits
|
|
its half additively in the existing host-report; the hub assembles it with the controller's app half.
|
|
|
|
## Implementation
|
|
|
|
- `internal/hub/dr_recipe.go` — wire types + the pure `BuildDRRecipeHostHalf(guests, targets, pbs)`:
|
|
- `guests[]` ← each `Guest.Spec` (cores/memory/disk), skipping status-unknown guests.
|
|
- `drives[]` ← user-data external drives only (`isUserDataDrive`: usb/local-dir with a non-empty
|
|
durable-id + mount path) → `{durable_id, role, mount_path, intent=enrolled, total_bytes}`.
|
|
- `pve_storage[]` ← **every** storage target `{name, type, content}` (rebuild `storage.cfg`).
|
|
- `pbs` ← the latest snapshot's `{repo_id (the pbs storage id), namespace, latest_snapshot_id}`.
|
|
- `HostReport.DRRecipe *DRRecipeHostHalf json:"dr_recipe"` (always set in `Collect()`, never null).
|
|
- No new reads — derived from the just-collected report facts.
|
|
|
|
## Boundary (non-negotiable)
|
|
|
|
Every field is an identifier / intent / size / coordinate. The PBS **key** stays in escrow, the access
|
|
**token** in identity-escrow, the restic **password** in escrow — the recipe names only the coordinates
|
|
the restore targets. This is the exact axis the retired infra-backup violated (it shipped
|
|
`encryption_key_b64`/`restic_password`/`cf_api_token`).
|
|
|
|
## Tests (non-hollow)
|
|
|
|
- `TestBuildDRRecipeHostHalf` — drives = only user-data; pve_storage = all targets; pbs = latest
|
|
snapshot; guests skip nil-spec.
|
|
- `TestBuildDRRecipeHostHalf_NoPBS` — no snapshots → `pbs` omitted, slices non-nil.
|
|
- `TestDRRecipeHostHalf_NoSecrets` — the boundary mirror: serialized half has NO key matching
|
|
`(?i)(password|secret|token|hash|passphrase|api[_-]?key|\bkey\b|enc:)`. (The load-bearing boundary
|
|
test, with a synthetic-secret app + allowlist red-proof, lives on the controller emitter.)
|
|
- `dr_recipe` key-set + sub-array element key-sets added to `TestHostReport_ContractMatchesGolden`
|
|
(the cross-repo golden, byte-pinned with the hub's copy).
|
|
|
|
## Versioning
|
|
|
|
`recipe_version=1`, carried in the section. Read is ignore-unknown (encoding/json default) for
|
|
forward-compat, mirroring `storage_manifest`. Golden discipline: `host-report.golden.json` here must stay
|
|
byte-identical to the hub's copy — manual checksum-diff on any wire change (the golden spans three repos).
|
|
|
|
## Gate
|
|
|
|
`go build`, `go vet`, `go test ./...` all green (local + build server). Deployed to felhom-pve.
|
|
|
|
## Deferred (NOT in this slice)
|
|
|
|
The agent-side `restore_directive` consumption / recovery-mode execution (`syncer.go:92`) — slice-10D.
|
|
This slice only EMITS the recipe.
|