crdt-cli 0.4.0

CLI development tool for crdt-kit: inspect, migrate, compact, export, and generate code
crdt-cli-0.4.0 is not a library.

Crates.io Downloads Docs.rs CI License

DocumentationCrateExamplesContributing

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 specifically for resource-constrained, latency-sensitive environments where existing solutions (Automerge, Yjs) add too much overhead:

+-----------+     +-----------+     +-----------+
|  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

Key Advantages

crdt-kit Automerge Yjs
Zero dependencies (core) Yes No (30+) No (N/A — JS)
no_std / embedded Yes No No
WASM-ready Yes Partial Native JS
Persistent storage SQLite, redb, memory Custom N/A
Schema migrations Automatic (lazy) Manual N/A
Delta sync Yes Yes Yes
Serde integration Yes Custom N/A
Pure Rust Yes Yes No (JS)

Built For

Environment Why it matters
IoT / Embedded no_std support — runs on bare metal, Raspberry Pi, ESP32
Mobile apps Offline-first with automatic conflict resolution on reconnect
Edge computing Delta sync minimizes bandwidth between edge nodes
P2P networks No central server needed — every peer is equal
Real-time collaboration Concurrent edits merge without coordination
WASM / Browser First-class WebAssembly bindings for web apps

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();

// When they reconnect — merge. Always converges.
phone.merge(&laptop);
assert_eq!(phone.value(), 3);

With Persistence

[dependencies]

crdt-kit = { version = "0.3", features = ["serde"] }

crdt-store = { version = "0.1", features = ["sqlite"] }

use crdt_store::{CrdtDb, CrdtVersioned, SqliteStore};
use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize)]
struct SensorReading { temperature: f32, humidity: f32 }

impl CrdtVersioned for SensorReading {
    const SCHEMA_VERSION: u8 = 1;
}

let store = SqliteStore::open("sensors.db").unwrap();
let mut db = CrdtDb::with_store(store);

// Save — automatically wrapped in a version envelope
db.save("sensor-42", &SensorReading { temperature: 22.5, humidity: 65.0 }).unwrap();

// Load — automatically migrated if the schema changed
let reading: Option<SensorReading> = db.load("sensor-42").unwrap();

Workspace Architecture

crdt-kit is a multi-crate workspace. Use only what you need:

crdt-kit/
├── crates/
│   ├── crdt-kit/              9 CRDTs + HLC + traits (the core library)
│   ├── crdt-store/            Persistence: SQLite, redb, memory + CrdtDb API
│   ├── crdt-migrate/          Version envelopes + migration engine
│   ├── crdt-migrate-macros/   #[crdt_schema] + #[migration] proc macros
│   ├── crdt-codegen/          Code generation from TOML schemas
│   ├── crdt-cli/              CLI: new/dev/status/inspect/compact/export/generate/dev-ui
│   ├── crdt-dev-ui/           Embedded web panel for database inspection
│   └── crdt-example-tasks/    Full example: codegen + migrations + events
Crate Description Feature flags
crdt-kit 9 CRDT types, HLC, Crdt/DeltaCrdt traits std, serde, wasm
crdt-store Unified storage abstraction + high-level CrdtDb sqlite, redb
crdt-migrate Transparent schema migrations (lazy, on-read) macros
crdt-codegen Generate structs, migrations, and helpers from TOML
crdt-cli Developer CLI tool (crdt binary)
crdt-dev-ui Web inspection panel (Axum, dark theme)

Persistence & Storage

Three backends, one trait surface:

Backend Feature Use case Dependencies
MemoryStore (always) Testing, prototyping None
SqliteStore sqlite Edge Linux, mobile, desktop rusqlite (bundled)
RedbStore redb Pure-Rust edge (no C deps) redb

All backends implement StateStore + EventStore with event sourcing, snapshots, and compaction:

use crdt_store::{EventStore, MemoryStore, StateStore};

let mut store = MemoryStore::new();

// State persistence
store.put("sensors", "s1", b"data").unwrap();

// Event sourcing
let seq = store.append_event("sensors", "s1", b"SetTemp(23.1)", 1000, "node-a").unwrap();

// Snapshots + compaction
store.save_snapshot("sensors", "s1", b"state", seq, 1).unwrap();
store.truncate_events_before("sensors", "s1", seq).unwrap();

Schema Migrations

When your data evolves between app versions, crdt-migrate handles it transparently:

use crdt_migrate::{crdt_schema, migration};

