FluxyChat

Core SDK

Wire protocol (v1)

Room sockets, frames, and what the Worker actually does.

This page is the contract. Use it when you wire a client or review a competitor.

One room, two sockets when you need Yjs

A room is one Durable Object. Chat, presence, feeds, threads, server_event, and derived share a JSON WebSocket:

GET /ws/room/:roomId?token=<JWT>

Yjs (FluxyYjsProvider) opens a second, binary WebSocket on the same route with replay off. Chat plus Tiptap means two connections to one object. JSON history and CRDT sync stay out of each other's way.

Transport falls websocket to sse to polling. Reconnect exposes connectionState.nextRetryAt.

Auth on the wire

WhoHow
MemberJWT in ?token= (or Authorization on REST). Roles: member / moderator / admin / owner.
GuestPOST /tokens/anonymous with pk_ (provider publishableKey), or POST /public/rooms/:id/guest-session then the same ?token=. Public rooms. Stable guestKey in localStorage on the guest-session path.
Non-memberHTTP 403 or close 1008.

Keep fc_… API keys on the server. Mint JWTs with POST /auth/token. Refresh with client.setToken() / updateToken(), or pass a function to authTokenProvider.

Frames that match the job

JobFrame / APIWrong primitive
Pointerstype: "cursor" / sendCursor (up to 600/min)Broadcast at 60 Hz
Rosterpresence_patch, presenceMembersFull roster above 250 users (presenceKind becomes aggregate)
ChatJSON messages + clientMessageIdFeeds or comment threads
Ephemeral pulseclient-ephemeral-* (skips webhooks)Persisting it
DocumentBinary Yjs on the second socketA second CRDT backend
REST inboxGET /inboxLiveblocks inbox
Inbox socketGET /ws/inbox?token= (connectInbox)User-channel only
Late-joiner JSONsetDerivedState / derivedState (16 KiB, 30/min)Stuffing a graph into chat history
Room extensionsGET/PUT /rooms/:id/extensions (extension_snapshot, max 5)Eval on the shared Worker
Private linesvisibility + visibleTo on sendFiltering only in the UI
Agent on the timelineinvokeAgentThe keyless copilot panel (local UI only)

What this layer is for

The room is the product artifact: a deal, a board, a classroom, a dispatch view. You do not buy Ably for this combination. You do not buy Liveblocks unless the artifact is only a document. You do not buy Stream unless you need MAU-scale consumer chat and mobile moderation.

Live audio is a media plane (LiveKit or an SFU). voice-stage is roster signaling. Devices POST JSON (iot.reading, fleet.gps_update). Game ticks are server-authoritative input. Publish rules live in your Worker / fluxy.config onPublish.

REST companion

History, uploads, geofences, and cross-org are HTTP. Routes: openapi.yaml (text/yaml).

On this page