feat: orchestrate governed tenant erasure

This commit is contained in:
2026-08-24 15:57:21 +02:00
parent 9ed32618ea
commit e412c7d1bd
15 changed files with 1791 additions and 72 deletions
+35 -4
View File
@@ -19,10 +19,32 @@ compatibility, but both routes delegate to the same
Tenant retirement is non-destructive. It marks the tenant inactive and stores
lifecycle metadata in tenant settings.
Destructive deletion is intentionally narrow: it is allowed only when the
tenant is not the caller's active tenant and all registered tenant-owned counts
are zero. Populated tenants must be retired first or cleaned explicitly by
their owning modules before physical deletion.
The compatibility `DELETE /api/v1/admin/tenants/{tenant_id}` route is
non-destructive retirement only. Requests with `mode=destroy` fail with a link
to the governed erasure-operation API; even an apparently empty scope must not
bypass recent authentication, typed confirmation, approval, and durable
evidence.
Populated-tenant erasure uses `/erasure-operations` instead. A durable
operation stores a non-secret, digest-bound provider preview, policy snapshot,
distinct approvals, and per-step checkpoints. The safe default production
policy requires two distinct approvals, a preview no older than fifteen
minutes, recent interactive authentication, the dedicated
`system:tenants:erase` permission, and exact tenant-slug confirmation. The
policy is configurable through `tenant_erasure_policy` in system settings;
production profiles cannot reduce approval below two.
Authorized system administrators read and update it through
`GET/PATCH /api/v1/admin/tenant-erasure-policy`; policy changes require both
`system:tenants:erase` and `system:settings:write` plus recent interactive
authentication.
Execution verifies the preview again, suspends tenant access, and commits a
checkpoint before each module effect. A timeout or unknown/pending outcome
stops for reconciliation using the same provider idempotency key. Cancellation
is available only before destructive work starts. The Core scope is deleted
only after providers finish, a fresh inventory is clear, and delete vetoes and
tenant counts are zero. The durable operation then removes its free-text reason
and never stores typed confirmation, secrets, or erased tenant content.
Tenant lifecycle planning uses registered tenant summary providers and delete
veto providers. Modules that own tenant-scoped data must contribute summaries
@@ -35,6 +57,15 @@ exception are treated as blocking module vetoes. Tenancy exposes those issues in
the deletion plan with module attribution and resource details, so operators can
see which module blocks or qualifies the lifecycle action.
Core's `tenancy.erasure_provider.<module_id>` contract supplies module-owned
resource dispositions, irreversible warnings, ordered steps, idempotent
execution, and outcome reconciliation. An installed module with nonzero tenant
summary counts but no erasure provider explicitly blocks the operation. A
module that declares neither a tenant summary nor an erasure provider is
reported as outside tenant-persistence scope rather than silently executed.
Access provides the first concrete contribution and retains shared global
accounts and identities while erasing target-tenant authorization records.
## Lifecycle Events
`govoplan-tenancy.backend.lifecycle` is the module-local contract for tenant