Why Pairwise Queues Replace Phone Numbers
The thing standing between your contacts and the server is not a user record. It is a disposable envelope — and the server throws it away the moment it's used.
Key Takeaways
- SimpleX has no user account on the server. The unit of addressing is the message queue, not the person.
- Every contact you have lives in a *different* unidirectional queue pair, with a different server and a different opaque address. The architecture explicitly breaks the social graph.
- Servers only hold messages in memory until delivery. They do not talk to each other. There is no global directory of users, queues, or servers.
- A 2022 security audit by Trail of Bits (October 2022) confirmed the implementation matches this design. The 2024 cryptographic review confirmed the design itself is sound.
- This structure has a behavioral consequence: 12 law-enforcement requests received in 2025, zero records produced (
docs/TRANSPARENCY.md, Feb 2026 revision). The architecture is what makes the number possible.
---
Mohamedou Ould Salahi spent fifteen years in Guantánamo Bay without trial. His crime: a phone call to a relative in Afghanistan after he had lived in Germany for a decade. The connection graph — who called whom, when — was enough.
I open with this not to dramatize but to fix the threat model. End-to-end encryption protects the *content* of messages. It does nothing for the *graph*. If the server knows you and your contacts are connected, that knowledge is enough to render content protection academic. The README makes this point by citing Salahi explicitly, and the rest of the design is the architectural answer.
The thing that is not there
Most messaging systems model the world as:
User account ↔ Server ↔ User account
The account is the persistent object. It survives across conversations, across devices (in the well-designed systems), across handovers, across years. Even when the account is "anonymous," it is the unit the server uses to find you.
SimpleX replaces that with a different model:
Queue pair ↔ Server ↔ Queue pair
There is no account. The only persistent objects are *queues* — unidirectional, single-purpose message containers. Each connection between two users gets two queues, one in each direction, addressed by a different opaque string. Different contacts of the same user live in *different* queue pairs, often on *different* servers. The README states this directly:
"SimpleX uses pairwise per-queue identifiers (2 addresses for each unidirectional message queue, with an optional 3rd address for push notifications on iOS, 2 queues in each connection between the users). It makes observing the network graph on the application level more difficult, as fornusers there can be up ton * (n-1)message queues." (README.mdlines 283)
Let that quantity settle. n × (n−1) is the combinatorial explosion. A 100-user network has up to 9,900 queue pairs; a 1,000-user network has up to 999,000. None of those queues are interchangeable. None carry the same address twice. Even the *server* cannot tell that two queues belong to the same person — they look like two independent relay conversations to anyone observing at the transport layer.
This is the architectural claim. The rest of this chapter is the operational consequence.
The consequence: unlinkability by construction
The property most readers miss on first contact is *unlinkability*. When Alice talks to Bob through queue pair Q_AB and to Carol through queue pair Q_AC, the two queue pairs share no identifier — not on the wire, not in the relay, not in TLS metadata, not in any header visible to a passive observer. From Bob's perspective, the queue address he sees is the only such address Alice has ever used to talk to him. From Carol's perspective, the same. There is no shared identifier Bob and Carol can compare to confirm or deny that they both know Alice.
This is a stronger property than anonymity. Anonymity protects Alice's *real-world identity* — her name, her phone number. Unlinkability protects Alice's *social graph* — whether two of her contacts can confirm she is in common. Every messenger with a persistent user identifier (phone number, account ID, public key) fails the unlinkability test by construction: Bob and Carol can both look up Alice's identifier, and the answer is the same. SimpleX's pairwise queues make the lookup return a different address for each contact, so the answer is *never* the same.
This is why the README's FAQ entry on Signal is so pointed:
"Signal is a centralized platform that uses phone numbers to identify its users and their contacts. It means that while the content of your messages on Signal is protected with robust end-to-end encryption, there is a large amount of meta-data visible to Signal - who you talk with and when." (README.md lines 213)
The critique is not that Signal encrypts badly. It is that Signal's *delivery* mechanism requires the phone-number mapping. SimpleX's delivery mechanism does not. The architectural choice is upstream of the encryption choice — and that is the point of this entire series.
A message, traced
Here is the path a message takes from Alice's client to Bob's client. The diagram is drawn from docs/protocol/simplex-chat.md and the v5.8 private-routing announcement (blog/20240604-...md). Read it as a sequence, not a picture.
sequenceDiagram
participant A as Alice's client
participant R1 as Alice's relay (SMP-A)
participant R2 as Bob's relay (SMP-B)
participant B as Bob's client
A->>R1: POST message to queue Q_AB
Note over A,R1: NaCl cryptobox (queue E2EE layer)<br/>+ recipient anonymous credential
R1->>R2: Forward encrypted blob
Note over R1,R2: Server-to-server envelope<br/>no shared identifiers or ciphertext
R2->>B: Deliver to queue Q_BA
Note over R2,B: TLS 1.2/1.3 only<br/>per-queue ephemeral key signing
B->>B: Decrypt double-ratchet message<br/>(forward secrecy + post-compromise)
Five observations about what just happened:
1. Alice has no identifier visible to SMP-A. SMP-A sees a queue address and a session credential — but the credential is generated *by the client* (a Curve25519 keypair bound to the queue), not issued by SMP-A from a user record. The relay has no way to map this queue to "Alice." 2. SMP-A and SMP-B never exchange user records. They exchange a single encrypted blob whose header contains no identifiers the two servers share. The 2024 Trail of Bits review (blog/20241014-simplex-network-v6-1-security-review-better-calls-user-experience.md) specifically verified this property. 3. The message is held in memory until delivered, then deleted. The current SMP server implementation uses *in-memory storage* — no message persistence on disk. There is no archive to subpoena. 4. Bob's queue Q_BA is a separate address from Q_AB. Different contact? Different queue. The two contacts cannot, by inspecting traffic, confirm they are talking to the same Alice. 5. End-to-end encryption is nested. A NaCl cryptobox layer at the queue level (redundancy for future multi-server routing) and a Signal-style double ratchet at the conversation level. The cryptographic depth is the subject of E02; here I only note that the layering exists.
What the server does and does not know
I want to be precise about this because the privacy claim collapses if you overstate it.
The server (SMP relay) knows:
- A queue exists and has some messages.
- The TLS fingerprint of whoever created or is reading from it.
- Traffic timing (mitigated, not eliminated — see E02's discussion of message padding).
- The approximate message size (also mitigated by padding to a fixed block size).
The server does not know:
- Who owns the queue (no user account).
- What other queues the same owner has (different addresses, possibly different servers).
- The content of any message (double ratchet + queue-layer NaCl + TLS).
- Who the queue's "recipient" or "sender" is as people (only as ephemeral queue identities).
This is a meaningfully smaller knowledge set than what Signal, Telegram, Matrix, or Session hold about you. The 2025 transparency report is the behavioral confirmation: in a year when most platforms served hundreds of subpoenas, SimpleX received 12 and produced records from zero of them — not because the team refused, but because the architecture left nothing to produce. (docs/TRANSPARENCY.md, revised 2026-02-09.)
Why this is harder than it looks
The first reaction I had when I read the protocol spec was: *how do you find someone if you don't have an address for them?* The answer is that you don't — you have to be handed a *one-time invitation link or QR code* out of band. No discovery. No "search for Alice by phone number." No "people you may know." Spam is structurally impossible because there is no address book to spam.
The cost is in the introduction ritual. SimpleX connections are made by exchanging a link or QR code. The README is explicit:
"You need to share a link with your friend or scan a QR code from their phone, in person or during a video call, to make a connection and start messaging." (README.md line 85)
If you are running a 200-person activist cell and your contact list was a phone address book, the migration friction is real. The architecture does not apologize for this. It treats the friction as the price of the property, and the property is the product.
Where I changed my mind
I came into this analysis skeptical of "no identifiers" claims — I'd seen enough "anonymous" platforms that turned out to have a single point of correlation. What changed my mind was reading the protocol spec alongside the audit reports. The pairwise-queue design is not a slogan bolted onto a Signal clone. It is a different routing topology with different objects, and the Trail of Bits July 2024 review of the cryptographic protocol design (blog/20241014-...md) confirmed the design properties line up with the documentation. The behavioral signature — 12 requests, 0 disclosed records — is what an architecture that cannot count its users produces under adversarial conditions. The number is the second argument.
The single sentence to carry forward
If E01 collapses to one sentence, it is this: the message queue is not a property of a user; the user is a temporary aggregation of message queues. The architecture has inverted the usual ownership relation. Everything in E02 — the cryptographic depth, the post-quantum additions, the repudiation choices — is engineering effort spent to defend that inversion under hostile network conditions.
---
References:
README.mdlines 281–298 — pairwise per-queue identifier specification.docs/SIMPLEX.md— the comparison framing and the spam-impossibility argument.docs/protocol/simplex-chat.md— wire-format details underlying the message trace.blog/20240604-simplex-chat-v5.8-private-message-routing-chat-themes.md— the four-encryption-layer onion routing introduced in v5.8.blog/20241014-simplex-network-v6-1-security-review-better-calls-user-experience.md— Trail of Bits July 2024 cryptographic review.docs/TRANSPARENCY.md— 2025 law-enforcement request disclosures (12 received, 0 complied).