feat(hub): v0.69.0 — customer DELETE is the guided full-teardown cascade (R-25b)

POST /configs/{id}/delete now runs hosts -> RESET -> purge behind three
acknowledgements, a typed customer-id, a stale-preview check and the
ONLINE-host refusal (every gate before any write, so a refusal has zero
side effects). The shallow handleConfigDelete is gone.

Two invariants are asserted, not just commented: ruling 3 is preserved by
construction (leg 2 never sees a host row) and retained escrow custody is
purged exactly once, in leg 3 (leg 2 runs with purgeEscrow=false).

handleCustomerReset's committed half was extracted as commitCustomerReset;
the standalone RESET path is byte-identical to v0.68.1 and its suite is
untouched. Five red-proofs run.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J55BQE1gE2V4ffud5jweGS
This commit is contained in:
2026-07-21 19:31:48 +02:00
parent f59aa97d0c
commit 61dbd870c3
13 changed files with 1087 additions and 208 deletions
@@ -171,4 +171,27 @@ This is the friction lens: the customer's hands on the keyboard, the operator's
degrade after the first.
- Post-rehearsal checklist item: rebuild the golden to the current controller before the FIRST
real tester (fresh boxes shouldn't sit in a stale window longer than needed), and re-verify the
Day-0 manifest after every controller release during alpha.
Day-0 manifest after every controller release during alpha.
## G. Offboarding — which lever to pull (hub v0.69.0, R-25b)
Two levers, and they are not interchangeable:
- **Ügyfél-visszaállítás (RESET)** — *identity-preserving re-onboarding*. Every operational trace
dies (offsite repository, PBS namespace, DR recipe, one-time password, claim state); the customer
record, the basic config and the audit history survive. This is Peti's Friday flow: the same
customer coming back on new hardware. RESET still **refuses while any host row exists** — delete
the host(s) first, on purpose.
- **Delete customer… (the DELETE cascade)** — *full teardown, the customer is leaving*. One guided
dialog shows the live inventory, takes **three acknowledgements** (1: the host(s) will be deleted,
custody demoted not destroyed; 2: the customer will be RESET incl. external teardown and offsite
data destruction; 3: the record and ALL escrow ciphertext are purged) plus the **typed
customer-id**, then runs `hosts → RESET → purge` in that order.
Notes for the operator:
- An **ONLINE host refuses the whole cascade** — decommission the box first (a live agent would get
permanent 401s). The dialog says so and disables the button.
- If a leg fails (a slow Hetzner call), **nothing is left half-forgotten**: the journal is retained,
the error names the leg, and re-opening the dialog offers **Resume**. Completed legs are no-ops.
- The **audit event stream survives** the delete — the customer_deleted event and the host-deletion
provenance outlive the record, by design.