Files
felhom.eu/REPORT.md
T
admin 7eb3772000 hub: opaque PBS recovery-code escrow storage (v0.8.0) + doc 03 §8a posture model
Slice-7 close-out (hub half). PUT /api/v1/hosts/{host_id}/escrow (per-host key)
stores the agent's OPAQUE R-wrapped blob verbatim against the host; the hub never
decrypts it (no recovery code, no decrypt path). host_escrow table + Save/GetHostEscrow.
Tests: verbatim store, rotation last-write-wins, 401/403/400 auth+body, wire contract.

doc 03 §8a rewritten into the key-custody posture model: separation principle,
topology matrix, default + anti-lockout ladder, SSH-vs-key, breach/legal, integrity
caveat. Corrected: hub opaque storage is slice 7 (this task); serving is slice 10.
Slice table + §13 updated.

No secrets committed (R/K never appear; spike findings + docs use placeholders).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 07:46:33 +02:00

3.1 KiB
Raw Blame History

felhom.eu — task reports

Overwrite this file with a summary of the most recent task only (uniform with the other repos; not cumulative). The cumulative hub history lives in hub/CHANGELOG.md.


REPORT — Slice 7 close-out: PBS escrow — hub opaque storage + doc 03 §8a (v0.8.0) (2026-06-10)

Outcome

The felhom.eu half of TASK — Slice 7 close-out: PBS recovery-code escrow. The agent (felhom-agent v0.9.0) creates an opaque R-wrapped copy of the PBS key in the zero-knowledge default; this slice adds the hub opaque storage for that blob and rewrites doc 03 §8a into a full key-custody posture model. The wrap→recover→restore round-trip was proven on a throwaway first (documentation/tests/slice7-escrow-spike-findings.md).

What landed (hub v0.8.0)

  • PUT /api/v1/hosts/{host_id}/escrow (internal/api/handler.go) — per-host-key authed (a host writes only its own escrow; global operator key also accepted). Decodes the base64 blob and stores the opaque bytes verbatim against the host. The hub never decrypts — there is no decrypt path; it has no recovery code. Rotation is last-write-wins.
  • host_escrow table + SaveHostEscrow/GetHostEscrow (internal/store). Blob is ciphertext.
  • Contract: escrowUploadRequest mirrors the agent's emit struct (blob_b64, key_fingerprint, posture, created_at); a key-set test in each repo guards drift.
  • Tests: stores the blob byte-identical; rotation last-write-wins; 401 (absent/wrong key), 403 (host writing another host's escrow), 400 (bad base64); contract key-set. go test ./... green.

Documentation (doc 03 §8a)

Rewrote §8a into the key-custody posture model: the separation principle (reading data needs both chunks and a key; zero-knowledge holds while Felhom never holds both), the topology matrix (data location × key custody → who can read; the one dangerous cell flagged), the default (Felhom storage + customer-only key; R printed durably), the anti-lockout ladder ((b) wrapped offline copy → (a) raw paperkey → Felhom-holds-a-key), SSH-for-support is a separate grant (not coupled to key custody), why zero-knowledge stays default (breach + legal compellability), and the integrity caveat for self-hosted-data postures. Corrected the storage-slice note: hub opaque storage is slice 7 (this task); only restore-mode serving is slice 10. §9 slice table + §13 updated.

Live validation

After the v0.8.0 deploy, the demo agent's --selftest=escrow-create -upload PUT the opaque blob and the hub stored it against the host; the stored bytes are ciphertext (not the key). The recovery code R is never sent to or stored by the hub. (No R/K value appears in any committed file.)

Deferred / security

Restore-mode serving + consumption → slice 10. The hub holds ciphertext only — possessing the blob does not let Felhom read customer data (separation principle). No secrets committed.

Deploy (GitOps)

Build+push felhom-hub:v0.8.0 → bump manifests/hub.yaml → commit → sync the felhom ArgoCD app.