Goal
A signed-in user can delete their own account, and doing so removes their personal data — including the files in the bucket, which nothing currently does.
Why now
Found on 05.09.2026 while clearing test accounts from dev: there is no code path for this at all. Removing an account meant hand-written SQL over an SSH session, and even then the objects were left behind in the bucket, unreferenced and unfindable — the keys are derived from a content hash stored in the row that was just deleted.
The schema already anticipates this. files.user_id and pending_uploads.user_id are ON DELETE RESTRICT on purpose, with the reason written next to them:
restrict, not cascade: file rows reference content-addressed S3 objects (G2) — deleting a user must go through application code that removes the objects first, otherwise they become unfindable orphans.
That application code is what is missing. Everything else about a user (profiles, sessions, accounts, two_factors, handle_redirects) already cascades.
With real users this stops being tidiness. A person asking to have their data removed has a legal right to it, and "the developer runs some SQL" is not an answer that survives being asked twice.
Acceptance criteria
Open decisions
Verification
Dependencies
None blocking. Related: #12/#14 (the objects), #15/#16 (the handle), #31 (shares the object-removal path).
Spec: SPEC.md G2, A10 · Size: M · Label: enhancement
Goal
A signed-in user can delete their own account, and doing so removes their personal data — including the files in the bucket, which nothing currently does.
Why now
Found on 05.09.2026 while clearing test accounts from dev: there is no code path for this at all. Removing an account meant hand-written SQL over an SSH session, and even then the objects were left behind in the bucket, unreferenced and unfindable — the keys are derived from a content hash stored in the row that was just deleted.
The schema already anticipates this.
files.user_idandpending_uploads.user_idareON DELETE RESTRICTon purpose, with the reason written next to them:That application code is what is missing. Everything else about a user (
profiles,sessions,accounts,two_factors,handle_redirects) already cascades.With real users this stops being tidiness. A person asking to have their data removed has a legal right to it, and "the developer runs some SQL" is not an answer that survives being asked twice.
Acceptance criteria
pr-<n>/preview prefix (PR preview deployments on the dev instance #31 leaves those deliberately)Open decisions
Verification
Dependencies
None blocking. Related: #12/#14 (the objects), #15/#16 (the handle), #31 (shares the object-removal path).
Spec: SPEC.md G2, A10 · Size: M · Label: enhancement