Offboarding#
A person leaves in one merge request, on the day they leave. Everything below the first step happens on its own; the rest of the page is what to check and what the automation cannot know. The manager opens the merge request; a member of infra merges and applies. Together: fifteen minutes.
Same day#
In the directory (https://id.profuse.ro/if/admin → Directory → Users → the person): clear Is active, and remove them from every group. Do not delete the account — it keeps their history and the audit trail; deletion is a later, separate decision (see "Later"). That single change is what closes everything else, and it takes effect immediately: no merge request, no apply, nothing to wait for.
Cloudflare sessions: the apply disables the account and SCIM tells Access, which revokes the person's sessions and WARP registration within minutes. Check under Zero Trust → My Team → Users → the person: status revoked. If it is not (SCIM not yet configured, or in doubt), revoke by hand there, or:
curl -X POST -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" -H "Content-Type: application/json" \ https://api.cloudflare.com/client/v4/accounts/fa3fedaca96d6834e5166640e2aee819/access/organizations/revoke_user \ -d '{"email":"<person>@profuse.ro"}'sv1 (only if the person was in
infra): open shells survive the directory change. On sv1:sudo sss_cache -u <username>; sudo pkill -KILL -u <username>The account disappears from
getent passwdat once (onlyak-active=TRUEaccounts are visible).PostgreSQL:
pg-directory-syncdrops the role within five minutes. A role with an open session cannot be dropped, so end them first if the person may be connected:SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE usename = '<username>';on prod and staging, then
sudo systemctl start pg-directory-syncand read its journal.Mail:
mailcow-directory-syncswitches the mailbox off within fifteen minutes (IMAP, SMTP and app passwords stop; nothing is deleted). Run it now if you want it now:sudo systemctl start mailcow-directory-sync. The manager decides forwarding and the auto-reply — set them in mailcow on the inactive mailbox — and the retention date.GitLab:
gitlab-directory-syncblocks the account and drops it from both groups within five minutes (sudo systemctl start gitlab-directory-syncif you want it now). Issues and merge requests keep their author. Transfer anything the person owned alone: personal access tokens die with the block, but a project or a schedule owned by them keeps running under a blocked owner — reassign it. An instance administrator is never blocked by the sync: it says so and leaves the decision to you.
What the automation cannot know#
- Secrets the person could read. Rotate every credential they had access to, the same day: for
developers, the staging.envvalues they could see in a deploy log or on the box; forinfra, additionally the authentik bootstrap token (/srv/authentik/.env, then the CI variableAUTHENTIK_TOKEN), the mailcow API key, the Cloudflare API tokens (infra-tofu-*), the GitLabpeople-provisionerandRELEASE_TOKENtokens, the restic/R2 credentials. The list of CI variables onprofuse/infraandguestgetis the checklist; each one is a decision. - Devices. Their WARP device is revoked with the user; a company laptop is collected and wiped; passkeys on a personal device become useless once the account is disabled.
- Things outside the directory. Cloudflare account membership, GitLab instance admin, registrar and hosting logins, Stripe, Channex, ANAF/SDI portals, the password vault: check each for the person's e-mail. The quarterly review (
docs/access-review.md) will catch a miss, but a leaver should not wait for it.
Later#
- Retention over (the date the manager set, default 90 days): delete the account in the directory, after exporting anything the company keeps from the mailbox. The GitLab account stays blocked rather than deleted, so its history survives.
- Coming back: tick Is active again and put back their roles. The account, the database role, GitLab and the mailbox return on their own; the person sets a new password through a reset link (
ak-recovery-link).