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_aton users (or a dedicatedapp.account_deletionstable 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 tsxsmoke: 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(bearerACCOUNT_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