How the whiteboard works

It runs on a server I control, and that server cannot read anything you draw on it. Here is exactly how that works — and where it stops working.

The short version

Your drawing is encrypted in your browser before it goes anywhere. The key never leaves your device. The server stores a blob of bytes it has no way to interpret, and the realtime relay forwards those bytes between collaborators without being able to read them either.

Sharing a board means sharing one link. That link contains both the board's address and its key — but only the address ever reaches the server.

Why the link is safe to be a key

A URL has a part after the # called the fragment. Browsers never send it in an HTTP request. It exists purely client-side.

So a share link looks like /draw#room=abc123,KEY. The server sees a request for /draw and nothing else. The room ID and the key live only in your browser's address bar, and the key is passed to the encryption code without ever crossing the network.

The encryption

Scenes are encrypted with AES-GCM using a 256-bit key generated by your browser. Every single save uses a fresh random initialisation vector — reusing one would leak information about the contents, so uniqueness is enforced by a test rather than assumed.

AES-GCM is authenticated encryption: if anyone alters a stored byte, decryption fails loudly instead of returning corrupted shapes.

You can optionally protect a board with a passphrase when you create it. The link then carries only the wrapped key, so the link on its own is useless — opening it asks for the passphrase before anything can be decrypted.

The wrapping uses Argon2id, a deliberately slow, memory-hard function, tuned to take roughly half a second. It is slow on purpose: if a link ever leaks, the passphrase is the only thing standing between an attacker and the board, and they can attack it offline at their leisure. A fast function would make guessing cheap.

The relay is deliberately stupid

Realtime collaboration needs a server in the middle. Ours knows three things: which connections are in which room, how large a message is, and how fast messages arrive.

It never inspects a message body. It has no database credentials, no keys, and it writes nothing to a log — not room IDs, not IP addresses. If someone compromised it completely, they would learn that somebody was drawing something, and nothing more.

Nothing here can phone home

Everything above assumes the code running in your browser is honest. That is a large assumption: this page is built on a few hundred open-source packages, and nobody has read all of them.

So the browser is instructed to refuse network connections to anywhere except this server. Not just for the drawing code — for everything on the page. A compromised dependency that captured what you type would have nowhere to send it. That is a rule the browser enforces, not a promise anyone is making.

The honest gap: this stops background requests, not navigation. Code could still send you to another site with data in the address. The browser rule that covered that was never widely implemented.

Images

Pasted and dropped images are encrypted exactly like everything else. The bytes are stored separately from the drawing itself and fetched when needed, rather than being re-sent every time anyone moves a shape. There is a 5MB ceiling per image, and anything larger is refused with a message rather than failing quietly.

One consequence worth stating: because images are stored as separate items rather than buried inside the drawing, the server can see how many images a board has and roughly how large each one is. It still cannot see what any of them contain.

Keeping boards in step

Updates get lost — a dropped connection, two people saving at the same instant, a bug. So every twenty seconds each open board re-sends and re-saves itself in full, whether or not anything changed. Anything missed corrects itself within one cycle rather than leaving two people looking at different boards until someone reloads.

The cost is worth being straight about: a board left open writes to the server every twenty seconds even when nobody is drawing. So a regular pattern of writes tells the server that somebody has this board open, where silence used to mean nothing was happening. It still reveals nothing about what is on the board — but it is more than the server knew before.

Detecting a dishonest server

Encryption stops the server reading your board. It does not stop the server handing you an older copy of it — authentication proves a snapshot is genuine, not that it is current.

So every snapshot carries a version number inside the encrypted payload, where it cannot be forged. Your browser remembers the highest version it has seen and refuses anything lower. A server that tries to roll your board back gets caught.

What this does not protect you from

A privacy claim is only worth as much as its exceptions, so here they are, plainly.

  • Traffic patterns. The relay necessarily sees connecting IP addresses, room IDs, message sizes and timing. It records none of it, but it could. Eliminating this entirely would mean onion routing or trusting somebody else's server — both worse trades for a personal site.
  • Deletion. Whoever controls the server can delete your board. No cryptography prevents that. Rollback is detectable; destruction is not preventable.
  • Whoever holds the link. Anyone with the link can read and edit the board. It is a key, so treat it like one — a screenshared URL bar or a pasted link in a group chat is a handed-over key. This is what the optional passphrase is for.
  • People you invite. A collaborator is inside the encryption by definition. They can vandalise a board. Incoming changes are validated for structure, not for good intentions.
  • Losing the key. Lose the link and the board is gone permanently. There is no recovery, no reset email, no support request. That is not an oversight — a recoverable key would mean the server could recover it too.

Burning a board

Burn… asks once to be sure, then deletes the room and every snapshot of it, immediately. Two steps because a mis-click on a shared board should not destroy it. It also signals everyone currently on the board: their canvas clears, their local copy is dropped, and the key is stripped from their address bar. Every share link stops working. Anyone holding a link can do this — if it is a panic button, it has to work for whoever is panicking.

Being precise about what that means: the board becomes unrecoverable through any interface, and what was stored was ciphertext whose key never reached the server. Burning also vacuums the database so the deleted rows are reclaimed rather than sitting on disk until routine maintenance happens to run.

It is still not a forensic wipe. Vacuuming marks space reusable rather than overwriting it, and the original write stays in the database's write-ahead log until those segments recycle. Undecryptable bytes can linger for a while.

Retention

Boards are deleted after 30 days without activity, by a job that runs daily. Worth knowing that the twenty-second sync above counts as activity, so a board left open in a forgotten tab keeps itself alive indefinitely. Web server request logging is switched off for the whiteboard pages, the API, and the relay, so there is no record of who opened which board or when.

Data that no longer exists cannot be leaked, seized, or subpoenaed — retention is a privacy control, not housekeeping.

Why bother

Mostly because it is a satisfying problem. But the honest reason to build it this way is that "trust me, I won't look" is not a security model. Designing so that I cannot look is the only version of that promise which survives me being compromised, careless, or compelled.