94 lines
4.7 KiB
Markdown
94 lines
4.7 KiB
Markdown
# GovOPlaN Tenancy Module Boundary
|
|
|
|
`govoplan-tenancy` owns tenant registry, tenant settings, tenant lifecycle, and
|
|
tenant-context route contributions.
|
|
|
|
## Owned Routes
|
|
|
|
- `/api/v1/admin/tenants*`: tenant registry, delta feed, owner candidates,
|
|
settings, deletion plans, retirement, and guarded empty-tenant destruction.
|
|
- `/api/v1/tenancy/switch-tenant`: tenant-owned route surface for interactive
|
|
tenant context switching.
|
|
|
|
`/api/v1/auth/switch-tenant` remains in `govoplan-access` for API
|
|
compatibility, but both routes delegate to the same
|
|
`auth.tenantContextSwitcher` capability.
|
|
|
|
## Lifecycle Rules
|
|
|
|
Tenant retirement is non-destructive. It marks the tenant inactive and stores
|
|
lifecycle metadata in tenant settings.
|
|
|
|
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
|
|
so destructive deletion cannot silently miss their rows.
|
|
|
|
Delete veto providers are registered through module manifests and receive
|
|
`(session, tenant_id, resource_id)`. Providers should return a structured
|
|
`DeleteVetoIssue`, a list of issues, or `None`; legacy providers that raise an
|
|
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
|
|
lifecycle event names and payload shape. Modules that need to react to tenant
|
|
lifecycle changes should depend on the event type strings or the emitted audit
|
|
events, not on tenancy API route internals.
|
|
|
|
The stable lifecycle event names are:
|
|
|
|
- `tenant.created`: tenant registry entry was created and owner membership
|
|
provisioning was requested.
|
|
- `tenant.suspended`: tenant was marked inactive through the admin lifecycle
|
|
route.
|
|
- `tenant.resumed`: tenant was reactivated through the admin lifecycle route.
|
|
- `tenant.deletion_requested`: retirement or destructive erasure was requested
|
|
after lifecycle planning passed.
|
|
- `tenant.erasure_completed`: destructive tenant deletion completed.
|
|
|
|
Legacy audit actions such as `tenant.updated`, `tenant.retired`, and
|
|
`tenant.destroyed` can still be emitted for compatibility. New module behavior
|
|
should key off the explicit lifecycle events above.
|
|
|
|
Lifecycle event details use concrete tenant identifiers plus optional actor,
|
|
reason, count, and mode information. Destructive erasure is only emitted after
|
|
the tenant row is successfully scheduled for deletion in the same transaction.
|