docs: record M8 step 2 (data safety) and plan self-service account deletion #391

Merged
whitlocktech merged 5 commits from docs/m8-data-safety-built into main 2026-10-29 17:33:24 -05:00
Member

Two things for M8 (android/PLAN.md). No code.

1. Step 2, data safety, built

Built as runicgateway.com#44. Everything the app sends is declared collected, not shared, encrypted in transit and required, after an audit of data/api/ on edge.

2. Account deletion, planned: website/ACCOUNT_DELETION.md (new)

The app starts accounts: the sign-up link, and in-app game-account creation. So Play requires a deletion path both in the app and at a web address, and the platform had none. The org lead chose self-service deletion, ahead of report/block/hide (AD1-AD3).

  • One deletion service for the admin route and a new DELETE /api/v1/auth/me/account. It checks the password, plus a 2FA code if 2FA is on, and the typed username. It refuses the last admin and the demo accounts.
  • What already happens: of the 59 foreign keys to users, those for credentials and sessions cascade and authored content detaches. Sessions die at once, because requireAuth re-reads the user.
  • The answers to the six questions (AD4-AD9):
    • delete at once
    • what they wrote stays, and members see every username snapshot as "[deleted player]"
    • staff keep seeing the name and the IP for moderation, so the stored rows are untouched and one displayAuthor(row, viewer) helper decides what each viewer sees
    • the password is always the proof
    • a UO game account stays on the shard
    • a deleted staff username is never available again (reserved_usernames)
  • Deletion unlinks through each module's own unlink (AD10, AD11):
    • The hook: MODULE_API 1.20.0, api.onUserDeleting. It runs before the delete, has a time limit, and can't stop the deletion.
    • Why not just cascade: a cascade isn't an unlink. For UO, the site table is only a mirror and the shard keeps the link, so a cascade alone would leave the shard attributing the account to a deleted user.
    • Rust calls links.unlinkOwned for each Steam id.
    • UO calls the sidecar's DELETE /link/{account} for each game account, as the staff unlink does.
    • UO, shard offline: the unlink waits in shard_pending_unlinks and is retried on reconnect.
    • UO, protected staff account (403): listed for admins as still linked on the shard.
  • Screens on the web and in the app, and the runicgateway.com /delete-account/ page as Play's URL.

Knock-on edits

  • M8's order is now: push → data safety → account deletion → report/block/hide → cutover.

  • USER_SAFETY.md's reportTargets moves to MODULE_API 1.21.0.

  • The README lists the new plan.

  • AI-assisted (Claude Code)

🤖 Generated with Claude Code

https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY

