Relay
Sender Unlinkability: The Pseudonym Problem
6 min read
Hiding your name is not the same as hiding that it's you. A relay that never learns "Alice" can still learn "the same unnamed person who messaged Bob at 9, Carol at 10, and a support line at 2am" — and that shape is often enough to put a name back on.
Sealed sender removed the sender's identity from the envelope and
left one thing behind: a 16-byte cert_id the relay needs for revocation and abuse
control. This article is about the surprisingly sharp leak that one small field creates,
and the shipped fix.
1. Linkability, and why a pseudonym is dangerous
Cryptographers distinguish anonymity (the system doesn't know who you are) from unlinkability (the system can't tell that two of your actions were done by the same someone). They are different, and the second is easy to lose while thinking you have the first.
The cert_id is stable for the life of a certificate — roughly a day. So even though the
relay never learns "Alice," it can group every message carrying ticket #7 into one
bucket and study the bucket: how many conversations it touches, at what hours, in what
bursts, in reply to what. That bucket is a pseudonym, and pseudonyms de-anonymize
through pattern. This is the entire field of traffic
analysis: intelligence agencies have
reconstructed identities and relationships from nothing but who-contacted-whom-and-when for
a century. "It's just an unlabeled graph" has never been much comfort, because graphs get
labels from the outside — one known contact, one predictable routine — and the whole shape
lights up.
So a stable per-user ticket, even an anonymous one, is a real leak. We want the relay to lose the thread between a person's conversations.
2. Why we can't just delete the ticket
The tempting answer is to drop cert_id from the envelope entirely. We cannot — not
without moving the jobs it does somewhere else first — because the relay uses it for two
things that genuinely have to happen at the moment a message arrives:
- Revocation. If a certificate is compromised or its owner is banned mid-day, the
relay has to stop honoring it now, before its daily expiry. It does that by checking
the incoming
cert_idagainst a short revocation list. No identifier, no revocation. - Abuse metering. An anonymous per-certificate counter (fed to a separate anomaly detector) is how the system notices "one ticket is behaving like a spam cannon" without attaching that behavior to a person.
Both are legitimate, both need some stable handle, and neither is allowed to be the user's identity. The tension is real: the same field that enables anonymous revocation is the field that enables pseudonymous clustering.
3. The shipped fix: a different ticket per conversation
The resolution is to keep a stable ticket, but make it stable only within a single
conversation rather than across a user's whole life. Instead of one certificate per user
per day, Alice mints one certificate per conversation — a distinct cert_id for her
chat with Bob, another for her chat with Carol.
The trick that makes this cheap and private is where the certificates are keyed: each is derived from the local channel key of that specific conversation — a secret Alice and her partner already share and that is never transmitted to anyone. Alice caches these per-conversation certificates locally. When she messages Bob she presents the Bob-ticket; when she messages Carol she presents the Carol-ticket.
Now walk it from the relay's side. It still sees a stable ticket inside a single conversation — but it already knew those messages belonged together, because a conversation shares a routing subject anyway. What it has lost is the ability to tie the Bob-ticket and the Carol-ticket to each other. Alice's separate conversations are now separate unlabeled shapes with no line drawn between them. The pseudonym that used to span her whole social life now stops at the edge of each chat.
The analogy is using a different alias at every venue. A doorman at one club can still recognize "the guy in seat 4 all night," but no one can cross-reference the clubs to discover it's the same person making the rounds — because the name on each wristband is different and unlinkable.
4. What it costs
Not much, which is why this is the shipped choice rather than a someday-plan:
- One extra certificate per active conversation, each renewed at half its (~1-day) life. The count is bounded by how many people you are actively talking to, not your whole address book.
- Each mint is a hybrid Ed25519 + ML-DSA-65 signature at credential-svc — cheap, and only for pairs with live traffic.
That is the entire price for erasing cross-conversation linkage at the relay.
5. The honest residuals
We name what this does not fix:
Within one conversation, the relay still sees a conversation's messages grouped — but it saw that already (they share a routing subject), so per-pair certificates reveal nothing new inside a chat. This is not a regression; it is the pre-existing floor.
The number of distinct tickets a device presents ≈ its number of active conversations. That count is itself a coarse signal — roughly "how socially busy is this device" — even though it links to no identity and no specific partner. It is a much fainter shape than a full cross-conversation graph, but it is not zero.
credential-svc sees the mint volume. The service that issues certificates knows how many were minted, though its ledger keys on a hashed user handle, never the raw account, and it never sees the content or recipients of anything. Mint volume is a weak, aggregate signal held by a different party than the relay.
The admission token carries a cleartext
key_id. This is the one field on the list that is not per-conversation, so it is the one that could re-stitch the pseudonyms above into a single identity — and it would survive cert rotation, which is what makes it worth naming separately. Its harmlessness is not a property of the cryptography but of three deliberate checks on the key set: a hidden known-answer canary, a published live-key list, and a transparency-log commitment with a proof that the committed generation is the current one (The Admission Token §5). Those checks are load-bearing here, not just at the mint.
None of these reconstruct a cross-conversation social graph, which is the leak we set out to close. But honesty is the house style, so they are on the record.
6. The endgame we didn't ship (yet)
There is a cleaner design, and it is worth stating because it is the recommended next step.
True Signal-style sealed sender removes cert_id from the envelope entirely. In that
world the relay does no per-message identity check at all: revocation moves entirely to
credential-svc (a banned account simply cannot mint fresh admission tokens, so its messages
stop being admitted rather than being recognized-and-rejected), and all relay-side
abuse control rides on the single-use admission token, which
is already anonymous and unlinkable by construction.
We did not ship that yet because it is a change to both the envelope format and the admission path — the riskier surgery — whereas per-conversation certificates deliver the property that actually matters (no cross-conversation linkage) as a localized, low-risk change that touches neither. It is the classic engineering call: ship the 90% that removes the real leak now, keep the elegant 100% as a planned migration. When the envelope format next changes, dropping the ticket is the plan.
References & further reading
- "Traffic analysis" — why an unlabeled graph is not a safe graph.
- "[Un]linkability and pseudonymity" — the distinction this article turns on.
- Signal, "Sealed Sender" — the token-only endgame in §6 is essentially Signal's model.
- Anonymous Admission — the token that would carry all abuse control in that endgame.