Reference
Collaboration
Live editing runs over a single WebSocket endpoint, /collab, speaking the
Hocuspocus/Yjs protocol. The editor handles all of this for you — this page is for understanding
the machinery and for connecting from outside the editor.
How a session works
- The client opens a WebSocket to
/collaband requests a document named by thenoteId. - The handshake carries a credential —
"session"(cookie auth, the editor default) or a service key. The connection is rejected unless the account owns the note. Opening a note that doesn't exist is also rejected: notes are born over the REST API, never implicitly. - The server sends the current Yjs state; from then on, updates flow in both directions and merge conflict-free (CRDT semantics). Presence — who's connected, where their caret is — piggybacks on the same connection.
Persistence & the markdown projection
The server persists the document on change, debounced by one second (five seconds at most under
continuous typing). Every store also re-derives the markdown projection — so GET /api/notes/:id is never more than a few seconds behind the live document. When the
last collaborator disconnects, the final state is flushed.
Connecting without the editor
Anything that speaks Yjs can collaborate — an importer, a bot, an AI agent writing into a note. From a backend, authenticate with a service key as the token:
import * as Y from 'yjs'
import { HocuspocusProvider } from '@hocuspocus/provider'
const doc = new Y.Doc()
const provider = new HocuspocusProvider({
url: 'wss://notr.example/collab',
name: noteId, // the document is the note
token: process.env.NOTR_KEY, // service key — backend only
document: doc,
onSynced() {
// append a paragraph to the live document
const fragment = doc.getXmlFragment('default')
const p = new Y.XmlElement('paragraph')
p.insert(0, [new Y.XmlText('Summarized by the import bot.')])
fragment.push([p])
}
}) The same rule as REST applies: service keys never belong in browsers. Browser clients use the
editor with session auth today; consumer-signed per-user tokens are on the roadmap.
Operational notes
- One process, one port — the collaboration server is mounted into the app's HTTP server; no separate deployment to run.
- Isolated by design — the collab layer is self-contained, so it can move to a dedicated service (with document-affinity routing) when scale demands, without API changes.
- Deleting a note while someone is editing doesn't kick the live session, but nothing is persisted afterwards and new connections are refused.
