Reserved and Unavailable Usernames
Categories:
What a “handle” is here
One value, two columns. users.display_name and users.username hold the same
idea — the person’s public handle — and setDisplayName() writes both from a
single variable so they cannot drift apart. It renders as @handle on the
public profile card and next to the person’s face on every Phenom Event they
publish.
Until PhenomApp #835 (2026-09-15) that field had no format rule and no
reserved list. The onboarding gate checked three things: the value is not
empty, it is not a raw Cognito or Matrix identifier, and nobody else already
holds it. That was all. support, noreply, admin and any slur were
first-come, first-served.
Two different refusals
The list separates them because they deserve different words at the UI, and different judgement when somebody appeals.
| Reason | Meaning | What the app says |
|---|---|---|
reserved |
The name belongs to the company or to a role. Someone holding it can impersonate us — @support messaging users in chat is a phishing primitive. |
That username is reserved — try another. |
offensive |
The name violates the terms of service. | That username isn’t allowed — try another. |
Neither message names the term that matched. Telling somebody exactly which word tripped the filter is a map for getting around it.
Normalisation
Every handle is reduced to one comparison form before it is compared to anything. In order:
- NFKD decomposition —
ádmínand fullwidth4dminlose their decorations. - Casefold —
AdminandADMINareadmin. - Leetspeak fold —
0→o,1→i,3→e,4→a,5→s,7→t,8→b,@→a,$→s,!→i,|→i,+→t. So4dm1n,@dm!nand5upp0r7land onadmin,adminandsupport. - Strip everything outside
a-z0-9—A.D-M_I Nisadmin.
The strip is also what lets a single entry cover every spelling of a compound:
one phenomsupport entry catches phenom_support, phenom-support,
phenom.support and Phenom Support.
For token matching the handle is also split on its separators, so
admin_matt is the two tokens admin and matt.
The three match rules
The rule is chosen per category, not per term, and the choice is the whole design. One rule for everything is wrong in both directions.
| Rule | Matches when | Trade-off |
|---|---|---|
exact |
the normalised handle equals the term | Safest. Reserves the bare name and nothing else. |
token |
exact, or the term is one of the handle’s separator-delimited tokens |
Catches the compound (phenom_support), lets a longer word through (supportive is one token and is not support). |
fragment |
the term appears anywhere inside the normalised handle | Catches padding (xx_nigger_xx). Also the Scunthorpe trap, so almost nothing qualifies. |
Category 1 — Slurs and hate terms
Rule: fragment · Reason: offensive · 13 terms
Deliberately tiny. A term earns fragment matching only if no ordinary word,
name or place contains it. Terms whose fragment would eat real words are in
category 2 instead — spic (spicy), coon (raccoon), chink (a chink of
light), retard (flame retardant), kike (Kiké, a Spanish nickname for
Enrique). Those are real handles belonging to real people, and blocking them to
catch a padded slur is a bad trade.
nigger niggers nigga niggas
faggot faggots hitler kkk
pedophile paedophile childporn childpornography
bestiality
Category 2 — Profanity, sexual and violent terms
Rule: token · Reason: offensive · 87 terms
Whole-token only, so assassin keeps its ass and the residents of
Scunthorpe keep their town. Padding these (xxfuckxx) walks through by design;
that is the price of not blocking real names, and category 1 is where the terms
worth paying false positives for live.
fuck fucker fuckers fucking motherfucker
shit shithead bullshit cunt cunts
bitch bitches bastard asshole assholes
arsehole ass arse dick cock
prick pussy twat wank wanker
whore slut bollocks piss anal
blowjob boner boobs clit clitoris
cum cumshot deepthroat dildo ejaculate
handjob hentai incest jizz masturbate
masturbation milf nipples penis porn
porno pornhub rimjob semen tits
titties titty vagina xxx zoophilia
rape rapist molest molester pedo
jailbait kys killyourself genocide nazi
nazis heilhitler whitepower klan spic
coon chink gook wetback tranny
trannies retard retarded kike fag
fags dyke
Category 3 — Internal staff mailboxes
Rule: exact · Reason: reserved · 42 terms
The local part of every internal address: the AWS WorkMail groups (see The Phenom App Group Email Accounts), the individual staff mailboxes, and the build/bot senders.
Exact match only, and that is deliberate. matt, logan, jon and aaron
are ordinary first names. Token-matching them would block matt_the_dog and
logans_run for no impersonation gain. What is being protected is the bare
handle that reads as “this is that mailbox”.
When a mailbox is added or removed in WorkMail, this list has to follow.
accounts beta careers contact dev
devops feedback int internal jobs
legal media partners press sales
systems aaron aaronsilverman il ilena
ilenalogan jon jonathan jonathanhart logan
m matt matthew matthewstevens moneypenny
phenomjon q silverman smsmatt zorin
agent androidbuildbot build builder buildbot
drop sanmarcsoft
Category 4 — Roles and authority
Rule: token · Reason: reserved · 27 terms
The four M named explicitly — root, admin, support, help — plus the rest
of the authority vocabulary.
Token match, because here the impersonation shape is the compound:
phenom_support, admin_matt, official-help. The cost is that legitimate
compounds using these words are refused too. That is accepted: the words are
narrow and authority-signalling, and the alternative is an @phenom_support
account we do not control.
abuse admin administrator admins
billing customercare customerservice customersupport
donotreply help helpdesk hostmaster
moderator moderators noreply official
postmaster root security staff
superuser support sysadmin sysop
trustandsafety verified webmaster
Category 5 — Brand
Rule: exact · Reason: reserved · 21 terms
Exact rather than token, so an enthusiast can still be phenom_hunter or
phenom_fan. The impersonating compounds are enumerated individually instead,
and separator stripping means one entry covers every spelling of each.
phenom phenoms phenomapp phenomearth
thephenom thephenomapp phenomadmin phenomhelp
phenomhq phenommod phenommoderator phenomofficial
phenomsecurity phenomstaff phenomsupport phenomteam
officialphenom codetalker trusteddit trustedditsupport
verifieddit
Category 6 — System, routing and placeholder words
Rule: exact · Reason: reserved · 93 terms
Names that would read as infrastructure, as a URL path, or as the absence of a
name — null, undefined, deleted, api, www. Exact only: these are cheap
to reserve and expensive to token-match, because news_feed, sound_system and
my_profile are all perfectly good handles.
about account all android anon
anonymous api app apps assets
auth blog bot bots callback
cdn channel console create dashboard
deleted deleteduser delete demo discover
dns edit everyone example explore
faq false favicon feed ftp
guest health here home imap
index ios login logout mail
me mobile my nan new
news nil nobody none notifications
null oauth ping policy pop
portal privacy profile profiles redirect
register robots sample search service
services settings signin signup sitemap
smtp static status system temp
terms test testing tmp true
undefined unknown user username users
web www you
Where it is enforced
HasuraAPIAdapter.setDisplayName() — the single choke point that both
handle-writing screens go through, so the list cannot be live on one and not the
other:
DisplayNameGate, the blocking onboarding modalProfileScreen, where an existing user changes their handle
The check runs before the availability query. A name nobody may have is not a question worth asking the server. Both screens also check locally as the person types, so the answer arrives without a round trip.
A rejection raises ReservedHandleError, which is deliberately not
DisplayNameTakenError. “Somebody else has that” and “nobody may have that” are
different answers.
What this does NOT do
Stated plainly, because a blocklist that is believed to be complete is worse than one known to be partial.
- It is client-side. It is bypassable by anyone talking to Hasura directly
with a user JWT. To be authoritative the same terms have to land as a Postgres
CHECKor a Hasura permission on theusersupdate. That is a phenom-backend change and has not been made. - It does not reclaim handles already held. If an account on production currently holds one of these names, this list does not take it away. That needs a query against production and a decision from the owner.
- Repeated characters are not collapsed.
fuuuckpasses. Collapsing runs would breakbookkeeperandaaron. - Cyrillic and Greek homoglyphs are not folded.
аdminwith a Cyrillic а is a different string. - Padded profanity passes the token list by design (
xxfuckxx), which is the stated price of not blocking real names. - It does not cover
users.name, the full name. Only the handle columns.
Changing the list
The source of truth is PhenomApp/app/utils/reservedHandles.ts. This page is
the human-readable copy of it; change the code, then change this page in the
same PR.
Two invariants are enforced by the test suite and will fail CI if broken:
- every term is already in normal form (lowercase,
a-z0-9only) — a term that is not could never match anything, because the handle it is compared against always is; - no category contains a duplicate term.
Before adding a term to the fragment list, check it against ordinary English
words, surnames, and place names. That list is small on purpose.
Total across all six categories: 283 terms.
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.