Skip to content

Account deletion: the user's own data, and the objects behind it #34

Description

@Devski

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

  • A signed-in user can delete their account from settings, behind a confirmation that requires re-entering the password
  • Deletion removes, in an order that cannot orphan anything: staged uploads and their objects, file rows and their objects (original and every variant), then the user — with the cascades doing the rest
  • A failure part-way through leaves the account intact rather than half-deleted; the objects can be removed again on retry
  • Every session for that user is invalidated
  • The public profile stops resolving, and the handle's redirects go with it
  • A confirmation e-mail is sent to the address, after the fact (A10 has no template for this yet — adding one is part of the work)
  • The same object-removal path is reusable for sweeping a pr-<n>/ preview prefix (PR preview deployments on the dev instance #31 leaves those deliberately)

Open decisions

Verification

  • Delete an account that has an avatar; confirm in the bucket that the original and both variants are gone, not merely unreferenced
  • Delete an account mid-flight with the storage unreachable; confirm the account still exists and works
  • Confirm the public profile returns 404 and the handle's redirect entries are gone

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    complianceLegal or regulatory obligation, not a product choice

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions