What the server keeps.
Field by field: what is stored, what is sealed with the message, and the requests the app does not make on your behalf.
What reaches our servers
Delivery needs to know where a message is going and when it arrived. Everything else is sealed with it.
What you wrote
the flight lands at 6:40, terminal 2
What we store
the flight lands at 6:40, terminal 2
There is no readable copy.
- Message body
- Ciphertext only
- Attachments
- Ciphertext, each file under its own key
- Who it is for
- Conversation, sender, and the recipient devices it is sealed to, so it can be delivered
- When it was sent
- Timestamp, and whether it was edited
- What kind it is
- Text, media, voice, shared post or forward; whether it is view once; which message it replies to
- Voice note
- Length only. The audio, transcript and waveform are sealed
- Shared post
- That it is a post. Author, preview and target are sealed
- Location, contact card, sticker
- Sealed in full
- Poll
- Vote counts. The question and the options are sealed
- Screenshot notice
- Readable. The app posts it as a plain system message when it detects a screenshot
Sealing works from an allowlist: a field is sealed because the app recognises its shape as one that carries content. A field a future version adds and does not recognise stays readable until it joins that list.
Features that switch themselves off
These stop working in an encrypted chat, because making them work would send something readable.
Link previews are not fetched
Pasting a URL into an unencrypted chat asks our server to fetch a preview. In an encrypted conversation that request is not made, because it would tell the server what you were about to send.
Translation moves onto the device
In an unencrypted chat, translate-before-send posts what you typed to our translation API and a translation is kept. In an encrypted chat that call is not made: a model in the browser does the work, and nothing readable leaves.
A sealed message is never uploaded to search it
An encrypted chat is searched against the copy this device decrypted, and a result is fetched by id. The words you typed are still sent to the server, which searches what it stores readable at the same time, so it does learn the search and not the match.
Photos lose their location
Every image is re-encoded in the browser before it is encrypted. That step discards the EXIF block, which is where the camera writes GPS coordinates.
The same rule covers the parts of Surf that read a conversation. Catch-up summaries stand down for the whole thread if any message in it is encrypted, and a poll suggestion is not made when the latest one is. A fully encrypted conversation produces no summary or suggestion on our side.
Transcribing a voice note follows the same pattern: a Whisper model runs in the browser, on your device, before the note is sealed, which is why the transcript travels sealed with it and never reaches us.
What a game gives away
A shared board needs a referee, and that is the server.
A live game inside a chat is not end to end encrypted. Two people cannot share a board unless something decides who moved and in what order, so the server does, and it can read the moves. It does not hold a board: a match is a seed, a party size and an ordered list of moves, and a move is a cell number.
Your messages in that same conversation are untouched by this. They stay encrypted, and starting a game does not weaken them.
On the device, at rest
What the browser writes down, and how.
The decrypted copy is sealed too
The readable history your device keeps is itself encrypted, under a key marked non-extractable, so a copy of the browser's storage files yields ciphertext and a key that cannot be exported. If a device has no usable keystore, the history is held in memory for the session and never written to disk.
An undelivered message is sealed as well
Text that could not be delivered survives a reload so you can send it again. It is written into the same sealed store, and dropped when you switch accounts.
Who can see that you are there
The signals around a message, and what each one tells the other side.
- Typing indicator
- A live presence channel. Nothing is written to any database
- Read receipts
- Reciprocal: narrowing who gets them retracts the ones you sent and stops you seeing others'
- Narrowing receipts
- To contacts or to nobody, a Sapphire setting. Free accounts send them to everyone
- Unread badge
- Separate from receipts, and never shared with the other side
- Blocking
- Enforced on the server in both directions. The person blocked is not told
If someone demands your messages
What we are able to hand over.
What we hold is the encrypted message and the routing that delivered it: which conversation, which sender, when. A lawful demand gets that. It does not get a readable message, because we do not have one and hold no key that produces one. The same is true of a backup: the server holds a wrapped key it cannot unwrap.
Deleting your account schedules a teardown thirty days out, and it can be cancelled inside that window. The in-app data export covers your profile, posts, bookmarks and follows. It does not include your messages, because our servers cannot read them.
Surf's privacy policy covers the account this signs you in with.
Two more things to know
There is no third-party audit of this implementation and the source is not published. Every primitive it uses is a standard one, named on the security page.
Page views and load times are measured with Vercel's analytics so the app can be kept fast. That data counts pages and timings. It carries no message, no search and no name.