Concepts
Rooms, primitives, and how people and agents share one room Durable Object.
FluxyChat is a room on Cloudflare: one Durable Object plus D1. Map roomId to a deal, a classroom, a board, a dispatch view. Chat and presence share a JSON WebSocket. Yjs uses a second binary connection to the same object. Your auth and data model stay yours. Member JWTs (or a public-room guest session) open the socket. Hosted is beta. Self-host is MIT on your Cloudflare account.
Cloudflare Think treats the agent as the unit of compute. Document collab products treat the file as the unit. We treat the room as the unit: humans and invokeAgent write the same chat timeline. Copilot UI (AiChat) stays off that timeline.
Building blocks
| Concept | FluxyChat |
|---|---|
| Project | Tenant. API keys (fc_…, server-only), rooms, agents, quotas. |
| Room | One Durable Object. Chat JSON WebSocket; Yjs is a second binary connection on the same /ws/room/:id route. |
| Primitives | Presence, Broadcast, Storage, Feeds, Threads, Chat, Polls, Derived, Visibility. |
| Users | Humans (member JWT or guest) and AI (copilot, room peer, workflow). |
| Features | Comments, multiplayer editors, notifications, copilots, AI collaboration. |
| Platform | Worker, dashboard, @fluxy-chat/sdk, react, ui, ui-kit, vue, svelte. |
Primitives (pick the right one)
| Primitive | Means | API | Stored? |
|---|---|---|---|
| Presence | Now: who is here, cursors, selections, agentStatus | sendCursor, useMyPresence / presence_patch, presenceMembers | No |
| Broadcast | Pulse: one-shot signals | sendClientEvent / useBroadcastEvent | No |
| Storage | The document | FluxyYjsProvider, useStorage, useMutation, LiveFile | Yes (Yjs on the room DO) |
| Feeds | Activity / agent / n8n logs | useFeeds, POST /rooms/:id/feeds | Yes (D1, ≠ chat) |
| Threads | Contextual comments | useThreads, /comment-threads | Yes |
| Chat | Timeline + nested replies | useChat, useThread, parentId | Yes |
| Polls | Ballots on the timeline (and /polls records) | createPoll / votePoll, POST /polls isAnonymous | Yes |
| Derived | Late-joiner JSON bag (not a CRDT) | setDerivedState / derivedState | Yes (DO storage) |
| Visibility | Who may see a chat line | visibility / visibleTo on send | Yes (D1) |
Do not put 60 Hz pointers on Broadcast. Do not mix Feeds with messages. Do not mix comment pins (useThreads) with chat parentId replies (useThread). Do not hide private lines only in the UI. Set visibility on send.
Wire limits and v1 non-goals: Wire protocol.
Two kinds of AI
| Kind | API | Writes |
|---|---|---|
| Copilot (side panel) | RegisterAiKnowledge + useAiChat + <AiChat /> | Local UI only (keyless mock) |
| Room peer | invokeAgent on useChat | Chat messages |
| Workflow | API key POST …/feeds/…/messages | Feed + server_event |
Auth
Browser: publishableKey (pk_) on FluxyRealtimeProvider (anonymous JWT via POST /tokens/anonymous), member JWT, or FluxyChatClient.joinPublicRoomAsGuest(workerUrl, publicRoomId) (stable guestKey in localStorage). Guest writes are on by default; set PUBLIC_GUEST_READ_ONLY=true to lock.
Server: project API key (fc_…) mints member JWTs (POST /auth/token). pk_ is rejected there (403). Never put fc_… in the client.
Verticals on the same bus
IoT, fleet, game ticks, voice stage, deal quorum: all server_event or chat on the same room WebSocket. Gallery has a copy-paste app for each. Those modules are optional. The product is the room.
Next: Getting started · CLI and examples