# 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.` 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.