Skip to content

Plan: CreatorTrack self-serve account deletion with 90-day retention

URL: https://mkdocs.justinsforge.com/memory/plans/creatortrack-account-deletion-2026-07-10/

Date: 2026-07-10 Approved spec: Settings gains an Account section with "Delete account". Flow: type DELETE + password re-entry (or Google re-auth) -> a confirmation email (Resend, from no-reply@) with a signed 24h link -> clicking it executes deletion: all sessions revoked, login blocked, Google/social OAuth credentials revoked at the provider and deleted from api_connections immediately, account row marked deleted_at (soft). Deletion is REFUSED while the user owns any workspace that has other members; the UI explains and links to transfer-ownership/remove-members. Solo-owned workspaces soft-delete with the account. Record retained 90 days (restore possible via contact@ support path: an admin-side unmark script), then a daily purge job hard-deletes accounts past 90 days (users row, workspaces they solo-owned, onboarding responses, tokens, collection data via existing cascade/purge helpers). Purge runs via a bearer-token job route (pattern of /api/finance/jobs) driven by a systemd timer on Console. Privacy policy section 7 updated: deactivated immediately, permanently deleted within 90 days. PostHog events: account_delete_requested, account_delete_confirmed, account_restored, account_purged. Out of scope: self-serve restore UI; data export/takeout; workspace ownership-transfer UI improvements (existing sharing controls suffice); GDPR data-export tooling.

Task list

Task 1: Migration: soft-delete columns

  • Files: db/migrations/<stamp>_account_deletion.sql, db/schema.ts
  • What: deleted_at, delete_confirm_token_hash, delete_requested_at on users (or a dedicated app.account_deletions table if users is core-schema; follow existing conventions), idempotent DDL + RLS consistent with neighbors.
  • Verification: migration applies twice clean on the DB clone.
  • Commit: feat(account): deletion columns migration

Task 2: Domain lib

  • Files: lib/account-deletion.ts
  • What: request (validates ownership-block rule, mints signed token, sends confirm email via lib/email.ts), confirm (executes soft delete: revoke sessions, revoke+delete OAuth connections including Google token revocation endpoint, stamp deleted_at), restore (admin script path), purge candidates query.
  • Verification: npx tsx smoke: request for a user owning a shared workspace throws the block error; solo user proceeds; confirm stamps and wipes connections.
  • Commit: feat(account): deletion domain lib

Task 3: API routes + login gate

  • Files: app/api/account/delete/route.ts (POST request, GET/POST confirm), app/api/account/jobs/route.ts (bearer ACCOUNT_JOBS_TOKEN, purge action), auth callback/login check for deleted_at (block sign-in with clear message)
  • What: Session-authed request with password/Google re-auth; signed 24h confirm link lands on a minimal confirm page; jobs route mirrors /api/finance/jobs pattern, cookie-exempt in proxy.ts.
  • Verification: curl walkthrough on dev slot: request -> email payload logged/sent -> confirm -> login blocked -> jobs purge with backdated deleted_at removes the rows.
  • Commit: feat(account): deletion + purge routes, login gate

Task 4: Settings UI

  • Files: components/profile/SettingsModal.tsx (Account section), components/account/DeleteAccountDialog.tsx
  • What: Danger-zone card, type DELETE + password (or Google re-auth) dialog, ownership-block state with guidance, post-request "check your email" state. Match app design language.
  • Verification: dev-slot walkthrough both states (blocked owner, solo user full flow).
  • Commit: feat(account): settings delete-account UI

Task 5: Purge timer (Console)

  • Files: /home/justinwieb/forge/scripts/forge_creatortrack_account_purge.sh, systemd unit+timer files under forge conventions (write units, do NOT enable; main session enables at merge)
  • What: Daily curl of the jobs route with token from ~/.forge-secrets/creatortrack-jobs.env (create/extend per existing job-token file conventions), fail-loud notify on error per forge notify patterns.
  • Verification: manual script run against dev slot purges a backdated test account.
  • Commit: feat(account): purge timer script + units

Task 6: Privacy policy update

  • Files: app/(marketing)/privacy/page.tsx
  • What: Section 7 reworded: deletion request or in-app delete deactivates immediately; permanent deletion within 90 days; restore window via contact@.
  • Verification: page renders; wording consistent with flow.
  • Commit: docs(privacy): 90-day deletion retention

Task 7: PostHog events + typecheck/lint/e2e

  • What: events per spec; tsc --noEmit + eslint clean; full dev-slot e2e (request, confirm, blocked login, purge, restore script).
  • Commit: fixups only

Task 8: Register and document

  • Files: /home/justinwieb/forge/memory/general/reference_creatortrack_account_deletion.md, /home/justinwieb/forge/MEMORY.md
  • What: Topic file (flow, token env var, timer name, restore procedure) + index line.
  • Commit: docs(memory): account deletion reference