Conflict-Free Replicated Data Types for Rust
Lightweight (~50KB), no_std compatible, optimized for IoT, edge computing, WASM, and local-first architectures.
cargo add crdt-kit --features serde What are CRDTs?
Conflict-Free Replicated Data Types are data structures that can be replicated across multiple devices, updated independently and concurrently, and merged automatically without conflicts.
Unlike traditional databases that require a central server to coordinate writes, CRDTs guarantee that all replicas will converge to the same state regardless of the order in which updates are received.
This makes them ideal for offline-first apps, peer-to-peer systems, IoT networks, and any scenario where devices need to work independently and sync later.
Three devices create data independently while disconnected from the network.
Why crdt-kit?
Built for resource-constrained, latency-sensitive environments where existing solutions add too much overhead.
Features
no_std
Runs on bare metal, Raspberry Pi, ESP32. All types work with #![no_std] + alloc.
Delta Sync
Only send what changed. DeltaCrdt trait for GCounter, PNCounter, ORSet. Minimizes bandwidth on LoRa, BLE.
WASM
First-class wasm-bindgen bindings. Same CRDTs in browser, Deno, Node.js, and Rust backend.
Migrations
Transparent, lazy migrations on read. #[crdt_schema] + #[migration] proc macros. Deterministic.
Storage
Three backends: SQLite (bundled), redb (pure Rust), memory. Event sourcing, snapshots, compaction.
Codegen
Define entities in TOML, run crdt generate. Get models, migrations, repositories, events, sync.
Serde
Serialize/Deserialize for all 9 CRDT types. JSON, MessagePack, Postcard, CBOR — any serde format.
Events
Full event log with append, replay, snapshots, compaction. EventStore trait on all backends.
Dev Tools
CLI: status, inspect, compact, export, generate, dev-ui. Web panel for visual inspection.
How It Compares
| crdt-kit | Automerge | Yjs / Yrs | |
|---|---|---|---|
| Language | Rust | Rust | JS / Rust |
| Zero deps (core) | ✓ | 30+ | N/A |
| no_std | ✓ | ✗ | ✗ |
| WASM | ✓ | Partial | Native |
| Storage | SQLite, redb, mem | Custom | N/A |
| Migrations | Automatic | Manual | N/A |
| Delta sync | ✓ | ✓ | ✓ |
| Event sourcing | ✓ | ✗ | ✗ |
| Code generation | ✓ | ✗ | ✗ |
| CLI | ✓ | ✗ | ✗ |
| Size | ~50KB | ~500KB+ | ~150KB |
9 CRDT Types
From counters to collaborative text. Every type is Send + Sync, serde-ready, mathematically convergent.
Counters
Registers
Sets
Quick Start
[dependencies]
crdt-kit = "0.3" use crdt_kit::prelude::*;
// Two devices, working offline
let mut phone = GCounter::new("phone");
phone.increment();
phone.increment();
let mut laptop = GCounter::new("laptop");
laptop.increment();
// Reconnect — merge. Always converges.
phone.merge(&laptop);
assert_eq!(phone.value(), 3); Delta Sync
let mut sensor = GCounter::new("sensor-a");
sensor.increment_by(142);
let mut gateway = GCounter::new("gateway");
// Delta: only send what the gateway doesn't have
let delta = sensor.delta(&gateway);
gateway.apply_delta(&delta);
assert_eq!(gateway.value(), 142); Schema Migrations
use crdt_migrate::{crdt_schema, migration};
#[crdt_schema(version = 1, table = "sensors")]
struct SensorV1 { device_id: String, temperature: f32 }
#[crdt_schema(version = 2, table = "sensors")]
struct SensorV2 { device_id: String, temperature: f32, humidity: Option<f32> }
#[migration(from = 1, to = 2)]
fn add_humidity(old: SensorV1) -> SensorV2 {
SensorV2 { device_id: old.device_id, temperature: old.temperature, humidity: None }
}
// v1 records auto-migrate to v2 on load Architecture
Multi-crate workspace. Each crate is independently versioned. Use only what you need.
Define
Write a crdt-schema.toml with entities, versions, CRDT fields, and relations.
Generate
Run crdt generate. Get models, migrations, repository traits, events, sync.
Use
Import Persistence<S> in your app. Access repos, store data, sync between nodes.
Use Cases
IoT & Sensors
no_std core on ESP32, Raspberry Pi. Delta sync over LoRa/BLE. Schema migrations handle OTA updates.
Mobile Apps
Offline-first. Edit without network. Changes merge automatically on reconnect. No conflict dialogs.
Real-time Collaboration
TextCrdt for docs-style editing. ORSet for shared collections. No central coordinator needed.
Edge Computing
CRDTs at CDN edges. Local writes, delta sync between nodes. Pure-Rust redb — no C deps.
P2P Networks
No server. Every peer is equal. Any transport: WebSocket, WebRTC, Bluetooth. Order doesn't matter.
WASM & Browser
Same logic in Rust backend and browser. wasm-bindgen bindings. Ship one codebase everywhere.
Ecosystem
7 crates, independently versioned on crates.io. Use only what you need.
crdt-kit
Core library
9 ready-to-use CRDTs: GCounter, PNCounter, LWWRegister, MVRegister, GSet, TwoPSet, ORSet, RGA, and TextCrdt. Includes Hybrid Logical Clock (HLC) and traits to build your own CRDTs.
crdt-store
Persistence layer
Save and load CRDTs to SQLite, redb (embedded), or memory. CrdtDb unifies access: save(), load(), merge(), list_keys(). Namespaces to separate entities.
crdt-migrate
Automatic migrations
Evolve your structs without losing data. Define versions with #[crdt_schema] and #[migration] functions. Records auto-migrate on load.
crdt-codegen
Code generator
Define entities in a TOML file (schema.toml) and auto-generate: versioned structs, migrations, repositories, and the complete persistence layer.
crdt-cli
Command-line tool
Inspect databases, generate code, export JSON, compact event logs, and launch the web panel. All from terminal with "crdt <command>".
crdt-dev-ui
Web inspection panel
Embedded web panel at localhost:4242. Browse namespaces, inspect entities, view event logs and state vectors. Built-in dark theme.
Interactive Demo
Two devices, one chat. Send messages offline, then sync — CRDTs guarantee convergence.
Performance
Measured with Criterion on optimized builds. 37–700x faster than Automerge and Yrs.
vs Automerge & Yrs
Comparative benchmarks with the two most popular CRDT libraries in the Rust ecosystem.
Counter ×1000
crdt-kit 33 μs
Automerge 23 ms
Text insert 1000
crdt-kit 83 μs · Yrs 3.1 ms
Automerge 16.5 ms
List insert 1000
crdt-kit 265 μs · Yrs 16.5 ms
Automerge 34.4 ms
Set insert 1000
crdt-kit 203 μs
Automerge 27.6 ms
8M ops/sec GCounter increment
Incrementing a distributed counter. The most basic and frequent operation.
125 μs / 1000 ops ▼
How does this benchmark work?
Creates a GCounter with one node and calls .increment() 1000 times consecutively. Measures total time. A GCounter is a map {node → count}. Each increment only adds 1 to its own slot — O(1), no locks.
4.5M merges/sec GCounter merge
Merging 10 replicas. Simulates 10 devices reconnecting at once.
2.2 μs / 10 replicas ▼
How does this benchmark work?
Creates 10 GCounters with different nodes, each incremented multiple times. Merges them all into one. Merge takes max() of each slot. Linear time in number of nodes.
3.7M ops/sec Rga insert ×1000
Inserting 1000 elements into a replicated list. For playlists, kanban boards.
267 μs / 1000 ops ▼
How does this benchmark work?
Rga uses a flat Vec with direct positioning. No sequence rebuild — each insert finds its position in amortized O(n). 62× faster than Yrs Y.Array, 130× faster than Automerge List.
4M ops/sec TextCrdt insert 1000
Inserting 1000 characters in collaborative text. For real-time editors.
246 μs / 1000 chars ▼
How does this benchmark work?
TextCrdt uses flat Vec with O(1) cached len(). Each insert computes the visible position and places the element directly. 37× faster than Yrs Y.Text, 199× faster than Automerge Text.
2.8M ops/sec ORSet insert ×1000
Inserting into a replicated set. For shopping carts, lists.
350 μs / 1000 ops ▼
How does this benchmark work?
Each insert generates a unique tag (node + logical clock). On merge, "add wins" over concurrent remove. 136× faster than Automerge Map.
7.9M merges/sec LWWRegister merge
Last-Writer-Wins: most recent value wins. For profiles, config, GPS.
12.6 μs / 100 replicas ▼
How does this benchmark work?
LWWRegister stores a value + timestamp (Hybrid Logical Clock). On merge, highest timestamp wins. Merge is a simple comparison — O(1) per pair.
Measured with Criterion on optimized (release) builds. μs = microseconds. Compared against Automerge 0.7 and Yrs 0.25. Click any card for details.
Mathematical Guarantees
All CRDTs satisfy Strong Eventual Consistency (SEC). Verified by 268 tests.
Commutativity
Order of sync doesn't matter.
Associativity
Group syncs however you want.
Idempotency
Safe to retry. No duplicates.
Developer CLI
$ crdt status app.db Database overview: namespaces, keys, size.
$ crdt inspect app.db sensor-42 Entity detail with event log.
$ crdt compact app.db Snapshot + truncate event logs.
$ crdt export app.db --namespace sensors JSON export.
$ crdt generate --schema schema.toml Generate persistence layer.
$ crdt dev-ui app.db Web panel at localhost:4242.
Start building offline-first
cargo add crdt-kit --features serde