Runbook: user offboarding and deactivation
Deactivate
tv admin user deactivate person@example.com
Requires admin/global_admin, and follows the same hierarchy rule as re-roling (see Users, roles, and groups): a global_admin may deactivate anyone but themselves; an admin may deactivate only a user-role member, never a fellow admin, and never their own account.
Deactivating the sole active owner of a group that still has other active members is refused with GROUP_OWNER_DEACTIVATION_BLOCKED until ownership is transferred (tv group transfer) or an administrator recovers it — see Group owner recovery below.
What deactivation does, immediately, in one transaction
deactivate_identity (supabase/migrations/20260820163000_mvp004_identity.sql) does all of the following atomically:
- Flips the subject's profile status and organization-membership status to
deactivated. - Deletes their
auth.sessionsrows andauth.refresh_tokensrows — the browser (Supabase-provider) session is revoked immediately, not just left to expire. - Records one
identity.deactivatedaudit event with the acting administrator, the subject, and the organization.
What deactivation does not do directly — and why it doesn't need to
It does not walk the CLI's cli_access_tokens table and revoke each device token individually. Instead, every CLI-authenticating RPC (authenticate_cli_access_token and everything built on it) re-checks the caller's profile and organization-membership status live, on every call. A deactivated account's existing tv_ bearer token therefore stops working on its very next use — tv whoami, tv file find, everything — with ACCOUNT_DEACTIVATED, exactly as if the token had been individually revoked. There is no window where a deactivated user's CLI session keeps working because "revocation hasn't caught up yet."
What deactivation deliberately preserves
- Ownership. Files owned by a deactivated user keep their
owner_user_idunchanged. Nothing is reassigned or orphaned. An administrator can still reach that user's files via--admin-reason(see Audit and administration), and any existing grant a deactivated user created stays valid on its own terms until someone revokes it — deactivating the grantor does not retroactively revoke grants they already made. - History. Every audit event, metadata revision, and content version the user is attributed to remains exactly as it was. Deactivation is a status flip, never a deletion.
- The account row itself. There is no delete-user command. Re-activation is not currently exposed either (
tv admin user deactivatehas no inverse command) — if a deactivated user needs to return, that is presently a database-level exception, not a supportedtvworkflow; note this gap for the requester rather than improvising an unaudited fix.
Group owner recovery
tv admin user deactivate refuses with GROUP_OWNER_DEACTIVATION_BLOCKED when the subject is the sole active owner of a group that still has other active members. An owner must run tv group transfer <group> <email> first, handing sole ownership to another active member in one atomic step, before deactivation can proceed.
If a group is ever left with zero active owners, it enters an explicit recovery state (needsOwnerRecovery: true in tv admin group list). An administrator resolves it with:
tv admin group transfer <group> <email>
This promotes an existing active member to owner without demoting anyone else — it never creates membership and never applies to a group that already has an active owner. Both the deactivation block and the recovery transfer are audit-logged.
Verify
tv admin user list
tv admin audit list --target-type user --target-id <user-id>
Confirm the member now shows deactivated status, and that the identity.deactivated event appears with the correct actor and correlation ID for your offboarding record.
Continue with Users, roles, and groups or Incident investigation.