#[crdt_schema(version = 1, table = "sensors")]
#[derive(Serialize, Deserialize)]
struct SensorV1 { device_id: String, temperature: f32 }

#[crdt_schema(version = 2, table = "sensors")]
#[derive(Serialize, Deserialize)]
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 }
}
  • Lazy: Data migrates on read, not on startup
  • Deterministic: Two devices migrating the same data produce identical results
  • Linear chain: v1 → v2 → v3 → current, never skipping steps
  • Write-back: Migrated data is re-persisted automatically

Code Generation

Define your entities in a crdt-schema.toml file and generate all the Rust boilerplate:

[config]

output = "src/generated"



# Entity with CRDT-wrapped fields (conflict-free replicated)

[[entity]]

name = "Project"

table = "projects"



[[entity.versions]]

version = 1

fields = [

    { name = "name", type = "String", crdt = "LWWRegister" },

    { name = "members", type = "String", crdt = "ORSet" },

]



# Entity with plain fields, versioned schema, and a relation

[[entity]]

name = "Task"

table = "tasks"



[[entity.versions]]

version = 1

fields = [

    { name = "title", type = "String" },

    { name = "done", type = "bool" },

]



[[entity.versions]]

version = 2

fields = [

    { name = "title", type = "String" },

    { name = "done", type = "bool" },

    { name = "priority", type = "Option<u8>", default = "None" },

    { name = "tags", type = "Vec<String>", default = "Vec::new()" },

    { name = "project_id", type = "String", default = "String::new()", relation = "Project" },

]

$ crdt generate --schema crdt-schema.toml

  Generated: src/generated/project.rs

  Generated: src/generated/task.rs

  Generated: src/generated/task_migrations.rs

  Generated: src/generated/helpers.rs

  Generated: src/generated/mod.rs


Generated 5 files in src/generated/

Field options:

Option Example Effect
type "String", "Vec<u8>" Rust type for the field
default "None", "Vec::new()" Default value for migrations
crdt "LWWRegister", "ORSet", "GCounter" Wraps the type with a CRDT (e.g., LWWRegister<String>)
relation "Project" Documents a reference to another entity

Supported CRDT types: GCounter, PNCounter, LWWRegister, MVRegister, GSet, TwoPSet, ORSet

What gets generated:

  • project.rs / task.rs — Versioned structs with #[crdt_schema], CRDT-wrapped field types, and type Project = ProjectV1
  • task_migrations.rs — Migration functions with #[migration] (auto-generated for field additions, CRDT fields get auto-defaults)
  • helpers.rscreate_db() and create_memory_db() with all migrations pre-registered
  • mod.rs — Module declarations and re-exports

All files are marked AUTO-GENERATED by crdt-codegen -- DO NOT EDIT.

See crdt-example-tasks for a complete working example.


Developer Tools

CLI

# Project scaffolding

$ crdt new                    # Interactive project creation (platform, template, entity)

$ crdt new my-app             # Quick create with name


# Development runtime

$ crdt dev                    # Build & run app + Dev UI dashboard

$ crdt dev --watch            # Auto-restart on file changes

$ crdt dev --port 8080        # Custom Dev UI port


# Database operations

$ crdt status app.db          # Database overview

$ crdt inspect app.db sensor-42 --events  # Entity detail + event log

$ crdt compact app.db --threshold 100     # Snapshot + truncate events

$ crdt export app.db --namespace sensors  # JSON export


# Code generation

$ crdt generate --schema crdt-schema.toml # Generate code from TOML

$ crdt dev-ui app.db          # Launch web inspector

crdt new — Interactive Project Scaffolding

Create a new crdt-kit project with a polished interactive experience (inspired by dx new and cargo tauri init):

$ crdt new

◇ crdt-kit — Create a new project

◆ Project name?  my-app
◆ Platform?      CLI App / Dioxus Client / IoT Device / Edge Computing
◆ Template?      Minimal / Full / Empty
◆ Entity name?   Task
◆ Event sourcing? Yes/No
◆ Delta sync?     Yes/No

◇ Creating project...
  ✓ Created my-app/Cargo.toml
  ✓ Created my-app/crdt-schema.toml
  ✓ Generated persistence layer
  ✓ Created my-app/src/main.rs

◇ Done! cd my-app && cargo run

crdt dev — Development Runtime

Launches your app and the Dev UI dashboard side-by-side with live log streaming:

