A Yjs CRDT provider that syncs documents over the AT Protocol. Documents are stored as ATProto records and real-time updates are delivered via Jetstream.
A new algorithm for resuming rooms from update records has been introduced, which should increase reliability when multiple clients are using the app at the same time. Existing rooms will still be compatible but the initial download may take longer. Please let me know if you experience any issues with the new resume behavior.
- Each collaborative session is represented by a room record (
io.github.uwx.yjs.room) in the user's ATProto repository. - Document updates are written as
io.github.uwx.yjs.updaterecords, with small updates stored inline and large updates (>1 MB) stored as blobs. - Real-time sync is achieved by subscribing to Jetstream for new update and awareness records.
- Rooms can optionally be encrypted (AES-256-GCM with PBKDF2 key derivation) and restricted to an allowlist of DIDs.
- Awareness (cursor positions, user presence) is supported via
io.github.uwx.yjs.awarenessrecords.
pnpm install y-atproto
import { Doc } from "yjs";
import AtprotoProvider from "y-atproto";
const ydoc = new Doc();
const { provider, room } = await AtprotoProvider.createNew({
secret: "optional-password",
repo: did,
handler: agent,
ydoc,
autosaveInterval: 1000,
});const provider = await AtprotoProvider.resume({
secret: "optional-password",
repo: did,
handler: agent,
room: "at://did:plc:.../io.github.uwx.yjs.room/...",
ydoc,
autosaveInterval: 1000,
});| Option | Type | Description |
|---|---|---|
secret |
string |
Optional password for end-to-end encryption |
repo |
ActorIdentifier |
DID or handle of the authenticated user |
handler |
FetchHandler | KittyAgent |
Authenticated AT Protocol agent |
ydoc |
Doc |
Yjs document to sync |
autosaveInterval |
number |
Milliseconds between flushes of queued updates |
resendAllUpdates |
boolean |
If true, always send the full document state |
fullUpdateRate |
number |
Probability (0-1) of sending a full update on each flush (default 0.1) |
awareness |
Awareness |
Custom Yjs awareness instance to reuse |
awarenessLive |
boolean |
If true, broadcast awareness on every local change instead of on an interval |
awarenessBroadcastInterval |
number |
Milliseconds between awareness broadcasts (default 29000) |
- demos/monaco-react -- Collaborative Monaco editor. Live at y-atproto-demo-monaco.wisp.place
- demos/react-prosemirror -- Collaborative ProseMirror editor. Live at y-atproto-demo-prosemirror.wisp.place
- demos/excalidraw -- Collaborative whiteboard using Excalidraw. Live at y-atproto-demo-excalidraw.wisp.place
Requires pnpm.
pnpm install
pnpm build
MIT