Live
Live is Anyknown’s realtime/collaborative Capability — multiple Visitors editing, watching, or reacting to the same state in a shared room, conflict-resolved with a CRDT (Yjs) underneath.
Live is split today: the client path works, the server path does not. The browser can open a WebSocket to a room and send/receive ops right now. sdk.live.room() — the server-side handle a handler would use to apply ops, read a snapshot, or push events from your own backend code — throws NotAvailableInV1Error unconditionally in a deployed App. Read this page for what you can build against now, and treat the server half as not-yet-shipped.
What works: the client connection
Section titled “What works: the client connection”liveConnect (exported as connect from @anyknown/app-client) opens a WebSocket to your App’s live gateway at /_anyknown/live/<room> and gives you an event-driven connection object. This is real, running infrastructure — app-runtime routes the WebSocket to a per-room Durable Object (ROOM_DO) that holds the CRDT document, ack’s ops, and fans out to every connected client.
import { liveConnect } from "@anyknown/app-client"
const room = liveConnect("my-room-id", { lastSeenVersion: 0, onUnauthenticated: () => { /* redirect to sign-in, etc. */ },})
room.on("op", (envelope) => { // apply envelope.op to local state})room.on("presence", ({ clientId, state }) => { // another client's awareness state changed})room.on("error", ({ code, message, retryAfter }) => { // e.g. code === "RATE_LIMITED"})
room.send({ opId: crypto.randomUUID(), clientId: myClientId, parentVersion: currentVersion, op: myChange,})
room.setPresence({ cursor: { x, y } })
// laterroom.disconnect()function liveConnect(roomId: string, opts?: ClientLiveOpts): ClientLiveConnection
type ClientLiveOpts = { lastSeenVersion?: number onUnauthenticated?: () => void}
type ClientLiveConnection = { send(input: { opId: string clientId: string parentVersion: number op: unknown ts?: number }): void setPresence(state: unknown): void disconnect(): void on<K extends "op" | "ack" | "presence" | "error" | "close" | "replay-done">( event: K, handler: (data: ClientLiveEventMap[K]) => void, ): () => void // returns an unsubscribe function}The connection reconnects automatically with exponential backoff (capped, up to 12 attempts) on a dropped socket, and replays missed ops from lastSeenVersion on reconnect — you do not need to write your own reconnect or replay logic. A "close" event fires only when you call disconnect() yourself, or after retries are exhausted; an "UNAUTHORIZED" error stops reconnection entirely and calls onUnauthenticated.
Events you can listen for:
| Event | Fires when |
|---|---|
op | A remote op was applied to the room and reached this client |
ack | An op this client sent was accepted and assigned a version |
presence | Another client’s awareness state changed |
error | The gateway rejected something (rate limit, disabled room, etc.) |
close | The connection was deliberately closed or exhausted its retries |
replay-done | Missed-op replay from lastSeenVersion finished on (re)connect |
What does not work yet: sdk.live.room()
Section titled “What does not work yet: sdk.live.room()”The server-side capability type exists in the SDK surface — sdk.live.room(roomId) resolves to a RoomHandle with applyOp, snapshot, subscribe, and awareness methods, mirroring the client shape. None of it is callable in a deployed App today. The binding it depends on (ROOM_DO) is not wired into the App Worker build, so every call to sdk.live.room() throws immediately:
NotAvailableInV1Error: ROOM_DO binding not configured; sdk.live requires features.live=true in planThis is not a runtime fluke or a misconfiguration you can fix from your App — it is the current, intentional state of the server half of this Capability. Wiring it up needs a cloud-api binding change and a tenant-auth design that has not shipped. Do not write handler code that calls sdk.live.room() and expects it to work; there is no App-level setting that unblocks it.
Using Live today
Section titled “Using Live today”Build realtime features entirely from the browser side with liveConnect. Multiple Visitors connecting to the same roomId share state through the CRDT-backed room; your handler code does not need to (and currently cannot) participate server-side. This covers collaborative cursors, live presence, shared editing surfaces, and similar client-to-client patterns — anything that does not require your own backend to originate or validate ops.
Things to know
Section titled “Things to know”-
sdk.live.room()throws today — do not build against it. The clientliveConnectpath is the only working surface. Treat the server-side room API as a documented future contract, not something you can call. -
The client path is real infrastructure, not a mock.
/_anyknown/live/<room>routes to a liveROOM_DODurable Object with CRDT conflict resolution, ack’d ops, presence/awareness, and automatic reconnect-with-replay. -
No client SDK ergonomics beyond
liveConnectyet. There is no framework-specific wrapper (e.g. a React hook) shipped today — you own theon/sendevent wiring in your component. -
A room can reject ops. Watch for
errorevents withcode: "RATE_LIMITED","ROOM_DISABLED", or a storage-cap condition, and handle them rather than assuming everysendsucceeds.