docs(website): unique, changeable, verifiable email addresses (engagement Phase 1b)

Companion to website#<pr>. Records Phase 1b as built, and corrects two things
the plan got wrong before anyone builds on them.

ENGAGEMENT.md
  - Phase 1b step 2 said to pin the index to a case-insensitive collation. Every
    _ci collation MariaDB offers here is also accent-insensitive, so that index
    would refuse jose@x.com once josé@x.com existed and the de-duplication would
    have cleared a legitimate account's address. The as-built block records the
    generated-column design that shipped instead, and the second-order version of
    the same bug that a seeded fixture caught in the de-dupe query itself.
  - §0.6 named two callers of isDuplicateUsername(). There are five, and the
    three it omits fail worse than the two it names.

BACKEND_DESIGN.md — the users table (already stale: it predated the player
account work), plus email_verifications and email_dedupe_report, and the five
new routes.

UPGRADE_NOTES.md — an operator entry, because the de-duplication is the kind of
quiet change this file exists for: nothing breaks, and the affected users find
out the next time they try to reset a password.

api-route-inventory.json — regenerated from the manifest; still ungated.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-08-29 01:54:05 -05:00
parent e833874e8b
commit 84481ee6b1
4 changed files with 236 additions and 14 deletions

View File

@@ -13,6 +13,71 @@ action, whether it is required, and what happens if you do nothing.
---
## Email addresses are now unique, and some accounts may lose theirs
**Required only if the dashboard says so.** Engagement Phase 1b.
### What changed
An account's email address is now the destination for account recovery and, in time, for notification
mail, so an address may belong to **one account only**. Until now it could repeat — SSO sign-up wrote
whatever address the provider returned, without checking — so a database that has been running with
SSO enabled may well hold duplicates.
Users can now also **set and change their own address**, under *Account → Email address*, confirming
it by opening a link. Before this there was no way for anyone to change their own address at all.
### What the upgrade does on first boot
Nothing you need to trigger, and **no account is deleted**. Where several accounts share an address:
- the **earliest-created** account keeps it — not the "verified" one, because SSO used to mark an
address verified merely for existing, so that flag cannot arbitrate anything;
- every later account has its address **cleared** (set to nothing) and is listed in a report;
- the dashboard then shows a warning naming how many accounts were affected.
Case is folded — `Foo@x.com` and `foo@x.com` are the same mailbox — but **accents are not**:
`josé@x.com` and `jose@x.com` are correctly treated as two different addresses.
### What you have to do
If the warning appears, open **Admin → Users** and read the report. It names each affected account and
the address it lost. **Those users are the reason this warning exists:** they can still sign in
normally, but they can no longer receive password-reset or notification email until they set a new
address themselves, and nothing tells them that. Contact them, and point them at *Account → Email
address*.
Dismissing the warning keeps the report — it is the record of what the upgrade did, and the only place
the lost addresses survive.
### Verification email
Confirmation links are sent through the same transport as everything else, so **if outbound email is
not configured, nobody can confirm an address.** The screen says so honestly rather than claiming a
mail was sent. If you have not configured SMTP yet, see the entry below first.
### The verification setting
A new setting, *require a confirmed address before sending notification email*, is seeded **off on an
existing deployment** and **on for a fresh install**. The asymmetry is deliberate: switching it on
retroactively would silently stop mailing every user who had already opted in, on the day you
upgraded. Nothing reads it yet — it takes effect when engagement notifications ship — so there is no
hurry to change it.
### If you do nothing
The de-duplication has already run; it runs on the first boot whether or not you read this. What you
lose by ignoring it is the chance to tell the affected users, who will otherwise discover it the next
time they try to reset a password.
### Data
Nothing is dropped and no row is deleted. `users` gains `email_norm` (a generated lowercase copy of
`email`, which carries the uniqueness index) and `email_pending`. Two tables are added:
`email_verifications` and `email_dedupe_report`. Cleared addresses survive in the report.
---
## Outbound email: the Gmail connect flow is gone; configure SMTP
**Required, if this deployment currently sends email.** Engagement Phase 1.