Two things for M8 (`android/PLAN.md`). No code. ## 1. Step 2, data safety, built Built as `runicgateway.com#44`. Everything the app sends is declared collected, not shared, encrypted in transit and required, after an audit of `data/api/` on `edge`. ## 2. Account deletion, planned: `website/ACCOUNT_DELETION.md` (new) The app starts accounts: the sign-up link, and in-app game-account creation. So Play requires a deletion path both in the app and at a web address, and the platform had none. The org lead chose self-service deletion, ahead of report/block/hide (AD1-AD3). - **One deletion service** for the admin route and a new `DELETE /api/v1/auth/me/account`. It checks the password, plus a 2FA code if 2FA is on, and the typed username. It refuses the last admin and the demo accounts. - **What already happens:** of the 59 foreign keys to `users`, those for credentials and sessions cascade and authored content detaches. Sessions die at once, because `requireAuth` re-reads the user. - **The answers to the six questions (AD4-AD9):** - delete at once - what they wrote stays, and members see every username snapshot as **"[deleted player]"** - staff keep seeing the name and the IP for moderation, so the stored rows are untouched and one `displayAuthor(row, viewer)` helper decides what each viewer sees - the password is always the proof - a UO game account stays on the shard - a deleted **staff** username is never available again (`reserved_usernames`) - **Deletion unlinks through each module's own unlink (AD10, AD11):** - **The hook:** `MODULE_API` 1.20.0, `api.onUserDeleting`. It runs **before** the delete, has a time limit, and can't stop the deletion. - **Why not just cascade:** a cascade isn't an unlink. For UO, the site table is only a mirror and the shard keeps the link, so a cascade alone would leave the shard attributing the account to a deleted user. - **Rust** calls `links.unlinkOwned` for each Steam id. - **UO** calls the sidecar's `DELETE /link/{account}` for each game account, as the staff unlink does. - **UO, shard offline:** the unlink waits in `shard_pending_unlinks` and is retried on reconnect. - **UO, protected staff account (403):** listed for admins as *still linked on the shard*. - Screens on the web and in the app, and the runicgateway.com `/delete-account/` page as Play's URL. ## Knock-on edits - M8's order is now: push → data safety → **account deletion** → report/block/hide → cutover. - `USER_SAFETY.md`'s `reportTargets` moves to `MODULE_API` **1.21.0**. - The README lists the new plan. - [x] AI-assisted (Claude Code) 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
wtclaude added 1 commit 2026-10-29 08:21:22 -05:00
runicgateway.com#44 declares everything the app sends as collected,
not shared, encrypted in transit and required, after an audit of
data/api/ on edge. Account deletion is left open for the org lead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
wtclaude added 1 commit 2026-10-29 17:09:00 -05:00
The org lead chose self-service deletion on the website and in the app,
with a runicgateway.com page as Play's deletion URL. Planned separately,
before the cutover.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
wtclaude added 1 commit 2026-10-29 17:18:09 -05:00
ACCOUNT_DELETION.md is the plan of record for the org lead's decision:
a member deletes their own account on the website and in the app, as
Google Play requires of an app that starts accounts.

- One deletion service for the admin route and the member's own route,
  with a confirm step, the last-admin and demo refusals, clearing the
  username snapshots on what the member wrote, and a notice by email.
- MODULE_API 1.20.0: api.onUserDeleted, which runs after the commit and
  can't stop a deletion.
- Screens on the web and in the app; runicgateway.com /delete-account/
  as the address Play is given.
- What the schema already does (59 keys: credentials cascade, content
  detaches), the gaps, six questions, and the build order.

M8's order puts account deletion before report, block and hide, which
moves USER_SAFETY.md's reportTargets to MODULE_API 1.21.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
wtclaude added 1 commit 2026-10-29 17:24:07 -05:00
The org lead answered the plan's six questions on 2026-10-29:
- delete at once, no grace period
- what a member wrote stays, and members see every snapshot of a
  deleted account as "[deleted player]"
- staff keep the name and the IP for moderation, so the stored rows are
  untouched and the rule is about who sees what (displayAuthor)
- the password is always the proof; SSO sits on top of a real account
- a UO game account stays on the shard; no protocol change
- a deleted staff username is never available again (reserved_usernames)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
wtclaude changed title from docs(android): record M8 step 2, the data-safety audit to docs: record M8 step 2 (data safety) and plan self-service account deletion 2026-10-29 17:24:15 -05:00
wtclaude added 1 commit 2026-10-29 17:29:02 -05:00
A cascade is not an unlink. For UO the link lives on the shard (the
site's shard_account_links is a mirror), so dropping the mirror alone
would leave the shard attributing the game account to a deleted user.

- AD10: the hook is onUserDeleting and runs before the delete; each
  module calls its own unlink for every link (Rust: links.unlinkOwned;
  UO: the sidecar's DELETE /link/{account}, as the staff unlink does).
- AD11: an unlink that can't finish never holds up the deletion. A shard
  that is offline queues into shard_pending_unlinks, retried on
  reconnect. A protected staff account (403) is listed for admins as
  "still linked on the shard".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
whitlocktech merged commit 3545e898af into main 2026-10-29 17:33:24 -05:00
whitlocktech deleted branch docs/m8-data-safety-built 2026-10-29 17:33:27 -05:00
Sign in to join this conversation.
No description provided.