v0.106.0: offsite provisioning SLICE 2 — controller apply-bridge
On startup reconcile the hub-served offsite: descriptor into a key-only offbox target. internal/offsiteapply.Bridge: verify-pin box host key vs host_fingerprint (NO blind TOFU) → consume the one-time password (single-use, never logged) → sshpass ssh-copy-id -s -f install + verify → configure offbox → EscrowState=pending (fork-4 via Manager.ApplyOffsiteTarget) → persist a descriptor-hash marker LAST. Idempotent + fail-safe. Seams faked in tests; both red-proofs run+reverted. Dockerfile + sshpass. NOT yet live-applied (supervised end-to-end next runbook). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
This commit is contained in:
@@ -1,5 +1,34 @@
|
||||
## Changelog
|
||||
|
||||
### v0.106.0 — offsite provisioning SLICE 2: controller apply-bridge (2026-07-09)
|
||||
|
||||
Pairs with hub v0.38.0. On startup the controller reconciles the hub-served `offsite:` descriptor into a
|
||||
working key-only offbox target — closing the loop to a hands-off, hub-driven offsite target. (Auto-confirm =
|
||||
SLICE 3; soft-quota = SLICE 4.)
|
||||
|
||||
- **`internal/config`:** `OffsiteConfig` (`offsite:` section) mirroring the hub descriptor
|
||||
(enabled/type/host/user/port/repo_path/quota_gb/box_type/**host_fingerprint**) — deep-merged from `controller.yaml`.
|
||||
- **`internal/offsiteapply` (the apply-bridge):** `Bridge.Reconcile` — idempotent (a descriptor-hash marker
|
||||
at `<dataDir>/offbox/applied_marker` prevents re-consuming a spent password) and fail-safe (any step fails
|
||||
→ nothing persisted, retried next cycle). Flow: **scan + VERIFY the box host key against `host_fingerprint`
|
||||
(no blind TOFU)** → generate the controller keypair → **consume the one-time password**
|
||||
(`POST /api/v1/offsite/consume-password/{id}`, Bearer APIKey, single-use, never logged) → install the
|
||||
pubkey (`sshpass -e ssh-copy-id -p 23 -s -f`) + verify key auth → configure the offbox target →
|
||||
`EscrowState="pending"` (fork-4 enable path via `Manager.ApplyOffsiteTarget`) → persist the marker LAST.
|
||||
Seams (consume/scan/keygen/install/enable) so unit tests fake all I/O. A consumed-but-failed install logs a
|
||||
loud "password is spent — reset on the hub" signal.
|
||||
- **`internal/backup`:** `Manager.ApplyOffsiteTarget` reuses `WriteOffboxSecrets`/`SetOffboxTarget`/
|
||||
`PushOffboxPasswordForEscrow` → `EscrowState="pending"`; the escrow stage-push is best-effort (agent-down ≠ apply failure).
|
||||
- **`cmd/controller`:** wires the bridge (real seams — HTTP consumer, x/crypto/ssh host-key scanner, ed25519
|
||||
keygen, sshpass installer) and runs `Reconcile` async at startup (non-blocking; the config-refresh restart re-runs it).
|
||||
- **`Dockerfile`:** + `sshpass`.
|
||||
- Tests (faked seams): apply-end-to-end (pinned known_hosts + key + pending + marker + **pw-not-logged**);
|
||||
host-key mismatch → refuse **+ companion red-proof** (drop the verify → wrong key pinned → test fails);
|
||||
idempotent (marker match → no re-consume); install-fail → fail-safe **+ companion red-proof** (persist
|
||||
marker early → failed apply looks done → test fails).
|
||||
- **NOT yet live-applied** — the supervised end-to-end (hub provisions on the new pool box → controller
|
||||
consumes + installs + configures) is the next runbook, gated on the hub's new scoped `HETZNER_TOKEN`.
|
||||
|
||||
### v0.105.0 — fork-4: offsite password custody hand-off + atomicity gate + DR inject + DR coord (2026-07-09)
|
||||
|
||||
Pairs with agent v0.77.0 to make the restic-offsite repo password recoverable at DR (rides the customer-R
|
||||
|
||||
Reference in New Issue
Block a user