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

  1. The client opens a WebSocket to /collab and requests a document named by the noteId.
  2. 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.
  3. 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:

server-side client
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.