docs(website): document account deletion core and MODULE_API 1.20.0

ACCOUNT_DELETION.md §6 step 2 is built (website feature/account-deletion).

- MODULE_API.md: 1.20.0, api.onUserDeleting (§1.1, §1.1 history, §2.4).
- BACKEND_DESIGN.md: reserved_usernames, DELETE /auth/me/account, the admin
  delete now running the same procedure, the demo lock row, and §6.6 on the
  procedure, the display rule and what goes and what stays.
- TEAMS.md: the forum's deleted-author rule is now "[deleted player]" for
  members and the stored name, marked, for staff.
- ACCOUNT_DELETION.md: AD12 (wrong password or code is 400, not 401) and
  AD13 (a deleted account drops off Forum guests), both asked of the org
  lead while building; §4.1 refusals carry codes; step 2 marked built.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
2026-10-29 18:34:28 -05:00
parent 3545e898af
commit 42c762cc13
4 changed files with 147 additions and 11 deletions

View File

@@ -1,7 +1,8 @@
# Account deletion — a member deletes their own account, on the website and in the app
**Status:** plan of record, 2026-10-29, with every question answered (AD4-AD11). Nothing built
yet. Each phase in §6 stops for the org lead before the next one starts.
**Status:** plan of record, 2026-10-29, with every question answered (AD4-AD13). **§6 step 2
(core) built 2026-10-29** as website `feature/account-deletion`, `MODULE_API` 1.20.0; steps 3-6 are
next. Each phase in §6 stops for the org lead before the next one starts.
**Why now.** Google Play's Data safety form asks whether the app lets people create an account, and
an app that does must let them delete it, both **in the app** and at a **web address** given to Play
@@ -73,6 +74,17 @@ player?"), the same day:
- A **protected staff account**, which the shard refuses to unlink (403), is logged and shown to
admins as *still linked on the shard*, for the shard's staff to clear by hand.
Asked while building core (§6 step 2), the same day:
- **AD12 — A wrong password or code is 400, not 401.** §4.1 first said 401. In the app, a 401 on a
signed-in call goes to `TokenAuthenticator`, which spends the refresh token and replays the request
with the same body: one typo would make two password attempts, both counted by the login backoff, and
a failed refresh would sign the member out mid-deletion. Every other self-service password check
already answers 400, so deletion does too, with `code: 'wrong_password'` or `'wrong_code'`.
- **AD13 — A deleted account drops off a Team's *Forum guests* list.** Its grant row stays (the id is
set NULL) and the staff grant ledger still shows it with the name, marked deleted. A grant is matched
by user id, so a deleted account cannot use it and is not a guest.
## 3. What exists today (read 2026-10-29, website `main` 222bcb0)
- **The admin route.** `DELETE /api/v1/admin/users/:id` refuses your own account and the last admin,
@@ -149,12 +161,12 @@ player?"), the same day:
- The typed `username` must match the account, ignoring case.
- The password is always checked (AD7).
- A two-factor code or a recovery code is checked when two-factor sign-in is on.
- Refusals:
- 400 for a username that doesn't match
- 401 for a wrong password or code
- Refusals, each with a `code`:
- 400 `username_mismatch` for a username that doesn't match
- 400 `wrong_password`, `code_required` or `wrong_code` (AD12: 400, never 401)
- 409 `password_required` for an account with no password: the screen points at the
Account page's set-a-password section, which already exists for that case
- 409 for the last admin
- 409 `last_admin` for the last admin
- the demo's own refusal for a demo account (`demo/mode.js`, the same lock other breaking
settings use)
- `#swagger.*` annotations; the spec is regenerated.
@@ -309,6 +321,15 @@ player?"), the same day:
2. **Core** (`website`): §4.1 and §4.2, `MODULE_API` 1.20.0, tests (the service's procedure, the
route's refusals, the demo lock, a module hook that throws, `displayAuthor` per table, a
reserved staff name), OpenAPI, `BACKEND_DESIGN.md`, `TEAMS.md`, `MODULE_API.md`. One PR.
**Built 2026-10-29.** Beyond the list: the section is on the staff Account page
(`AccountAdmin.jsx`) as well as the player one, since staff delete themselves too; the staff views
(Reports queue, Teams forum log, moderation requests, leader overrides, the forum itself) mark a
deleted account's stored name "(deleted account)"; a `transaction()` helper in `utils/db.js`. Walked
locally against a throwaway database: refusals, deletion on the web and over a bearer with 2FA, the
other device's session dying on its next request, staff names refused at admin create and at a
rename, a player name free at once, the admin route, the last admin refused, and the web screens
for a player and an admin. The forum's "[deleted player]" rendering and the demo lock are covered
by tests only; the walk in step 6 sees them with a game module and the demo flag on.
3. **Module-Rust** (`edge`) and **Module-uo**: §4.4's hooks plus their core-ref pins, one PR
each. Module-uo's PR adds `shard_pending_unlinks`, the retry, and the *still linked on the
shard* list. **Integration Kit**: the re-pin and the paragraph, one PR.