How it works

Sending a message

One message, followed end to end. At every hop the server sees only a recipient pseudonym, a ciphertext blob, and a size — never the sender, the content, or an IP.

You and your friend write in a language only the two of you agreed on, before the note ever leaves your hand. When you drop it off, the doorman only reads “deliver to” — the sender’s name is folded up inside the sealed note. If your friend is out, the note waits in their mailbox and is shredded the instant they pick it up.

The client ratchet-encrypts the message, wraps it in a Sealed Sender envelope, and POSTs it. The authenticated sender id is used only as a Redis rate-limit key — never stored with the row. If the recipient is online it’s pushed over WebSocket with a signed timestamp; otherwise it waits in the UNLOGGED queue under a per-message TTL and drains on reconnect. An ACK hard-deletes the row.

Send → deliver sequence
online push and offline park, then ACK-delete
Figure. The sender identity is authenticated for rate-limiting, then thrown away. The stored row has no sender, no type, and no read state.

Step by step

1Alice

Fetches Bob’s prekey bundle (which costs a proof-of-work) with a transparency proof, verifies it against a pinned key, and derives a Double Ratchet session.

2Alice

Ratchet-encrypts the text (a fresh key per message), pads it to 1024 bytes, and seals the sender identity inside.

3Server

Rate-limits by a keyed hash of the sender, then enqueues the ciphertext addressed only to Bob.

4Server → Bob

If Bob is online, pushes it instantly over the WebSocket with a signed timestamp; if offline, the row waits under its TTL.

5Bob

Opens the seal, verifies Alice’s signature, decrypts, and acknowledges.

6Server

Hard-deletes the row on ACK. Anything unacked past its TTL is swept within 10 minutes.

What the server learns

Server can observe

  • Recipient id, ciphertext size, queue time, expiry, delivery event.
  • The sender — but only transiently, for auth and rate-limiting.

Server cannot learn

  • Message plaintext, or the sender stored with the row.
  • Conversation id, read content, or contact names.
  • A permanent read-receipt or delivery log.