$ crdt dev

  ┌─────────────────────────────────────────┐

    crdt dev — Development Runtime         │

  ├─────────────────────────────────────────┤

    Schema   crdt-schema.toml              │

    Database my-app.db                     │

    Dev UI   http://localhost:4242         │

    Command  cargo run                     │

  └─────────────────────────────────────────┘


  [crdt] 10:30:01 Running codegen...

  [app]  10:30:03 Compiling my-app v0.1.0

  [ui]   10:30:01 Dev UI running on http://localhost:4242

  [app]  10:30:05 my-app is running

The Dev UI stays alive after the app exits for database inspection — press Ctrl+C to stop.

Dev UI

A dark-themed web panel for visual database inspection during development:

$ crdt dev-ui app.db

  Dev UI: http://localhost:4242

Browse namespaces, entities, event logs, version envelopes, and snapshots — all from your browser.


Edge Computing & IoT

crdt-kit is purpose-built for edge environments. Import it with no_std for bare metal or with serde for network serialization:

# Raspberry Pi / ESP32 / bare metal (no standard library)

[dependencies]

crdt-kit = { version = "0.3", default-features = false }



# Edge node with JSON sync over MQTT/HTTP

[dependencies]

crdt-kit = { version = "0.3", features = ["serde"] }

serde_json = "1"

use crdt_kit::prelude::*;

// Edge sensor node collects temperature readings
let mut sensor_a = GCounter::new("sensor-a");
let mut sensor_b = GCounter::new("sensor-b");

// Each sensor counts events independently (no network needed)
sensor_a.increment_by(142); // 142 events detected
sensor_b.increment_by(89);  // 89 events detected

// When the gateway collects data — merge. Order doesn't matter.
sensor_a.merge(&sensor_b);
assert_eq!(sensor_a.value(), 231); // exact total, no double-counting

// Delta sync: only send what changed (saves bandwidth on LoRa/BLE)
let mut gateway = GCounter::new("gateway");
let delta = sensor_a.delta(&gateway);  // minimal payload
gateway.apply_delta(&delta);            // gateway is up to date
assert_eq!(gateway.value(), 231);

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 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

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

Examples

# CRDT basics

cargo run -p crdt-kit --example counter         # Distributed counters

cargo run -p crdt-kit --example todo_list        # Collaborative todo list

cargo run -p crdt-kit --example ecommerce        # E-commerce entities

cargo run -p crdt-kit --example chat             # Chat with conflict detection


# Persistence & migration

cargo run -p crdt-store --features sqlite --example iot_sensor      # Schema migration on OTA update

cargo run -p crdt-store --features sqlite --example collaborative   # Multi-node merge + persist

cargo run -p crdt-store --features sqlite --example event_sourcing  # Event log, snapshots, compaction


# Full-stack example (codegen + migrations + event sourcing)

cargo run -p crdt-example-tasks                                     # Task management app


Performance

Measured with Criterion on optimized builds:

Operation Time Throughput
GCounter increment x1000 53 µs ~19M ops/sec
GCounter merge 10 replicas 1.1 µs ~9M merges/sec
GCounter merge 100 replicas 17.8 µs
PNCounter inc+dec x1000 60 µs ~16M ops/sec
ORSet insert x1000 187 µs ~5M ops/sec
ORSet merge 500+500 elements 191 µs
GSet merge 1000+1000 elements 102 µs
LWWRegister merge 100 replicas 11.5 µs ~8M merges/sec
cargo bench  # Run benchmarks yourself


Guarantees

All 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 256 tests across the workspace.


Roadmap

  • G-Counter, PN-Counter
  • LWW-Register, MV-Register
  • G-Set, 2P-Set, OR-Set
  • RGA List (ordered sequence)
  • Text CRDT (collaborative text editing)
  • no_std support (embedded / bare metal)
  • serde serialization support
  • Delta-state optimization
  • WASM bindings
  • Persistent storage (SQLite + redb)
  • High-level CrdtDb API with version envelopes
  • Transparent schema migrations (#[crdt_schema] + #[migration])
  • Developer CLI (crdt status/inspect/compact/export/generate)
  • Dev UI web panel
  • Code generation from TOML schemas (crdt generate)
  • Interactive project scaffolding (crdt new) with platform & template selection
  • Development runtime (crdt dev) with live logs, Dev UI dashboard, and auto-codegen
  • Network transport layer (TCP, WebSocket, QUIC)
  • Sync protocol (delta-based replication)
  • Benchmarks against Automerge / Yrs

Contributing

Contributions are welcome! Please read CONTRIBUTING.md before submitting a pull request.

License

Dual-licensed under your choice of: