Files
govoplan-tenancy/docs/TENANCY_MODULE_BOUNDARY.md
T
zemion bfc38a7bc9
Module Package Release / publish-packages (push) Successful in 12s
feat: cover tenant erasure evidence in DSAR
2026-08-24 16:07:38 +02:00

102 lines
5.2 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.
The module's `privacy.dsar.tenancy` provider returns bounded requester and
approver roles, approval timestamps, operation state, and an unfinished
request reason only to the corroborated account selector. These actor and
checkpoint references are immutable authorization, separation-of-duties, and
recovery evidence, so the provider returns an explicit non-executable retain
action. Typed confirmation and credentials are never persisted; free-text
reason is removed when the tenant-erasure operation completes.
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.