Skip to content

URL: https://mkdocs.justinsforge.com/memory/general/reference_creatortrack_account_deletion/

CreatorTrack account deletion (90-day retention)

Built 2026-07-10 on branch feat/account-deletion (worktree ~/forge-suite-wt/feat/account-deletion, DB clone ct_feat_account_deletion). Plan: creatortrack-account-deletion-2026-07-10. Merged to main 2026-07-11 (merge commit 0c88ef6) and deployed to prod; its migration (20260711T000228) has been applied to prod.

Flow

  1. Settings, Account, danger zone: type DELETE plus password re-entry (password accounts only; Google-only accounts rely on session + the emailed link). POST /api/account/delete.
  2. REFUSED (409, inline guidance) while the user owns any workspace with other members. Solo-owned workspaces soft-delete with the account.
  3. Confirmation email (Resend, branded accountDeletionEmail) carries a single-use 24h token to the public page /account/delete/confirm?token=...; the button POSTs /api/account/delete/confirm (never GET side effects; scanners prefetch).
  4. Confirm: revokes Google refresh tokens at oauth2.googleapis.com/revoke (best-effort, logged loud, never aborts), wipes app.api_connections, stamps core.users.deleted_at, deletes all core.auth_session rows, soft-deletes solo-owned non-pinned workspaces.
  5. Sign-in gates: credentials (loginAction) and Google (auth.ts callbacks.signIn -> /login?error=account_deleted) both show "This account has been deleted. To restore it within 90 days..., email [email protected]." core.login also excludes deleted accounts.
  6. Purge: daily timer curls POST /api/account/jobs {"job":"purge"} (bearer ACCOUNT_JOBS_TOKEN); core.account_delete_purge (owner-GUC gated) hard-deletes accounts past 90 days plus workspaces where the deleted user was the sole member. Pinned workspaces are excluded at every step (soft-delete AND purge), so workspace 67 can never be touched.

SQL (migration 20260711T000228)

core.users gains deleted_at, delete_requested_at, delete_confirm_token_hash. Functions: account_delete_blockers/request (user GUC), account_delete_pending/confirm (token-authed), account_delete_status (email gate), account_delete_restore (lifeos_admin ONLY, EXECUTE revoked from app role), account_delete_purge (core.is_owner() gated).

Ops

  • Token: ACCOUNT_JOBS_TOKEN in ~/.forge-secrets/creatortrack-auth.env (generated 2026-07-10; prod service must restart with it at merge).
  • Purge timer: forge/scripts/forge_creatortrack_account_purge.sh + forge-creatortrack-account-purge.{service,timer}.unit (04:30 CT daily). Written, still NOT enabled post-merge: copy units to /etc/systemd/system/, systemctl daemon-reload && systemctl enable --now forge-creatortrack-account-purge.timer.
  • Restore (contact@ support path): bash forge/scripts/forge_creatortrack_account_restore.sh <email> (prod db lifeos default; pass a clone name for dev). Fails loud past 90 days; replay fails loud; fires PostHog account_restored.
  • PostHog events: account_delete_requested, account_delete_confirmed (server HTTP capture via lib/posthog-server.ts), account_purged, account_restored.
  • Dev-slot e2e trick: with WORKSPACE_DEV_AUTH=1 the request route logs the confirm link to the dev log; set [email protected] temporarily to exercise logged-out signup/login UI on a dev slot.

[Claude Code]