examples/live-notes Public
---id: note-roomtype: conceptowner: justinreviewed: 2026-10-16component: note-roomdescribes: - src/note-room.ts - src/room.ts - src/index.ts---# The note roomEach note is one Durable Object of class `NoteRoom`, found with `idFromName(noteId)` through the`NOTES` binding. Everyone who opens the same note id reaches the same object, wherever theyare, and that object is the single sequencer described in [[conflict-strategy]].## Two layers`NoteRoom` (`src/note-room.ts`) is the part that knows about the runtime: it accepts sockets,reads frames, sends replies and broadcasts, and owns the SQLite storage. Everything it decidesis delegated to `Room` (`src/room.ts`), which takes a `NoteStore` and returns, for eachmessage, a reply for the sender and an optional broadcast for the others. The tests drive`Room` with an in-memory store, so they run in plain Node without workerd.## StorageThree tables in the object's SQLite database:- `note`: one row with the text and its version. `Room` caps the text at 100,000 UTF-16 code units, so the row and every snapshot frame stay small.- `ops`: the op log, keyed by the version each op produced, trimmed to the last 200.- `snapshot`: one row with the version of the last copy saved to R2.`commit()` writes the new text, appends the op and trims the log inside one `transactionSync`,so a note and its log never disagree. The room only updates its in-memory copy after thewrite.## SnapshotsEvery 100 versions `Room` asks for the note to be copied to the `SNAPSHOTS` bucket.`webSocketMessage` sends the ack and the broadcast first, then writes the copy and records itsversion in `snapshot`. A failed write is logged and tried again on the next edit, and only oneruns at a time. See [[snapshots]].## HibernationSockets are accepted with `ctx.acceptWebSocket`, the Hibernation API. When nobody has typed fora while the runtime evicts the object but keeps the sockets open; the next frame wakes it,runs the constructor, and calls `webSocketMessage`. The constructor creates the tables ifneeded and loads the note, so waking up is the same as starting fresh. Nothing the room needslives only in memory.Broadcasting uses `ctx.getWebSockets()`, which lists every accepted socket even after a wake,so there is no connection list to rebuild.## Reads without a socket`GET /notes/:id` calls the `read()` RPC method and returns `{ id, text, version }`. It reads theroom's in-memory copy, which is always the last committed state.`GET /notes/:id/snapshots` and `GET /notes/:id/snapshots/:version` do not reach the room at all:the Worker reads them from R2 under `idFromName(id)`, the same id the room saves them under.