Skip to content

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.

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 } })
// later
room.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:

EventFires when
opA remote op was applied to the room and reached this client
ackAn op this client sent was accepted and assigned a version
presenceAnother client’s awareness state changed
errorThe gateway rejected something (rate limit, disabled room, etc.)
closeThe connection was deliberately closed or exhausted its retries
replay-doneMissed-op replay from lastSeenVersion finished on (re)connect

What does not work: server-side sdk.live.room()

Section titled “What does not work: server-side sdk.live.room()”

The server-side room API is retired in v1 — realtime is client-only, via liveConnect. sdk.live.room() has been removed from the public SDK type, so newly built Apps cannot even compile a call to it. Older bundles built before the removal hit a runtime tombstone instead:

NotAvailableInV1Error: server-side sdk.live is retired in v1 (client-only realtime via liveConnect); ROOM_DO binding is not configured in deployed Apps

This is not a runtime fluke or a misconfiguration you can fix from your App, and it is not unlockable through any plan or App-level setting — it is the intentional v1 posture. Bringing the server half back 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().

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.

  • sdk.live.room() throws today — do not build against it. The client liveConnect path 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 live ROOM_DO Durable Object with CRDT conflict resolution, ack’d ops, presence/awareness, and automatic reconnect-with-replay.

  • No client SDK ergonomics beyond liveConnect yet. There is no framework-specific wrapper (e.g. a React hook) shipped today — you own the on/send event wiring in your component.

  • A room can reject ops. Watch for error events with code: "RATE_LIMITED", "ROOM_DISABLED", or a storage-cap condition, and handle them rather than assuming every send succeeds.