System overview
datadata is a library with a clean client/server split. An application talks to it through a session, and two deliberate extension points — the event bus and the storage adapter — decide where and how it runs.
flowchart TB
accTitle: datadata's client/server split
accDescr {
A session sits on top of a client. The client writes a journal and cache to
an optional persistence adapter, and exchanges events with an event bus.
The bus exchanges events with the server, which persists through a storage
adapter. Blob bytes bypass the bus: the client uploads and downloads them
over HTTP through the host, which persists them in an optional object
storage adapter.
}
session["session<br/>live / staging<br/><small>reads, writes, presence</small>"]
client["client<br/><small>optimistic overlay</small>"]
persistence["persistence adapter<br/><small>IndexedDB / memory<br/>optional, shared by tabs</small>"]
bus["event bus<br/><small>WebSocket / in-process</small>"]
server["server<br/><small>authoritative</small>"]
storage["storage adapter<br/><small>SQLite / Postgres / memory</small>"]
objects["object storage adapter<br/><small>R2 / S3 / fs / memory<br/>optional</small>"]
session --> client
client -->|journal + cache| persistence
client <-->|events| bus
bus <--> server
server --> storage
client <-->|"blob bytes, HTTP<br/>via the host"| objects
The parts
Section titled “The parts”- Session — what the application actually holds. Every read and write goes through one, in one of two kinds: a live session writes straight through, a staging session accumulates a changeset to commit. Both expose the same document API — prepare a document, read it, write it, publish presence — so an app switches lanes without rewriting its call sites. Framework bindings like the React integration hand you a session too.
- Client — one per connection, shared by the sessions layered on it. Owns subscriptions and the optimistic overlay, and speaks the protocol; a session’s writes land here before they reach the wire.
- Server — the authority for one folder: validates against schemas, assigns sequence numbers, persists, broadcasts. Enforces limits (document size, rates) and exposes a change callback for automation.
- Event bus — routes protocol events between clients and server. Implementations: WebSocket (one each for the Durable Object and Node hosts), in-process (server-side agents, tests, demos), and a composite that combines both on one server.
- Storage adapter — persistence behind the server: documents, the event log, and Yjs state. Two persistent implementations pass the same conformance suites: SQLite in a Durable Object (production) and Postgres behind the async server (not yet in production); tests use memory.
- Object storage adapter — optional, and the one place bytes live outside the storage adapter: an object store (R2, S3-compatible, filesystem, memory) holding blobs — files referenced from document JSON by immutable handle. The server keeps the catalog and the authorization; the host streams the bytes over HTTP. Without one the server refuses schemas that declare blob fields and nothing else changes.
- Persistence adapter — the storage adapter’s client-side counterpart, and optional: the pending-write journal and the document cache that let a reload replay its writes and render offline. IndexedDB in browsers, memory in tests. Without one the client is memory-only. See offline persistence.
Tabs on the same (folder, principal) namespace share a persistence adapter,
so two offline tabs converge with no server round-trip — the only path in this
architecture where clients exchange data without the server.
Where it runs today
Section titled “Where it runs today”The production shape is Cloudflare Workers + Durable Objects: one Durable Object per folder, embedding the server, its storage, and its WebSocket connections. The extension points exist precisely so that this is a deployment choice, and a second backend now proves it: one Node process over Postgres, serving many folders from one database through the async server. It passes the same conformance suites as the Durable Object; it has not yet run in production.