Delivery and queuesTechnical referenceBrowse
Reference

Sealed sender and anonymous queues

Encryption hides what you say. This is the harder half: routing a message with fewer explicit identity fields while retaining capability-based admission control. The service may still infer relationships from IP addresses, timing and related operations. The trick everywhere is the same: a capability instead of an identity.

Released beta
On this page

The problem

A registered socket identifies its owner no matter what a message omits; the server knows who registered it. So an ordinary message from a registered socket tells the server the full pair edge: who wrote to whom, when, and how big. Content stays sealed, but the social graph is in the clear. Removing the sender field alone would turn the anonymous path into an open relay for flooding anyone's mailbox. The design needs anonymity and admission control, without identity.

Delivery tokens: sealed sender

  1. You mint a token

    One random 32-byte delivery token per user. Its SHA-256 hash is registered with the server alongside your public keys. The server stores only hashes, so the hash alone is insufficient to present the raw token, assuming high-entropy generation and preimage resistance.

  2. You hand the raw token to established contacts

    Over the encrypted channel, once per contact. Strangers never receive one, so strangers cannot post to you sealed at all; the server silently drops a sealed send with no valid token; silently, to avoid an explicit success/failure response that directly discloses an ID’s existence. Timing, quotas and other paths need separate analysis.

  3. A contact posts on an anonymous socket

    Sealed sends go over a dedicated socket that never registers. The frame carries your ID, the token, and the ciphertext. No sender anywhere on the wire.

  4. Your phone identifies the sender cryptographically

    The message arrives with no sender field. An index from ratchet public keys to peers resolves the common case instantly; a miss falls back to a bounded trial decrypt across live and archived sessions, always on a copy. Successful authenticated trial decryption can bind the message to a local session; the server can still infer an origin through traffic correlation.

Because every contact uses the same token to reach you, the server cannot even separate one incoming edge from another by token. It sees that your mailbox received sealed traffic, not which of your contacts sent each item. The cost is that revoking one contact at the token layer is not possible; rotation (every thirty days, with a grace period for the previous token) revokes everyone at once and re-issues to current contacts. Per-contact blocking lives above this layer. Sealed sends are rate-limited per recipient (600 a minute; live captions ride this path at about two a second) purely as a denial-of-service valve.

Anonymous queues: removing the recipient too

Sealed sender still names the recipient's ID on the frame. Between current builds, messages route instead over per-contact rotating anonymous queues: a mailbox created on an anonymous socket with no account attached to it at all.

Separate send and drain capabilities
Contact
holds the send address
Mailbox
stores ciphertext
Recipient
holds the drain key

Post

The contact uses the send address to deposit an encrypted message.

Drain

The recipient authenticates with the separate drain key to receive and acknowledge messages.

Arrows show message flow. The recipient creates the queue and shares its send capability over the encrypted channel. The drain key stays with the recipient. The server stores capability hashes, ciphertext and timestamps without an owner field; IP and timing correlation remain possible.

Each contact pair gets its own mailbox, with two random capabilities minted by the recipient: a send address shared with the contact over the encrypted channel, and a drain key retained by the recipient. The server stores hashes of both and (by design) no user ID. A post to an unknown, expired, full or rate-limited address is silently dropped; success is the only answer. The server acknowledges every accepted post, and a missing acknowledgement makes the sender automatically retry over sealed sender, so a dead mailbox degrades one message's routing privacy rather than losing it. Rotated-out mailboxes stay drainable for a grace period and are then torn down.

Two optional conveniences link a mailbox back to a device, and are named as trades rather than hidden. Outside Lockdown, a mailbox can carry a push wake token so an offline phone can be woken for a message; since build 141 it is stored as a keyed seal that only the running server can resolve, never a raw value a database snapshot could join, but the running server can still cluster every mailbox carrying one into "one device". Lockdown omits the token and gives up offline wake. And a non-default retention choice rides each mailbox registration, so an owner's mailboxes with the same unusual setting are weakly groupable; a small grouping signal. Binding co-membership is a third signal: all mailboxes one socket drains together belong to one device.

Which route a message takes

  • Anonymous queue when the pair has exchanged mailbox capabilities; the preferred, steady-state path.
  • Sealed sender when you hold the peer's delivery token but no mailbox yet, or as the automatic fallback when a mailbox stops acknowledging.
  • The identified route for the first message of a conversation (the handshake reveals a party anyway; the directory lookup already revealed interest) and for session recovery. Since build 146 that handshake is sealed so the server sees the routing edge but no exposed initiator signature in the sealed initial. The recipient can open the signed transcript, so sealing is not deniability.

Retry wrapping removes a direct byte-equality signal on the wrapped paths: once a pair holds its shared secret, every transmission of a retained ciphertext is wrapped afresh under a domain-separated key, so the server cannot string-match one message across routes. The bootstrap controls that deliver that secret are the one bounded exception, because their delivery cannot depend on the key they carry.

Presence, discovery and blocking

Presence is capability-gated for every account: you publish the hash of a random presence token, contacts receive the raw token end to end and must present it to subscribe. Hidden, unknown, wrong-token and over-budget probes all get the same offline answer. Updates to admitted watchers are independently jittered by four to eighteen seconds and rapid flaps are coalesced, so peers never see exact shared transition timestamps. Blocking rotates the token: the server observes a rotation and can infer a privacy-state transition, and may also infer affected contacts from changes in admitted watchers or subsequent traffic.

Discovery is gated the same way for invite-only users: the directory answers a lookup as if the ID did not exist unless the requester presents the invite token, of which the server holds only the hash. Found responses are padded to one fixed 128 KiB class so their size reveals neither whether a one-time prekey was dispensed nor how many devices exist.

Blinded call rooms

A call's rendezvous room used to be derivable from both IDs. Between current peers it is an HMAC over a pair secret exchanged end to end and the UTC day, so the room string no longer spells out who is calling whom, third parties cannot guess a pair's room to spoof its call-end, and the room identifier itself links calls within that day. Identified call invitations and traffic may enable longer-term linkage. Call invitations themselves still ride the authenticated socket and name the edge; sealed invites are a designed follow-up, not a shipped property.

Cover traffic

An opt-in setting sends a validated 5–6 KiB no-op on the anonymous socket at a jittered idle cadence and receives an acknowledgement-shaped reply, so a wire observer no longer sees a distinctive one-way fingerprint. It is deliberately sparse: it does not hide a specific send, does not run while the app is suspended, does not fool the server (which parses the type), and a small opt-in cohort may itself be recognisable. It is not constant-rate cover and is not described as anonymity.

What still leaks, stated

  • Traffic shape: the server sees that a mailbox received something, its size class and its timing. Shape analysis is not defeated.
  • IP and timing correlation across a device's registered and anonymous sockets, for the server or a global observer, collapses the separation. The anonymous paths hide the pair from the row, not from traffic analysis.
  • The first handshake, session recovery and call invitations ride the identified route, so those edges are visible.
  • Presence remains server-visible after admission: the server sees the successful watcher graph and transitions.
  • The anonymity set is a security parameter: with a small user base, every "the server cannot tell who" claim is bounded by cohort size before any protocol property matters.

Every field, per observer, is enumerated in what the server sees. What would close the remaining edges is the identity and multi-server work described in the research section.

Reviewed · Review scope and versions