Limitations
Each limitation is tagged:
- By design — a consequence of the model.
- Not yet — wanted, understood, unbuilt.
- Open question — we don’t know the right answer yet; see Open questions.
| Limitation | Status | Notes |
|---|---|---|
| No per-field authorization | By design | The engine authorizes every write and filters every read (Authorization) across three declarative grains — folder roles, docType rules, and per-document sys:access entries — but visibility is whole-document: there are no per-field rules, and no content-based rules (verdicts never read document data). Whether per-field rules should exist is open. |
| Documents cap at 1.9 MB | By design | Both persistent backends cap a document, a single event, and each embedded Y.Doc state at 1.9 MB by default — just under the Durable Object SQLite value limit, and the Postgres adapter keeps the same default on purpose so a document valid in one deployment is valid in every other. Whole-document sync makes huge documents the wrong shape anyway: model large data as more documents. |
| No queries across documents | By design | There is no query language — no select/project over multiple documents. The supported pattern is a derived store: listen to document changes and project them into another document (or another database), then query that. |
| No partial replication within a document | By design | Subscriptions are per-document; a document syncs whole — including all its embedded Y.Docs (full states on first subscribe, state-vector diffs on reconnects). There is no per-Y.Doc lazy loading. Folder = authority unit, document = sync unit. |
| Folders are sized for thousands of documents | By design | A folder’s listings — sys:index and sys:trash — are documents like any other, so a subscriber receives every entry it may read, built from one scan of the folder when it subscribes. That cost grows linearly with the folder, and the design target is up to a few thousand documents per folder. Past that, split the data across more folders. |
| No cross-folder sync or federation | By design | A folder is one server’s world; nothing spans folders. |
| No P2P, no decentralized authority | By design | The server-authoritative model is the point — see Design decisions. |
| No E2E encryption | By design | Server-side validation requires the server to read the data. |
| Offline durability is best-effort | By design | The persisted write queue and document cache live in browser storage, which browsers evict under pressure, prune for stale origins, and discard in private browsing. The degraded mode is memory-only behavior, never corruption — but “survives a reload” is a practical guarantee, not a durable one. |
| Long-offline unguarded writes land last-writer-wins | By design | A queued unguarded JSON write replays silently on reconnect, however old. Protocol-legal, occasionally surprising. Offline work that deserves review belongs in a staged session, whose reconnect produces a changeset instead. |
| Cached reads outlive an access revocation | By design | A revoked principal can still read cached documents offline — the client already saw the data — until the next connection replaces them with notfound. The persistence namespace must be (folder, principal)-scoped and clear() belongs in logout. |
| Blobs default to 16 MiB | By design | A blob is accepted at upload only up to maxBlobSizeBytes, which defaults to the ceiling a document export can carry inline — so every blob the platform holds stays exportable. A host may raise it consciously, trading away export of the larger blobs until a streaming export exists. |
| No blobs without a served host | By design | The backendless in-memory client has no blob lane: a blob’s download URL is a plain string for <img src>, which no in-process transport can serve. Demos and tests that need files run against a real host. |
| Blob uploads stream through the host | Not yet | Bytes go client → host → object store, capped by the host’s request body limit. Presigned direct-to-store uploads and multipart are future work behind the same client contract, as are signed download URLs and serving user content from a cookie-less origin; today the response headers make same-origin serving safe. |
| No event-log compaction | Not yet | Document and Yjs logs are append-only and unbounded (doc:get-events pages through them). The most concrete scaling gap. Yjs’s own item-level garbage collection is also unmanaged: deleted-item tombstones are not reclaimed from stored Y.Doc state, since native GC is only safe once every peer has seen the delete and datadata does not coordinate that condition across its stored log. See Storage & event log. |
| No named changesets | Open question | A changeset occupies one field of its host document, shared by every session opened over that field. There is no way to address several changesets by id, nor an API to create and drop them — deliberately deferred until the apps demand it. |
| Projections are per-document, read-mostly | Open question | A projection layer reshapes a document into more ergonomic views — but it operates on the full synced document (it is not partial sync), and writable lenses are very alpha. Whether projections should be writable at all is open. |
| JS/TS only | Open question | datadata is a TypeScript library on both sides of the wire. Whether other-language clients are wanted is untested — nobody has asked. Supporting them would also turn the wire protocol into a public contract, versioned and held stable for implementers outside this repo; today it is documented, not promised. |
If one of these is the thing you came to check — that’s the page working as intended. If you think one of the classifications is wrong, that’s a conversation worth having.