Documents and Deltas
Strata stores each document as a lattice ORMap. The server merges the state in
each delta message and saves the merged map. It sends the submitted encoded
state to the other applications that opened the document. Each receiving
application must merge that state into its local document.
When an application opens a document, Strata sends a full snapshot. The v0
delta message also contains full encoded ORMap state. It does not contain
only the changed operation.
Typed documents
Section titled “Typed documents”strata_document provides optional types for ORMaps that contain LWW
registers. A field has a key, JSON encoder, decoder, and document type tag.
Gleam checks writes. Reads return Result because another application can
send an incompatible value.
The server does not receive or enforce the schema. The schema does not change the v0 protocol or stored data format.
Each typed document owns a logical clock. A local write advances that clock, and merging remote state raises it to at least every observed register timestamp. Modern lattice registers use the writer's replica ID to resolve equal-clock concurrent writes deterministically.
import gleam/dynamic/decodeimport gleam/jsonimport strata_documentimport strata_document/schema
pub type Board
pub fn title() -> schema.Field(Board, String) { schema.field("title", json.string, decode.string)}
let document = strata_document.new("tab-1")let assert Ok(document) = strata_document.set(document, title(), "Sprint board")let assert Ok(Some("Sprint board")) = strata_document.get(document, title())Add a document view
Section titled “Add a document view”Document(tag) contains CRDT data. It does not contain UI code.
strata_document targets Erlang and JavaScript, so it has no Lustre
dependency.
An application can put the schema and its default view in companion modules:
strata_kanban/├── doc_schema.gleam # fields and document tag└── doc_view.gleam # default Lustre surfaceThe view accepts the typed document and a set of host actions:
pub fn surface( cards: Document(CardDocument), actions: Actions(msg),) -> Element(msg)The msg type belongs to the host. The same surface can run in the
standalone kanban app or inside the workspace app. The host connects each
action to its own update function.
Use a renderer parameter when one context needs another view. For example, a
workspace pane can add a document id and connection data around the standard
surface. A compact list can pass another renderer for the same
Document(CardDocument).
The examples use this pattern in:
examples/kanban/src/strata_kanban/doc_view.gleamexamples/todo/src/strata_todo/doc_view.gleamexamples/workspace/src/strata_workspace/doc_view.gleam
How document sync works
Section titled “How document sync works”- The application sends
joinfor a document id. - The server locates the document in its persistent store.
- The server replies with the current full snapshot.
- The application merges the snapshot into its local state and edits locally.
- The application sends encoded ORMap state in a
deltamessage. - The server merges and saves the accepted state.
- The server sends the submitted encoded state to the other applications.
- Each receiving application merges that state into its local document.
Snapshot versus delta
Section titled “Snapshot versus delta”snapshot | delta | |
|---|---|---|
| When | On every join and reconnect | On every edit |
| Contains | Whole document | Encoded ORMap state |
| Application action | Merge into local state | Merge into local state |
The server sends a delta to the other sockets on the document. It does not send the delta back to the socket that submitted it.
An unknown document returns a snapshot with state: null. Joining an unknown
document does not create stored state. The first valid delta stores the
document.
Conflict and removal semantics
Section titled “Conflict and removal semantics”Typed fields use LWW registers. Higher logical clocks win; equal clocks use the lexicographically greater replica ID. Removing a field uses ORMap observed-remove semantics, so a concurrent write wins over a removal.
This differs from Watershed's typed SharedMap: Watershed sends optimistic map
operations through a central sequencer, which supplies a global order for
conflicting sets and deletes. Strata clients resolve conflicts by merging
CRDT state, but the server still uses one store owner on one node. This
does not provide replication between Strata servers; see
single-node deployment.
Snapshots use a single frame on every join and reconnect. The default 16 MiB encoded-document growth budget does not guarantee that a snapshot fits your proxy or client limits. Chunking and partial-transfer resume remain deferred in #5. Use the snapshot sizing guidance before increasing the budget.