Website • Documentation • Crate • Examples • Contributing
Why crdt-kit?
Traditional sync solutions break when devices go offline. CRDTs solve this at the data structure level — every replica can be updated independently, and merges always converge to the same result, guaranteed by math, not by servers.
crdt-kit is built for resource-constrained, latency-sensitive environments where existing solutions (Automerge, Yjs) add too much overhead:
- Zero heap allocations on node IDs (
u64instead ofString) no_std+alloc— runs on bare metal, ESP32, Raspberry Pi- 11 CRDT types with delta-state sync, Hybrid Logical Clocks, and versioned serialization
- Single dependency-free crate (core) — optional
serdeandwasmfeatures - Battle-tested — 137 unit tests, 14 integration tests, property-based testing (proptest), 6 fuzz targets
+-----------+ +-----------+ +-----------+
| Device A | | Device B | | Device C |
| (offline)| | (offline)| | (offline)|
+-----+-----+ +-----+-----+ +-----+-----+
| | |
| local edits | local edits | local edits
| | |
+--------+--------+--------+--------+
| |
v v
+-----------+ +-----------+
| merge | | merge |
+-----------+ +-----------+
| |
v v
Same state! Same state! <-- Strong Eventual Consistency
Quick Start
[]
= "0.5"
use *;
// Two devices, working offline — NodeId is u64 (zero heap allocs)
let mut phone = new;
phone.increment;
phone.increment;
let mut laptop = new;
laptop.increment;
// When they reconnect — merge. Always converges.
phone.merge;
assert_eq!;
Delta Sync (bandwidth-efficient)
use *;
let mut edge = new;
edge.increment_by;
let mut cloud = new;
// Send only what cloud doesn't have
let delta = edge.delta;
cloud.apply_delta;
assert_eq!;
With Serde
[]
= { = "0.5", = ["serde"] }
no_std (embedded / bare metal)
[]
= { = "0.5", = false }
Available CRDTs
Counters
| Type | Description | Real-world use |
|---|---|---|
GCounter |
Grow-only counter | Page views, IoT sensor events, download counts |
PNCounter |
Increment & decrement | Inventory stock, likes/dislikes, seat reservations |
Registers
| Type | Description | Real-world use |
|---|---|---|
LWWRegister |
Last-writer-wins (HLC) | User profile fields, config settings, GPS location |
MVRegister |
Multi-value (shows conflicts) | Collaborative fields, version tracking |
Sets
| Type | Description | Real-world use |
|---|---|---|
GSet |
Grow-only set | Seen message IDs, tags, audit logs |
TwoPSet |
Add & permanent remove | Blocklists, revoked tokens |
ORSet |
Add & remove freely | Shopping carts, todo lists, chat members |
Maps
| Type | Description | Real-world use |
|---|---|---|
LWWMap |
Last-writer-wins per key | Sensor config, user preferences, feature flags |
AWMap |
Add-wins (concurrent add beats remove) | Device registries, permission tables, metadata |
Sequences
| Type | Description | Real-world use |
|---|---|---|
Rga |
Replicated Growable Array | Playlists, kanban boards, ordered lists |
TextCrdt |
Collaborative text | Google Docs-style editing, shared notes |
Traits
| Trait | Description |
|---|---|
Crdt |
Core merge semantics (commutative, associative, idempotent) |
DeltaCrdt |
Efficient delta sync — send only what changed (all 11 types) |
Versioned |
Schema versioning for serialization envelopes (all 11 types) |
v0.5 Highlights
Zero-allocation Node IDs
All CRDTs use NodeId (u64) instead of String for replica identity. This eliminates heap allocations on every operation — critical for embedded and IoT targets.
// Before (v0.4): heap allocation on every construct
let mut c = new;
// After (v0.5): zero-cost u64 identity
let mut c = new;
Hybrid Logical Clocks (native)
LWWRegister uses HybridTimestamp natively — physical clock + logical counter + node_id for total ordering. No more dual-timestamp correctness bugs.
use HybridClock;
use *;
let mut clock = new;
let mut reg = new;
reg.set;
New Map Types
LWWMap and AWMap complete the CRDT toolbox for key-value workloads:
use *;
use HybridTimestamp;
// LWWMap: per-key last-writer-wins
let mut config = new;
let ts = HybridTimestamp ;
config.insert;
// AWMap: add-wins (concurrent add beats remove)
let mut registry = new;
registry.insert;
Examples
Edge Computing & IoT
use *;
// Edge sensor nodes count events independently
let mut sensor_a = new;
let mut sensor_b = new;
sensor_a.increment_by; // 142 events detected
sensor_b.increment_by; // 89 events detected
// Gateway merges — order doesn't matter
sensor_a.merge;
assert_eq!; // exact total, no double-counting
// Delta sync: only send what changed (saves bandwidth on LoRa/BLE)
let mut gateway = new;
let delta = sensor_a.delta;
gateway.apply_delta;
assert_eq!;
Guarantees
All 11 CRDTs satisfy Strong Eventual Consistency (SEC):
| Property | Meaning | Why it matters |
|---|---|---|
| Commutativity | merge(a, b) == merge(b, a) |
Order of sync doesn't matter |
| Associativity | merge(a, merge(b, c)) == merge(merge(a, b), c) |
Group syncs however you want |
| Idempotency | merge(a, a) == a |
Safe to retry — no duplicates |
Verified by 151+ tests (137 unit + 14 integration), property-based testing (proptest: commutativity, associativity, idempotency, and delta equivalence for all 11 types), and 6 fuzz targets for crash resistance under arbitrary input.
Benchmarks
Feature Flags
| Feature | Default | Description |
|---|---|---|
std |
Yes | Standard library support |
serde |
No | Serialize/deserialize all CRDT types |
wasm |
No | WebAssembly bindings via wasm-bindgen |
For no_std, disable defaults: default-features = false
Roadmap
- G-Counter, PN-Counter
- LWW-Register (HLC-native), MV-Register
- G-Set, 2P-Set, OR-Set
- LWW-Map, AW-Map (v0.5)
- RGA List (ordered sequence)
- Text CRDT (collaborative text, thin
Rga<char>wrapper) -
no_stdsupport (embedded / bare metal) -
serdeserialization support - Delta-state optimization (11/11 types with
DeltaCrdttrait) - HLC (Hybrid Logical Clock) — native
HybridTimestamp -
NodeId(u64) — zero heap allocations - Tombstone compaction for ORSet
- Error handling for RGA/TextCrdt (
RgaError,TextError) - WASM bindings (GCounter, PNCounter, LWWRegister, GSet, ORSet, TextCrdt)
- Fuzz testing (6 targets via cargo-fuzz)
- IoT Sensor Dashboard example (all 11 CRDTs)
-
Versionedtrait withCrdtTypeenum for all 11 types -
HybridClockderivesDebug + Clone, acceptsNodeId(u64) - RGA merge optimization (two-phase: tombstones then inserts, no index-shift loop)
- Memory footprint benchmarks for embedded use case
- Network transport layer (TCP, WebSocket, QUIC)
- Sync protocol (delta-based replication)
- AWMap tombstone compaction
- Rope-backed RGA for large documents (>10K elements)
Contributing
Contributions are welcome! Please read CONTRIBUTING.md before submitting a pull request.
License
Dual-licensed under your choice of:
- MIT — LICENSE-MIT
- Apache 2.0 — LICENSE-APACHE