From c2a8426b74c1833e0fea43266eab6a8986cad223 Mon Sep 17 00:00:00 2001 From: Ilia Denisov Date: Fri, 3 Jul 2026 13:24:01 +0200 Subject: [PATCH] docs: account deletion = legal retention, not erasure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FUNCTIONAL (+ru): the deletion user story — anonymised live surfaces ([Deleted]), freed sign-in methods, a two-year-retained dossier, the code/phrase step-up, game forfeit + all-robot drop, fresh account on reopen. ARCHITECTURE: the retention model (retained_identities journal on every detach, tombstone + [Deleted] sentinel, drop all-robot games, purpose=delete step-up, cold-load last-login, two-year TTL reaper). --- docs/ARCHITECTURE.md | 19 +++++++++++++++++++ docs/FUNCTIONAL.md | 10 ++++++++++ docs/FUNCTIONAL_ru.md | 9 +++++++++ 3 files changed, 38 insertions(+) diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index f1376c5..b80bedc 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -273,6 +273,25 @@ arrive from a platform rather than completing a mandatory registration). new address already confirmed by **another** account is refused **without disclosure** (a neutral "check the address or contact support") and **never merged** — the anti- enumeration check is only reachable by someone who controls the new mailbox. +- **Deletion is legal retention, not erasure** (`internal/accountdelete`). The account row + survives as a **tombstone** (`accounts.deleted_at`) — its chat/complaint foreign keys + have no cascade, so a hard delete is impossible. `AnonymizeAndTombstone` **journals** + every live identity into an append-only **`retained_identities`** log (the legal dossier) + then removes it, freeing the `(kind, external_id)` for a new account to reuse; it scrubs + the live `display_name` → **`[Deleted]`** (an unspoofable sentinel — the editable-name + rule forbids brackets) while snapshotting the real name into `deleted_display_name`, + anonymises the game-seat snapshots, and drops the account's own friendships / blocks / + invitations / friend-codes / drafts / pending-codes. **Chat, feedback and complaints are + kept.** The orchestration a layer up resigns the account's active games (so opponents are + not stranded), **drops its all-robot games** (no human opponent; children cascade), and + revokes its sessions. Every credential detachment — **unlink, email change and delete** — + writes a `retained_identities` row (`reason`), so the dossier keeps the full timeline. + Step-up is a mailed code (`purpose=delete`, no deeplink — a stray click must not delete; + `ConfirmByToken` refuses a delete token) for an email account, else a typed phrase. + `last_login_at` / `last_login_ip` are stamped on the cold-load profile fetch (throttled + to once an hour). A **two-year TTL reaper** (from the event) purges the whole dossier — + the journal, plus a deleted account's feedback thread and dossier PII — while the chat and + the tombstone row stay. - **Merge** retires the account that owns the linked identity into the **current** account, in a single transaction (`internal/accountmerge`): statistics summed (counters incl. moves/hints added, max points kept, and the per-variant best moves diff --git a/docs/FUNCTIONAL.md b/docs/FUNCTIONAL.md index c517b61..08feae1 100644 --- a/docs/FUNCTIONAL.md +++ b/docs/FUNCTIONAL.md @@ -117,6 +117,16 @@ address that already belongs to another account is refused with a neutral "check address or contact support" — the switch never merges and never reveals the other account. +A player can **delete their account**. This is a legal-retention removal, not an erasure: +the account is deactivated and its live surfaces anonymised (opponents see "[Deleted]"), its +sign-in methods are freed for reuse, and its sessions are revoked — but a dossier (the +credentials that were linked, the last login, and the player's messages) is retained for +the operator and purged after two years. Confirming deletion needs a mailed code for an +account with an email, or a typed phrase otherwise. Active games are forfeited so opponents +are not stranded; solo games against the AI are removed, while games with a human opponent +are kept under the anonymised name and the player's chat stays. Reopening the app after +deletion simply creates a fresh account. + ### Lobby & matchmaking On a cold open the lobby greets the player with a brief **loading splash** — Scrabble tiles spelling **ЭРУДИТ / ЗАГРУЗКА / ОЖИДАНИЕ** as a small crossword — that clears the moment the diff --git a/docs/FUNCTIONAL_ru.md b/docs/FUNCTIONAL_ru.md index 1d9c52b..e06dd6e 100644 --- a/docs/FUNCTIONAL_ru.md +++ b/docs/FUNCTIONAL_ru.md @@ -121,6 +121,15 @@ Telegram; добавление VK в вебе пока не предлагает «проверьте правильность e-mail или обратитесь в поддержку» — смена никогда не сливает аккаунты и не раскрывает чужой. +Игрок может **удалить аккаунт**. Это удаление с юридическим удержанием, а не стирание: +аккаунт деактивируется, живые поверхности обезличиваются (соперники видят «[Deleted]»), +способы входа освобождаются под повторную регистрацию, сессии отзываются — но досье +(какие креды были привязаны, последний вход, сообщения игрока) сохраняется для оператора и +чистится через два года. Подтверждение удаления — код на почту (если у аккаунта есть +e-mail) либо ввод фразы. Активные игры форфейтятся, чтобы не бросать соперников; одиночные +игры против ИИ удаляются, а игры с людьми сохраняются под обезличенным именем, чат игрока +остаётся. Если снова открыть приложение после удаления — создаётся новый аккаунт. + ### Лобби и подбор При холодном запуске лобби встречает игрока короткой **заставкой загрузки** — фишки Scrabble складывают небольшой кроссворд из слов **ЭРУДИТ / ЗАГРУЗКА / ОЖИДАНИЕ** — и она исчезает, как