Skip to content

Documents all the way down

Most sync engines juggle several kinds of things, each with its own API: relational engines sync rows but evolve schema through out-of-band migrations; document engines separate data from the schemas that type it; nearly all keep the engine’s own sync state on a side channel apart from your data. datadata has one kind of thing. Everything is a document, and there is one small API for all of them.

User documents hold application data. A document has an id, a type, a server-assigned sequence number, and JSON data. The whole API is a handful of calls — createDocument and updateDocument to write, getDocument to read, and an onDocumentChange subscription to follow along — and you use the same ones for every kind of document below.

Schemas are documents. A document type — task, say — gets its schema from a document at sys:schema:task, itself a document of type sys:schema. Defining a new type means writing that document, through the same createDocument/updateDocument calls as any other. Schemas validate against a meta-schema, carry their own migration history, and sync to clients like any other document. See Schemas as documents.

System state is documents. The engine exposes its own state as read-only documents you can subscribe to — the folder index (sys:index), the trash (sys:trash), a live view of staged work (sys:session), and more. These are synthesized read-models, never written directly — but to a client they’re just documents. System documents enumerates the full set.

Presence is documents too. Ephemeral per-participant state — who’s here, where their cursor is, what they’re streaming — lives in a companion sys:presence:<presenceType>:<docId> document that rides the same subscribe/read/update path as everything else. What sets it apart is that it’s never stored durably: the server keeps only an in-memory aggregate, and each client is the source of truth for its own cells. See Presence.

The API stays tiny. Subscribe, read, create, update. Learn it once and you can manipulate data, evolve schemas, and introspect the engine. There is no separate admin API, migration tool, or metadata endpoint to learn.

Everything is referential. Documents point at other documents by id — including schemas (sys:schema:<type>) and staged work (sys:stage:<docId>). A small set of concepts composes instead of multiplying.

UI and agents get introspection for free. A devtools panel, a “pending changes” sidebar, or an AI agent inspecting its own staged edits all work the same way: subscribe to a system document and render it.

The data model is extensible at runtime. Because schemas are documents, anything connected to a folder — application code, a migration, or an AI agent — can design a new document type, write its schema, and start creating documents of that type, with no out-of-band tooling or redeploy. And since a schema is just a document, a new type can be staged in a session alongside the very documents that use it and committed atomically.