feat: orchestrate governed tenant erasure
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user