hub v0.37.0: offsite provisioning SLICE 1 — Cloud-API client + provisioning core

Hetzner storage-box provisioning against api.hetzner.com/v1 (NOT .cloud).
internal/hetznerapi (typed client + CloudAPI interface + Fake + WaitAction);
internal/offsite (Provisioner.ProvisionOffsite — idempotent by label, shared
sub-account/dedicated box, transient password, non-secret Descriptor,
fail-closed); one_time_secrets store (single-use Save/Consume); POST
/offsite/consume-password/{id} (customer-key auth, once); config-form Offsite
section → applyOffsite (502+no-save on error) → descriptor in ConfigJSON →
version bump. Token/passwords never logged/committed/in ConfigJSON. Tested vs a
faked Cloud API + fail-closed red-proof. NOT yet live-provisioned (needs the
dedicated-project scoped token; current token can delete ep0).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
This commit is contained in:
2026-07-09 18:38:24 +02:00
parent 996d403248
commit 44ec06b50f
16 changed files with 1279 additions and 1 deletions
+12
View File
@@ -3,6 +3,18 @@
> Created with the REUSE.md rollout (2026-07-03). Authoritative history: `hub/CHANGELOG.md` (hub),
> `website/CHANGELOG.md`, `scripts/CHANGELOG.md`; end-of-task detail in `REPORT.md`.
- **2026-07-09 — hub offsite provisioning SLICE 1 (hub v0.37.0).** The hub can now provision the offsite tier
on operator enable: `internal/hetznerapi` (typed client, base **api.hetzner.com/v1**, `CloudAPI` interface +
exported `Fake`, `WaitAction`), `internal/offsite` (`Provisioner.ProvisionOffsite` — idempotent by label
`felhom-customer=<id>`, shared sub-account / dedicated box, generates the transient password, builds the
NON-SECRET descriptor, **fail-closed**), `one_time_secrets` store (single-use `Save/ConsumeOneTimeSecret`),
`POST /offsite/consume-password/{id}` (customer-API-key auth, once), config-form Offsite section →
`applyOffsite` (502 + no-save on error) → descriptor in ConfigJSON → ConfigVersion bump. Token from
`HETZNER_TOKEN` (out-of-band); password NEVER in ConfigJSON/logs. Unit-tested vs a faked Cloud API (+ a
fail-closed red-proof). **NOT yet live-provisioned** — awaiting the **dedicated-project scoped token** (the
current token can delete ep0). NEXT: SLICE 2 (controller apply-bridge consuming the descriptor + one-time
password), SLICE 3 (escrow auto-confirm), SLICE 4 (soft-quota). Deployed via GitOps.
- **2026-07-09 — Hetzner API provisioning spike (offsite tier).** Both offsite models are API-provisionable
(FIT). **#1 correction: the Storage Box API is `https://api.hetzner.com/v1`, NOT `api.hetzner.cloud/v1`**
(Cloud API 404s for storage boxes); same Bearer token. Proven live on `spike-*` (torn down): sub-account