← yapbox.fun  ·  Design note  ·  draft one

Yapbox

An anonymous group chat with a Discord-shaped client. What to build on, where the identity layer has to be invented, and the three problems that aren't solved yet.

Where the ground stands, September 2026

Revolt
Still no end-to-end encryption. Moved from "side quest" to roadmapped, not shipped. Messages are TLS in transit and readable server-side.
Matrix / MLS
MSC4256 proposes MLS in Matrix rooms; in discussion, not shipped. Open problem is syncing MLS state with room state across federation. Today it is still Olm/Megolm.
Element
Tightened defaults per MSC4153 in April 2026 — non-cross-signed devices excluded from encrypted rooms.
Cinny
Actively developed. Discord-shaped Matrix client, E2EE inherited. Voice calls and a Tauri v2 desktop build in progress this year.
Finding

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:

What Signal does and doesn't provide
PropertyMeaningSignal's answer
Content confidentialityServer can't read messagesDouble Ratchet with X3DH, now PQXDH — solved, and solved well
Metadata privacyServer can't see who talks to whomSealed sender, private group system, private contact discovery
Identity anonymityYou needn't be anyone to sign upNot provided. Registration requires a phone number
Constraint

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.

Decision

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.

Decision

Identity is three layers, not one

The trick is to stop asking one object to be both the gate and the name. Split it:

The identity stack
LayerWhat it isWhat it buys
Key materialA keypair generated on device, never registered anywhereMaximal anonymity; no server-side identity store to subpoena or leak
Right to actAn anonymous credential, blind-signed by an issuerScarcity without deanonymisation — the unlock
AdmissionInvite tree; each community gates its own issuanceSybil 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.

The idea

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.

Decision

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 N community members so any K can 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.

Constraint

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.

What changes, and what we owe the user instead
AffordanceWhat breaksOur answer
Scrollback on joinYou 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 searchNo server-side index over plaintext.Client-side search over local history. Fast, but only yours.
BotsCan't read messages.A bot with keys is a group member with full access. Say so in the UI; never pretend otherwise.
ModerationMods see only what's reported, not everything.Client-side reporting that reveals the reported message and nothing else.
Web client anywhereEphemeral sessions fight key material.Device verification on each new session; accept the friction.
Open problem

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.

Open problem

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.

Open problem

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.

Build

The gap, on top of Cinny and Matrix

  1. 1Registration without an identifierHomeserver accepts a keypair or credential in place of user and password. The item that fights Matrix hardest.
  2. 2Credential issuance servicePrivacy Pass style. Blind signature, unlinkable redemption.
  3. 3Per-community pseudonym derivationOne master identity, many unlinkable community identities.
  4. 4Invite-tree admissionCommunity-controlled issuance; abuse traceable up the tree without unmasking.
  5. 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.

Reference

Prior art

Who has already built which half
ProjectDoesDoesn't
SessionSignal protocol fork; keypair identity, no phone numbers, onion routingDiscord shape; large-group scale
SimpleXStrongest metadata story — no user identifiers at allLarge communities; familiar UX
Matrix / ElementE2EE group messaging, cross-signing, federation, spacesAnonymous identity; Discord shape
CinnyDiscord-shaped Matrix client with E2EE inheritedAnonymous identity
RevoltFull Discord shape, self-hostable, AGPL-3.0Any 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.

Next

What to decide first

Three questions, in order. The first one gates the rest.

  1. Own homeserver, or join the federation? This determines how far we can go on identity, and therefore whether the central idea is even reachable.
  2. Community-scoped bans, or revocable credentials? This constrains the cryptography, so it can't be deferred.
  3. 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.