From 094c6062386db872ee25003f43bf12809f8ad0a9 Mon Sep 17 00:00:00 2001 From: wtclaude Date: Fri, 30 Oct 2026 01:25:48 -0500 Subject: [PATCH 1/2] feat(legal): /delete-account/, the page Google Play is given (D85, D86) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Step 5 of docs/website/ACCOUNT_DELETION.md (§4.5). The page explains how to delete an account on a community's site and in the app, what goes, what stays and for how long, and who to ask when you cannot sign in. It cannot delete anything, because we hold no account. - The facts are one deployment row in collection.mjs (deletion.goes, .stays with a keep each, .staff), in the website's own words. /privacy section 3 renders it as prose, the page renders its lists, and PLAY_DATA_SAFETY.md prints them under the console's deletion questions. - The app rows that a deletion touches (session, trust token, push, inbox snapshot) say so. The Data Safety notes now answer "yes" to account deletion and name the URL. lastUpdated is 2026-10-30. - D85: linked from the footer's legal bar, /privacy sections 2 and 3, and /app/'s "Two ways to get it". - D86: someone who cannot sign in is sent to their community's operator, never to us. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY --- PLAN.md | 30 +++ PLAY_DATA_SAFETY.md | 48 +++-- scripts/playDataSafety.mjs | 53 ++++-- src/components/Footer.astro | 3 + src/data/collection.mjs | 128 ++++++++++++- src/data/legal.mjs | 2 +- src/pages/app.astro | 12 ++ src/pages/delete-account.astro | 327 +++++++++++++++++++++++++++++++++ src/pages/privacy.astro | 13 ++ test/legal.test.mjs | 35 ++++ 10 files changed, 618 insertions(+), 33 deletions(-) create mode 100644 src/pages/delete-account.astro diff --git a/PLAN.md b/PLAN.md index 17800c5..5ee7e25 100644 --- a/PLAN.md +++ b/PLAN.md @@ -264,6 +264,7 @@ somewhere other than the thing it decided. The count of record is **eighty-four* | D80–D81 | §16, "How phase 18 built it" | A name in a screenshot is a role; the Rust captures' players are mock data | | D82–D83 | §15, "How the site reaches it" | `/beta` lists the demo's sign-ins as the demo publishes them; a demo deep link names its game and shows only for the games the mounted `demoGames` lists | | D84 | §9, "How phase 6 built the legal pages" | The Data Safety answers say "collected, not shared" for everything the app sends, amending D33 | +| D85–D86 | §9, "`/delete-account/`" | The deletion page is linked from the footer, `/privacy` and `/app/`; someone who cannot sign in is sent to their community's operator, never to us | --- @@ -1086,6 +1087,34 @@ form's security questions. D33's test now asserts the part of the premise that i **nothing is shared**. `/privacy` section 2 keeps saying none of it reaches us, and `lastUpdated` moved with it. Report, block and hide (Android M8 step 3) adds its rows when it lands. +### `/delete-account/` (2026-10-30) + +Play needs one account-deletion address from whoever publishes the app. The platform gained +self-service deletion on the website and in the app (`docs/website/ACCOUNT_DELETION.md`, AD1), and +this page is the address Play is given (AD2, §4.5 there). It cannot delete anything: we hold no +account, so it explains where the button is in the app and on the website, what goes and what stays +and for how long, and who to ask when you cannot sign in. + +**The facts are one row.** What a deletion takes is the `deploy-account-deletion` row in +`src/data/collection.mjs`, whose `deletion` field holds the website's own words for what goes and +what stays (`client/src/lib/accountDeletion.js`, which the app's strings repeat) plus how long each +kept thing is kept, which Play asks and the screens do not say. `/privacy` section 3 renders the row +as prose, the page renders its lists, and `PLAY_DATA_SAFETY.md` prints them under the console's +deletion questions, so the three cannot disagree. `deletionRow()` throws when the row is missing, +and `test/legal.test.mjs` fails a kept entry with no retention. The app rows that deletion touches +(the session, the trust token, push and the inbox snapshot) say so, and `lastUpdated` moved. + +**D85 — The page is linked from the footer, `/privacy` and `/app/`** (org lead, 2026-10-30). The +footer's legal bar reads Privacy · Terms · Delete account, because a player looking for it looks +where the privacy link is; `/privacy` sections 2 and 3 and the bottom of `/app/`'s "Two ways to get +it" link to it as well. + +**D86 — Someone who cannot sign in is sent to their community's operator, never to us** (org lead, +2026-10-30). The page offers "Forgot your password?" first, which works when the account has an +email address and the site can send mail, and then the operator, who can delete any account from the +admin panel. It says plainly that we cannot do it: we hold no account and have no access to any +community's site. Offering our contact address to help find an operator was considered and declined. + **What phase 6 also closed.** `/privacy/` and `/terms/` were the last two entries in `checkLinks.mjs`'s `PLANNED_ROUTES`; building them emptied the list, and its reverse check is what forced the deletion. The list itself stays, because §10's documentation routes land in phases 7 and 8 @@ -1111,6 +1140,7 @@ Organised by what a reader is trying to do. A reader should never need to know t | `/beta/` | The closed-beta signup (§8). The one page that handles its own POST (D28) | | `/community/` | Discord (`discord.gg/t2Jav8yT4g`) as the front door, the Gitea org for code and contributions, the `brand.json` contact address for vulnerabilities (D13) — the split in §14 N3 | | `/privacy/`, `/terms/` | §9 | +| `/delete-account/` | §9: how to delete an account you use in the app, and what it keeps. The address Play is given (D85, D86) | **Feature grouping**, using project terminology: diff --git a/PLAY_DATA_SAFETY.md b/PLAY_DATA_SAFETY.md index dc8cd4a..6b0abfc 100644 --- a/PLAY_DATA_SAFETY.md +++ b/PLAY_DATA_SAFETY.md @@ -1,7 +1,7 @@ +

+ Done with it? How to delete the account you use in the + app, and what deleting it keeps. +

@@ -335,6 +341,12 @@ const downloadBase = `${releasePage}/download/${release}`; padding-top: 0.4rem; } + .app-delete { + margin: 1.25rem 0 0; + color: var(--muted); + font-size: 0.95rem; + } + .app-get__foot { margin: 1rem 0 0; padding-top: 0.85rem; diff --git a/src/pages/delete-account.astro b/src/pages/delete-account.astro new file mode 100644 index 0000000..b48d6eb --- /dev/null +++ b/src/pages/delete-account.astro @@ -0,0 +1,327 @@ +--- +import Base from '../layouts/Base.astro'; +import PageHeader from '../components/PageHeader.astro'; + +import { deletionRow } from '../data/collection.mjs'; +import { legal } from '../data/legal.mjs'; +import { brand } from '../lib/brand.mjs'; + +/** + * `/delete-account/` — the page Google Play is given for account deletion + * (docs/website/ACCOUNT_DELETION.md §4.5, AD2). + * + * --------------------------------------------------------------------------------------- + * IT EXPLAINS; IT CANNOT PERFORM + * --------------------------------------------------------------------------------------- + * Play needs one deletion address from whoever publishes the app, and that is us. But we + * hold no account: every community runs its own site, and the account lives there. So this + * page says where the button is on the website and in the app, what goes and what stays, + * and who to ask when you cannot sign in, which is that community's operator and never us + * (D86). We have no access to any community's site, so offering to help would promise + * something we cannot do. + * + * What goes and what stays is not written here. It is the `deletion` row in + * `src/data/collection.mjs`, the same row `/privacy` section 3 renders, so the two pages + * cannot disagree about what a deletion takes. + */ +const title = 'Delete your account'; +const description = + `How to delete an account you use in the ${brand.siteName} Android app or on a ` + + `${brand.siteName} community site, what is deleted, and what is kept.`; + +const row = deletionRow(); +const { goes, stays, staff } = row.deletion; + +const surfaces = [ + { + id: 'on-the-website', + eyebrow: 'On the website', + heading: 'Your community’s site', + steps: [ + 'Sign in to the site.', + 'Open “Account”. Staff open “Account” in the admin panel instead.', + 'Scroll to the bottom, to “Delete my account”, and press “Delete my account…”.', + 'Type your username, then your password, and a two-factor code if the account has two-factor sign-in on.', + 'Press “Delete my account”. You are signed out everywhere, and the site says the account has been deleted.', + ], + }, + { + id: 'in-the-app', + eyebrow: 'In the app', + heading: `The ${brand.siteName} app`, + steps: [ + 'Sign in, if you are not already.', + 'Open the menu and choose “My account”.', + 'Scroll to the bottom and tap “Delete my account…”.', + 'Type your username, then your password, and a two-factor code if the account has two-factor sign-in on.', + 'Tap “Delete my account”. The app forgets the account and returns to sign-in, saying it has been deleted.', + ], + }, +]; +--- + + + +

+ For the {brand.siteName} Android app, and for the community sites it connects to. We + are {brand.siteName}, the project that makes both. +

+

+ You can delete your account yourself, on the website or in the app. It is deleted at + once: there is no waiting period and nothing to cancel. +

+
+ + + +
+
+

Your account is with your community, not with us

+

+ The app ships pointed at nothing. On first run it asks for the address of a + {' '}{brand.siteName} site, and that site is run by whoever runs that community. Your + account lives there, under their control. We hold no account for you and have no + copy of one, so the deletion happens on that site, and you are the one who does it. +

+
+
+ +
+

How to delete it

+

+ The same steps on both, and either one deletes the account everywhere: deleting it in + the app also deletes it on the website, and the other way round. +

+ +
+ { + surfaces.map((surface) => ( +
+

{surface.eyebrow}

+

{surface.heading}

+
    + {surface.steps.map((step) => ( +
  1. {step}
  2. + ))} +
+
+ )) + } +
+ +
    +
  • + A password is required. If you only ever signed in with Google, + Discord or Steam and never set a password, set one in the Password section of the + same page first. +
  • +
  • + The only admin cannot delete themselves. Make someone else an admin + first, so the site is never left with nobody to run it. +
  • +
+
+ +
+
+
+

Deleted

+

What goes with the account

+
    + {goes.map((item) =>
  • {item[0].toUpperCase() + item.slice(1)}.
  • )} +
+

{row.retention.detail}

+
+ +
+

Kept

+

What stays, and for how long

+
    + { + stays.map((entry) => ( +
  • + {entry.what} + + How long + {entry.keep} + +
  • + )) + } +
+

{staff}

+
+
+
+ +
+
+

If you cannot sign in

+
    +
  1. + Use “Forgot your password?” on the sign-in page, on the website or in the app. It + works if your account has an email address and the site can send email. +
  2. +
  3. + Otherwise, ask the community that runs the site. Its operator can delete any + account from the admin panel, and the result is the same as deleting it yourself. +
  4. +
+

+ We cannot do it for you. We hold no account, and we have no access to any + community’s site. +

+
+
+ + + diff --git a/src/pages/privacy.astro b/src/pages/privacy.astro index 17e9b35..2d0bc62 100644 --- a/src/pages/privacy.astro +++ b/src/pages/privacy.astro @@ -66,6 +66,8 @@ const sections = [ 'the app happens between your phone and their server, and there is no account with ' + 'us, no service of ours in the middle, and no copy of anything on our side — because ' + 'we do not operate one.', + /* The deletion page Play is given (D85). The app is where most people will look. */ + link: { href: '/delete-account/', text: 'How to delete the account you use in the app' }, rows: collectedIn('app'), }, { @@ -79,6 +81,7 @@ const sections = [ 'and their policy — this page is not it. What follows is an inventory of what the ' + 'software collects, so that an operator can see plainly what they are taking on, ' + 'and a player can see what to ask their operator about.', + link: { href: '/delete-account/', text: 'What deleting an account takes, and what it keeps' }, rows: collectedIn('deployment'), }, ]; @@ -131,6 +134,11 @@ const sections = [

{section.heading}

+ {section.link && ( + + )}