lazily-spec
Language-agnostic wire protocol specification for the lazily reactive signals family.
This site is the rendered companion to the lazily-spec repository. It defines the canonical message schemas shared across every lazily implementation:
- lazily-rs (Rust)
- lazily-py (Python)
- lazily-zig (Zig)
- @lazily/signaling (TypeScript / Cloudflare Worker)
Cell Model
Upstream of every transport, the Cell Model fixes how a cell’s value
converges. A cell is either single-writer (local/direct, no merge) or
multi-write, and a multi-write cell carries a pluggable merge: <mechanism>
attribute. CRDT is the first multi-write merge mechanism (merge: crdt), not the
only one — lww, ot, lease, and custom are reserved alongside it. All transports
below carry cells classified by this model.
Protocol Layers
| Layer | Spec | Schema |
|---|---|---|
| IPC (Snapshot + Delta) | Wire Protocol § IPC | snapshot.json, delta.json |
| Cross-language FFI | Wire Protocol § FFI | ffi.json |
| Signaling (WebSocket) | Wire Protocol § Signaling | signaling.json |
| Distributed (CRDT) | Wire Protocol § Distributed | distributed.json |
| Capability negotiation | Wire Protocol § Capability Negotiation | inline |
Every layer in this matrix is required of every binding. The Distributed CRDT
row and the required keyed cell collections layer
are unconditional. The C-ABI FFI row is required by default with a narrow platform
carve-out (a binding whose runtime cannot host a native in-process C ABI — e.g.
browser/Worker JS — declares ffi = none and interops over the wire instead). The
thread-safe and async reactive contexts are required where the platform supports
them (a platform that structurally lacks threading or suspendable async declares
thread_safe = none / async = none); the shared-memory payload path is required
where the platform supports it, with an I/O-channel fallback (Inline payloads over
IPC/WebSocket/WebRTC) when it does not. See the
Binding Conformance Matrix for the full
MUST/MAY breakdown and the carve-out terms.
Wire Format
All messages use JSON with serde-compatible tagging ("type" discriminant). Future
binary codecs (bincode, postcard, protobuf) encode the same schemas — the JSON representation
is normative.
Schema Format
Schemas are provided as JSON Schema (Draft 2020-12). Each implementation must validate against these schemas. See JSON Schemas.
Scope & non-goals
This repo extracts the cross-language, wire-protocol, and behavioral sections
from lazily-rs/SPEC.md into a standalone reference. Every lazily-rs feature
area is accounted for here exactly once: either normatively specified (with a
link below) or explicitly marked Rust-specific. Rust-specific internals remain
in the Rust crate and are intentionally out of scope.
Covered (normative, cross-language)
Out of scope (Rust-specific implementation)
These lazily-rs features are implementation choices, not cross-language contracts. Other bindings pick their own; they MUST meet the normative contracts above but need not mirror Rust’s approach.
| lazily-rs area | Why out of scope |
|---|---|
Context / ThreadSafeContext lock strategy (ReadStrategy, inline seqlock, typed cache fast-path) | Internal scheduling/locking; each binding picks its own concurrency strategy. The existence of the thread-safe and async context surfaces is required where the platform supports it (Wire Protocol § Concurrency layers are required); only the lock internals are out of scope |
SlotId internal representation | Volatile internal handle; the wire-stable identity is NodeId / NodeKey |
instrumentation (lock-site tracking) | Rust diagnostics |
str0m_backend / str0m_net | Concrete Rust WebRTC backend (str0m crate); only the transport abstraction is cross-language |
| Performance benchmarks | Rust-specific measurement |
lazily-serde type-erasure internals | Rust serialization approach; the wire shape, not the codec implementation, is normative |
Versioning
Protocol versioning follows the IPC capability negotiation: each session exchanges
{ protocol_id, protocol_major_version, codec } before any graph state flows. A major version
bump is a breaking change; minor additions are additive.