Files
felhom.eu/REPORT.md
T
admin 3457415117 slice 10D (hub): DR capstone — recovery mode + re-enroll + directive serving (hub v0.11.0)
Recovery-mode toggle (global key, bounded auto-expiry) gates re-enroll +
restore-directive serving. Re-enroll rotates the agent<->hub credential to the
new box (old key revoked); returns the opaque escrow blobs + non-secret
directive. Store gains recovery_mode_until + identity_blob + directive_json.
Hub holds no usable secret + no Cloudflare write-power (operator-side rotation).
Doc 03 §9: slice 10 CLOSED.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 09:48:38 +02:00

2.5 KiB

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 10D (hub half): DR capstone — recovery mode + re-enroll + directive serving (hub v0.11.0) (2026-06-10)

Type

TASK (CC-implemented). The hub half of the slice-10 DR capstone (closes slice 10). Pairs with felhom-agent v0.18.0 (identity escrow + restore-mode consumption).

What changed (hub)

The hub ORCHESTRATES recovery but holds no usable secret and no Cloudflare write-power — a compromised hub can at most hand out opaque blobs (they need R, which the hub never has) + rotate its own per-host credential. It cannot hijack a customer's tunnel (the destructive rotation is the operator's job).

API

  • PUT/DELETE /admin/hosts/{id}/recovery-mode (global key) — arm/disable recovery mode with a bounded TTL (clamped [60s, 4h], default 30m → auto-expires). Directive + re-enroll are served ONLY while active.
  • POST /hosts/{id}/re-enroll — gated ONLY on recovery mode (the lost box has no old key). Rotates the host's API key to the new box's key (old box revoked) + returns the directive + opaque blobs.
  • GET /hosts/{id}/restore-directive (re-enrolled key, recovery-gated) — re-fetch.
  • The slice-7 escrow upload now also accepts the identity blob + non-secret directive (additive).

Store

  • hosts.recovery_mode_until; host_escrow.identity_blob + directive_json. Methods: SetRecoveryMode/ClearRecoveryMode, RotateHostAPIKey, SaveHostDRBundle/GetHostDRBundle.

Tests (green)

  • re-enroll refused without recovery mode (403); recovery-arm is global-key-only; re-enroll rotates + revokes (old key→401, new key→200); directive served only in recovery mode + expires; clear disables re-enroll.

Docs

  • Doc 03 §9 (10D done → SLICE 10 CLOSED) + the host-loss DR flow with the operator-side rotation model (hub orchestrates + read-only verifies; the operator deletes the stale connector + rotates the tunnel/PBS token from a trusted environment).

Deferred (non-blocking, per the locked model)

  • The Config DR/Recovery web UI (functional today via the recovery-mode admin API) + a small operator rotation CLI. No Cloudflare write-credential is in the hub by design.

Pending

  • Build + deploy hub v0.11.0 + agent v0.18.0; run the operator-in-the-loop DR drill (throwaway identity).