Signal's phone number is not an identity mechanism
It is worth getting this right first, because it reframes the whole project. Signal requires a phone number at registration, and the obvious reading is that the number is your account. It isn't. Usernames arrived in 2024 and let you hide your number from other users — Signal still holds it.
The phone number is a Sybil-resistance mechanism. It is the cheapest scarce resource Signal could bind an account to. Numbers cost money and effort to acquire in bulk, and that is what stops one person running ten thousand accounts.
So when we strip it out, we are not removing a privacy wart. We are removing the thing holding back spam and abuse, and we have to replace its function, not just its appearance. Everything else in this document follows from that.
For clarity, Signal actually gives three separable properties, and only two of them are ones we want to inherit:
| Property | Meaning | Signal's answer |
|---|---|---|
| Content confidentiality | Server can't read messages | Double Ratchet with X3DH, now PQXDH — solved, and solved well |
| Metadata privacy | Server can't see who talks to whom | Sealed sender, private group system, private contact discovery |
| Identity anonymity | You needn't be anyone to sign up | Not provided. Registration requires a phone number |
Anonymity and Sybil resistance pull against each other
This is the governing tension of the project. Every mechanism that makes accounts scarce either costs money — leaving a payment trail — or binds to a real-world identifier. Pick anonymity and you get infinite free accounts. Pick scarcity the obvious way and you get a name attached to each one.
The good news is that this tension is not fundamental. It only looks that way because the gate and the identity are usually the same object. Separate them and it dissolves, which is what the identity design below does.
Build on Matrix, not Revolt
Revolt is the tempting choice and I think it is the wrong one. It gives us the Discord shape — servers, channels, granular roles, bots, a polished client — with none of the cryptography. That is precisely the half we already knew was the easy half.
Worse, its architecture assumes readable content in load-bearing places. Link previews are generated server-side, which means the server fetches URLs out of your messages. Search and notifications are server-side indexes over plaintext. The bot API hands bots message content. Permissions and moderation are built around a server that can see what it governs. Revolt looks like a 90%-finished starting point and is closer to 40%, because the 10% we would replace runs through everything.
Group E2EE with key rotation, multi-device and verification takes years to get right. A Discord-shaped UI is a few months of good front-end work. Build the tractable thing on top of the hard thing, not the reverse.
Concretely that means starting from Cinny — a Matrix client already built with a Discord-like interface, spaces as servers and rooms as channels, inheriting Matrix's encryption. Revolt still earns a place as a UI reference; its permission editors and member sidebars are worth reading.
The honest cost of this decision is in the open problems below, and it is not small.
Identity is three layers, not one
The trick is to stop asking one object to be both the gate and the name. Split it:
| Layer | What it is | What it buys |
|---|---|---|
| Key material | A keypair generated on device, never registered anywhere | Maximal anonymity; no server-side identity store to subpoena or leak |
| Right to act | An anonymous credential, blind-signed by an issuer | Scarcity without deanonymisation — the unlock |
| Admission | Invite tree; each community gates its own issuance | Sybil resistance, and moderation that scales socially |
The middle layer is the one that does the real work. An issuer blind-signs a token proving the bearer is entitled to be here, and the token is cryptographically unlinkable to the moment it was issued. Privacy Pass and VOPRF are the standard primitives.
Because issuance is unlinkable from use, we can gate it on anything scarce we like — an invite, a payment, a proof-of-personhood — and the server still never learns who the user is. That is exactly what the phone number bought Signal, minus the phone number.
There is precedent in the codebase family we started from: Signal already uses anonymous credentials for its private group system, so their group server can verify you are a member without learning which member.
Per-community pseudonyms that don't link to each other
This is the part worth building the product around, and it is a capability rather than an absence — a better pitch than "encrypted Discord."
On Discord, one account is one global identity. Every server sees the same user ID, so correlating someone across communities is trivial. That is a real harm, not a theoretical one: someone who knows you from one server can find you in another, and people who need separation between the parts of their life have none.
Anonymous credentials let us derive a per-community pseudonym from a single master identity, such that:
- Inside a community you are persistently the same person. Roles, history, reputation and moderation all work normally.
- Across communities your pseudonyms are cryptographically unlinkable — to the server, and to other members.
- You still manage one account and one recovery phrase.
Discord structurally cannot offer this. Matrix does not do it by default. It is the thing that makes this a product rather than a re-skin.
Recovery and multi-device, or normal people won't use it
Keypair identity has two failure modes that kill it for ordinary users, and both are solved problems we should not reinvent.
Losing the device
No authority exists to reset your account. Session's mnemonic phrase works, but real users lose mnemonics. Two better mechanisms, and we probably want both:
- Social recovery. Shamir-split the master key across
Ncommunity members so anyKcan restore it. This fits a community app unusually well — the people who can vouch for you are already there. - Passphrase escrow. The server stores ciphertext it cannot read; a strong KDF over your passphrase recovers it. This gives password-reset UX with zero server knowledge. Matrix already does roughly this with Secure Backup.
Using more than one device
Cross-signing, as Matrix implements it: a master identity key signs per-device keys, and a new device is verified by an existing one over QR or emoji comparison. Inherited free by building on Matrix.
A Discord UI without server-side history behaves differently
Nearly every Discord affordance exists because plaintext lives server-side. We do not get "Discord but private" — we get something Discord-shaped that differs in ways people notice on day one. The design work is deciding, feature by feature, what to keep, reimplement client-side, or drop honestly rather than half-support.
| Affordance | What breaks | Our answer |
|---|---|---|
| Scrollback on join | You weren't a member, so you have no keys. New joiners see an empty room. | The most jarring difference. Consider opt-in history sharing on invite, with the tradeoff stated plainly. |
| Server-wide search | No server-side index over plaintext. | Client-side search over local history. Fast, but only yours. |
| Bots | Can't read messages. | A bot with keys is a group member with full access. Say so in the UI; never pretend otherwise. |
| Moderation | Mods see only what's reported, not everything. | Client-side reporting that reveals the reported message and nothing else. |
| Web client anywhere | Ephemeral sessions fight key material. | Device verification on each new session; accept the friction. |
Unlinkable and bannable are in tension
This is the sharpest unsolved corner, and it constrains the cryptography, so it should be decided early rather than discovered late.
If pseudonyms are unlinkable across communities, cross-community ban evasion is trivial. A banned user makes a fresh pseudonym and returns. Revocable anonymous credentials exist, but revocation and unlinkability are natural opposites — every revocation mechanism leaks a little linkability.
Options worth costing out: revocation lists against credential epochs, per-community credential rotation with a revocation window, or accepting that bans are community-scoped and leaning entirely on invite trees so a banned user needs a new voucher to return. The third is the least cryptography and possibly the best product.
Matrix identity is server-bound and not anonymous
This is the honest cost of building on Matrix, and it is the largest single risk in the plan.
Matrix identity is @user:homeserver — human-readable, bound to a server, and woven through the federation model. Pseudonymous Matrix identity is not the default and not a small patch. Everything we want at the identity layer fights the grain of the thing we chose for its cryptography.
Before committing, we should establish whether we are running a single homeserver we control — in which case we can be far more aggressive about replacing registration — or joining the federation, which forces us to live with @user:server in some form. That is the first question to answer, because almost everything else depends on it.
Moderation determines whether this survives real users
Anonymous large-group chat makes moderation genuinely hard, and it tends to decide whether a platform lives. It is solvable — invite trees, reputation, per-community admission, client-side reporting that reveals only what was reported — but it is design work to do early, not bolt on.
Worth noting that our architecture pushes in a helpful direction here. Because admission is social and communities issue their own credentials, moderation is community-scoped by construction rather than centrally imposed. That is a coherent position, and a defensible one.
The gap, on top of Cinny and Matrix
- 1Registration without an identifierHomeserver accepts a keypair or credential in place of user and password. The item that fights Matrix hardest.
- 2Credential issuance servicePrivacy Pass style. Blind signature, unlinkable redemption.
- 3Per-community pseudonym derivationOne master identity, many unlinkable community identities.
- 4Invite-tree admissionCommunity-controlled issuance; abuse traceable up the tree without unmasking.
- 5Moderation tooling for all of the aboveReporting, scoped bans, and whatever we decide about revocation.
Items 2 through 4 are real cryptographic engineering but use existing primitives — no new cryptography required, which is a much better position than it could be. Item 1 is an architecture question disguised as a feature.
Prior art
| Project | Does | Doesn't |
|---|---|---|
| Session | Signal protocol fork; keypair identity, no phone numbers, onion routing | Discord shape; large-group scale |
| SimpleX | Strongest metadata story — no user identifiers at all | Large communities; familiar UX |
| Matrix / Element | E2EE group messaging, cross-signing, federation, spaces | Anonymous identity; Discord shape |
| Cinny | Discord-shaped Matrix client with E2EE inherited | Anonymous identity |
| Revolt | Full Discord shape, self-hostable, AGPL-3.0 | Any encryption at all |
Note on licensing, since it constrains what we can ship: libsignal is AGPL-3.0, and so is Revolt. That has real consequences depending on whether this becomes a hosted service or a distributed product. Worth settling before we write code against either.
What to decide first
Three questions, in order. The first one gates the rest.
- Own homeserver, or join the federation? This determines how far we can go on identity, and therefore whether the central idea is even reachable.
- Community-scoped bans, or revocable credentials? This constrains the cryptography, so it can't be deferred.
- What do we tell a user who joins a channel and sees nothing? The answer to this is the product's honesty test.
My read is that parts of this are research-grade — there is a reason no mainstream product ships unlinkable per-community pseudonyms. But every piece exists separately and none of it needs new cryptography. That is a much better starting position than it sounds like.