3.9 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; the scripts history lives in scripts/CHANGELOG.md.
Hub v0.61.0 + felhom-tenantsync v1.1.0 — Customer RESET (middle lifecycle tier) — 2026-07-17
Commits: 4009401 (code), e144c5e (manifest). Live: hub 0.61.0 on k3s (Synced/Healthy);
felhom-tenantsync v1.1.0 on ep0 (felhom-hetzner).
What shipped
The middle lifecycle tier between host delete and customer Delete. One operator action returns a
customer to pre-first-install: every OPERATIONAL trace dies — offsite repo (Hetzner sub-account/box),
PBS namespace + all backup groups + token, DR recipe, one-time secret, claim state, retained escrow
custody — while identity + basic config survive (customer_configs incl. the offsite tier CHOICE, all
provenance rows, and the audit-event stream).
Viktor's rulings, all implemented: (1) destroying retained escrow custody gets its own separate acknowledgment; (2) RESET clears claim state → fresh code next onboarding; (3) RESET refuses while any host row exists (delete hosts first — reset never deletes hosts); (4) the confirm surface shows a live-counted inventory.
Discipline
External teardown FIRST, DB purge LAST (publish-last). Every leg idempotent → a partial run is re-run from
the top; a failed external leg is a clean journal entry and the DB purge (which erases the descriptors that
say what still needs tearing down) is withheld until every external leg is ok. Provenance + events
are never wiped.
Components
- store/customer_reset.go:
customer_resetsjournal (per-attempt, per-leg, resumable);CustomerResetInventory(live counts incl. retained blobs via the F-14host_deletionsUNION);Start/UpdateResetLeg/Finish/LatestCustomerReset; ack-gatedPurgeCustomerResetDBState;DeleteClaim. - claim.ResetToUnclaimed: deletes the claim row →
EnsureIssuedmints a fresh first code next onboarding. - offsite:
Deprovision(idempotent label-lookup delete),OffsiteIdentifier,ClearProvisionedDescriptor. - tenantsync + scripts/felhom-tenantsync.sh v1.1.0:
deprovisionop destroys the namespace + backup groups + token; sharedfelhom@pbsuser untouched; idempotent. - web/customer_reset.go:
GET /configs/{id}/reset→ inventory JSON;POST→ orchestration (all gates before any write/external call). A distinct amber RESET card +reset_doneflash.
Verification
- Green gate:
go build ./... && go vet ./... && go test ./...all pass. Hub confirm gate OK. - Red-proofs (run-fail-revert, both proven red then restored): (a) escrow-ack gate — defeat it → reset proceeds & destroys blobs → FAIL; (b) partial-failure resumability — don't withhold the purge → DB purged despite an external failure → FAIL. Plus store ack-gating + journal round-trip; offsite Deprovision idempotency + descriptor clear; RESET-card render.
- Live drill on ep0 (throwaway
drill-reset-01, real PBS data): provision → real backup written into the namespace → deprovision (deleted:true; namespace + backup group + token destroyed) → token/ns confirmed gone → idempotent re-run (deleted:false) → all 3 real tenants + the shared user survived.
Not live-drilled (covered otherwise)
The web RESET POST and the Hetzner sub-account delete are covered by the httptest-driven orchestration
tests + offsite unit tests (the offsite Deprovision faithfully mirrors the live-proven
ReissueCredentials, swapping reset→delete). The hub UI is operator-password-gated → CC verifies the UI
via render tests, not a live click. A live Hetzner create/delete drill was not run (billed external
service; mirror-of-live-proven + idempotency-tested).