Skip to main content

Encrypted relay

For colleagues who aren't on the same network, DeepState syncs through a relay: a small service that stores and forwards encrypted change bundles between devices. The relay is a dumb mailbox. It never has the board key, cannot decrypt anything it holds, and cannot even tell which board a mailbox belongs to.

DeepState runs a relay at relay.getdeepstate.app, included with every license. You can also run your own; the app accepts any relay URL.

Turning it on

In the board's Collaborate dialog, under Encrypted relay, the URL field is pre-filled with DeepState's relay. Click Connect. The status line shows Connecting… then Connected · synced just now. Do the same on each device, with the same URL. Everyone else joins with Join shared board… → Relay and the same URL.

Once connected the dialog offers:

  • Pause / Resume - stop exchanging without forgetting anything.
  • Push full history - re-publish everything this device knows about the board to the relay. Use it to seed a new relay, or after the relay has expired old history (below).
  • Change… - switch to a different relay URL.
  • Remove - stop using the relay for this board.

How it works

  • The board key is hashed to produce a room ID. Devices with the same key derive the same room; nobody else can guess it, and the relay cannot link a room to a board name, a license or a person.
  • Each device keeps a WebSocket open to its rooms. Local changes are encrypted with the board key (the room ID is bound into the encryption so a frame can't be replayed into another room) and published; the relay assigns a sequence number and fans the frame out to every connected device, which decrypts and applies it.
  • Devices that were offline read forward from the last sequence number they saw when they reconnect. The relay keeps frames for 90 days.
  • Media files are encrypted, named by a hash, and stored in the relay's blob store; devices upload what they have and download what they lack. File names travel inside the ciphertext.
  • Every device publishes its own edits. When you first connect a board that already has history, DeepState also publishes the full history once so that newcomers can rebuild it.

Retention and re-seeding

Frames older than 90 days are pruned from the relay. If a device has been away longer than that, the relay tells it so and the Collaborate dialog shows: The relay no longer holds the early history of this board. Any current device fixes it by clicking Push full history, which republishes the complete board; the stale device then catches up. Nothing is lost - every device has the full board locally - this is only about what the relay is holding at the moment.

Self-hosting

The relay is a Cloudflare Worker with a Durable Object per room and an R2 bucket for blobs. It is open in the repository under relay/ and deploys with wrangler deploy to any Cloudflare account; it fits in the free tier for a small team. Then enter https://your-relay.example.com in the Collaborate dialog on every device.

GET  /health
GET /rooms/:room/ws?since=N WebSocket: replay frames after N, then live
POST /rooms/:room/frames append a ciphertext frame
GET /rooms/:room/frames?since=N poll instead of WebSocket
PUT /rooms/:room/blobs/:name store an encrypted blob (idempotent)
GET /rooms/:room/blobs/:name
GET /rooms/:room/blobs

Retention, frame size (1 MB) and blob size (64 MB) limits are Worker variables. The relay has no accounts: possession of a room ID is the only credential, and a room ID is derived from a 256-bit key. If you self-host, put the Worker behind Cloudflare Access or an IP allow-list if you want a second factor.

What the relay sees

The relay knowsThe relay does not know
A room ID (a hash)Which board, license or person it belongs to
Ciphertext frames and their sizes and timesAny node content, titles, URLs or names
Encrypted blobs under hashed namesOriginal file names or contents
The IP addresses that connectDevice identities (device IDs are inside the ciphertext)

DeepState's hosted relay is not authenticated against your license; anyone who knows a room ID could read that room's ciphertext. Since the room ID is a hash of the key, the only way to know it is to hold the key - at which point one could decrypt anyway. Rotating the key (stop sharing, create a new invite) moves the board to a fresh room.

Troubleshooting

  • Stuck on Connecting…. Check the URL (it must start with https://), and that outbound WebSocket connections are allowed on your network. Corporate proxies that strip WebSockets will block it; the app falls back to reconnecting with backoff up to once a minute.
  • Connected, nothing arrives. The other device must be connected to the same relay URL with the same key. Ask them to click Push full history.
  • "History no longer available". See Retention and re-seeding above.