examples/live-notes Public
id: wire-protocol
type: concept
owner: justin
reviewed: 2026-10-13
component: ops
describes:
- src/protocol.ts
Wire protocol
Editors and a room exchange JSON text frames over the WebSocket at /notes/:id/ws. Versions
are the room's: the number of ops it has applied to the note.
From an editor:
{ "type": "op", "base": 41, "op": { "type": "insert", "pos": 4, "text": "oat " } }
{ "type": "op", "base": 41, "op": { "type": "delete", "pos": 0, "count": 3 } }
From the room:
| type | fields | when |
|---|---|---|
snapshot |
text, version |
on connect, and when an editor's op can't be used |
ack |
version |
your op was applied, or turned out to change nothing |
op |
version, op |
someone else's op, already transformed, at that version |
error |
message |
the frame was not an op message |
parseClientMessage() checks every field and copies only the ones it knows, so a frame with
extra fields still works and a malformed one is answered with error and changes nothing.
An editor sends its next op only after the ack for the previous one; see [[conflict-strategy]] for why that keeps the room's job simple.