hub v0.61.0 + felhom-tenantsync v1.1.0: Customer RESET (middle lifecycle tier)
One operator action returns a customer to pre-first-install: all operational state dies (offsite repo, PBS namespace+backups, DR recipe, one-time secret, claim state, retained escrow custody); identity + basic config + provenance + events survive. Sits between host delete and customer Delete. - store/customer_reset.go: customer_resets journal, live inventory, ack-gated purge (never touches identity/provenance/events), DeleteClaim. - claim.ResetToUnclaimed: delete claim row -> fresh code next onboarding. - offsite.Deprovision (idempotent) + OffsiteIdentifier + ClearProvisionedDescriptor. - tenantsync.Deprovision + felhom-tenantsync.sh deprovision op (destroys ns + backup groups + token; shared user untouched; idempotent). - web/customer_reset.go: GET reset -> inventory JSON; POST -> orchestration (external teardown FIRST, DB purge LAST; refuse-while-hosts; typed-id + separate escrow ack). Amber RESET card distinct from red Danger-zone Delete. - Red-proofs: ack-gate + partial-failure resumability (both proven red); store ack-gating + journal round-trip; offsite idempotency + descriptor clear; RESET-card render. Green: build + vet + test.
This commit is contained in:
@@ -1,5 +1,43 @@
|
||||
# Felhom Hub — Changelog
|
||||
|
||||
## v0.61.0 — Customer RESET: the middle lifecycle tier (2026-07-17)
|
||||
|
||||
One operator action returns a customer to **pre-first-install**: every OPERATIONAL trace dies (offsite
|
||||
repo, PBS namespace + backups, DR recipe, one-time secret, claim state, retained escrow custody), while
|
||||
**identity and the basic config survive** (the `customer_configs` row, all provenance rows, and the
|
||||
audit-event stream). It sits between the two existing tiers — *host delete* (< RESET) and *customer
|
||||
Delete* (> RESET, the one true purge point). Viktor's rulings: (1) destroying retained escrow custody
|
||||
gets its **own** separate acknowledgment; (2) RESET clears claim state (a 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.
|
||||
|
||||
Orchestration discipline (spec §3): external teardown FIRST, DB purge LAST (publish-last), every leg
|
||||
idempotent → a partial run is simply 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.
|
||||
|
||||
- **Store** (`internal/store/customer_reset.go`, new): `customer_resets` journal table (per-attempt,
|
||||
per-leg status, resumable); `CustomerResetInventory` (live counts: hosts, retained blobs via the
|
||||
F-14 `host_deletions` UNION, dr_recipe/one-time-secret/claim presence); `Start/UpdateResetLeg/Finish/
|
||||
LatestCustomerReset`; `PurgeCustomerResetDBState` (ack-gated escrow-blob delete + one-time-secret,
|
||||
dr_recipe, log bundles — never touches identity/provenance/events); `DeleteClaim` primitive.
|
||||
- **Claim** (`internal/claim/engine.go`): `ResetToUnclaimed` DELETES the claim row so `EnsureIssued`
|
||||
mints a fresh first code on the next onboarding (no parallel revoked-flag, no stale generation).
|
||||
- **Offsite** (`internal/offsite/offsite.go`): `Deprovision` DELETES the labelled sub-account/box
|
||||
(idempotent — label-lookup, `len==0` = already gone); `OffsiteIdentifier` (preview name);
|
||||
`ClearProvisionedDescriptor` (keeps the tier CHOICE `enabled/type/quota/box_type`, drops every
|
||||
provisioned field). **PBS** (`internal/tenantsync/client.go` + `scripts/felhom-tenantsync.sh`
|
||||
`deprovision` op): destroys the customer's namespace + all backup groups + token; the shared
|
||||
`felhom@pbs` user is never touched; idempotent.
|
||||
- **Web** (`internal/web/customer_reset.go`, new): `GET /configs/{id}/reset` → live inventory JSON;
|
||||
`POST` → the orchestration (precondition + typed-id + escrow-ack gates BEFORE any write/external
|
||||
call). A distinct **amber** RESET card on the customer page (separate from the red Danger-zone
|
||||
Delete), with the typed-id confirm + the separate escrow-custody ack row.
|
||||
- **Red-proofs**: ack-gate (defeat → reset proceeds & destroys blobs → FAIL); partial-failure
|
||||
resumability (purge-not-withheld → DB purged despite external failure → FAIL); both proven red then
|
||||
restored. Plus store ack-gating, journal round-trip, offsite Deprovision idempotency + descriptor
|
||||
clear, and the RESET-card render test. Green: `go build ./... && go vet ./... && go test ./...`.
|
||||
|
||||
## v0.60.1 — host deletion DEMOTES escrow custody (never destroys) + S6b obsolete (2026-07-17)
|
||||
|
||||
Closes the deletion-path gap in v0.60.0's review: `DeleteHost(deleteEscrow=true)` was still DELETING
|
||||
|
||||
Reference in New Issue
Block a user