examples/live-notes Public
---id: wire-protocoltype: conceptowner: justinreviewed: 2026-10-13component: opsdescribes: - src/protocol.ts---# Wire protocolEditors and a room exchange JSON text frames over the WebSocket at `/notes/:id/ws`. Versionsare the room's: the number of ops it has applied to the note.From an editor:```json{ "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 withextra 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.