Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

119 Commits

Folders and files

Repository files navigation

crdt-collab

CI

Introduction

A terminal-based collaborative text editor where multiple nodes type into the same document over direct TCP. There is no server so every node talks directly to every other node. If two people type at the same time, both edits land on all nodes and the text ends up identical everywhere.

graph LR
    A[node A] <-->|TCP| B[node B]
    B <-->|TCP| C[node C]
    A <-->|TCP| C
Loading

The CRDT that makes this work is RGA (Replicated Growable Array). Each character gets a unique ID when inserted, so concurrent inserts at the same position always resolve in the same order on every node. All CRDT types are written from scratch with no external CRDT libraries.

Crates: crdt-lib has the CRDT types and document model, crdt-transport has the networking. crdt-client is the TUI that uses both.

graph TD
    crdt-client --> crdt-transport
    crdt-client --> crdt-lib
    crdt-transport --> crdt-lib
Loading

Dev team: Tri Tac Le, Shakti Om Sharma, Yazan Samer Zarka

Implemented functionality

  • RGA - the text document. Characters have stable IDs. Concurrent inserts converge to the same order using Lamport tiebreaking. Deletes are tombstones.
  • Lamport clock - ticks on every local op, jumps to max on receive.
  • Version vector - tracks which ops each replica has seen. Used to compute exactly what a peer is missing.
  • LWW register and LWW map - last-write-wins using Lamport timestamps.
  • OR-Set - add-wins set. Concurrent add and remove: add wins.
  • Op log - append-only log per document, used for delta sync and snapshot restore.
  • TCP transport - length-prefixed bincode frames over tokio TcpStream. Local ops fan out to all peers immediately.
  • Causal buffer - ops that arrive out of order wait until the gap fills before being applied.
  • Anti entropy - every 500ms each node sends its version vector to one peer. The peer sends back anything the node is missing. Catches any broadcast that got dropped.
sequenceDiagram
    participant A as node A
    participant B as node B
    A->>B: Hello(version_vec)
    B->>A: StateSync(missing ops)
Loading
  • Atomic snapshot - op log saved to disk via write-to-tmp then rename. Node restarts replay the log and rejoin without a full resync.
  • mDNS discovery - nodes advertise themselves on the local network and dial peers they find automatically. No --peer flags needed on a LAN. Use --no-mdns to turn it off.
  • Multiple documents - each session can hold multiple named documents. A document picker lets you switch between them.
  • TUI - ratatui editor with a status line showing replica ID, peer count, and document length.

Future work

  • We don't have WebSocket transport. Adding a WebSocket gateway would let browser clients connect to a node and edit from a web page without installing anything. The node would accept both raw TCP (from other nodes) and WebSocket (from browsers) on separate ports.
  • No TLS. All traffic is plaintext. Adding TLS to the TCP connections would stop passive sniffing and give basic peer authentication via certificates.
  • No NAT traversal. Peers behind different routers cannot find each other even with mDNS, since mDNS is link-local only. A STUN/TURN relay or a shared server would fix this.

External dependencies

Crate Used for
tokio Async runtime, TCP, timers, mutexes
serde Serialization derives
bincode Binary wire format
uuid UUIDv7 replica identifiers
ratatui Terminal UI
crossterm Terminal input and output
clap CLI argument parsing
tracing + tracing-subscriber Structured logging
thiserror Typed error enums
anyhow Error propagation in app code
bytes + tokio-util Length-delimited codec
futures Stream combinators in the message router
mdns-sd mDNS peer discovery

All CRDT types are written from scratch. The banned crate check at scripts/check-banned-crates.sh runs in CI and fails the build if any banned package appears in Cargo.toml or Cargo.lock.

Installation

Requires Rust 1.85+.

git clone https://github.com/yazanzarka1/crdt-collab
cd crdt-collab
cargo build --release -p client

Binary lands at target/release/crdt-client.

Usage

Nodes on the same machine or LAN find each other via mDNS automatically:

# terminal 1
./target/release/crdt-client --listen 0.0.0.0:4001 --journal state1

# terminal 2
./target/release/crdt-client --listen 0.0.0.0:4002 --journal state2

# terminal 3
./target/release/crdt-client --listen 0.0.0.0:4003 --journal state3

They connect within a few seconds. Type in any window and the text appears in all of them.

To connect two machines on the same network, find each machine's IP (ip addr) and use --peer:

# machine A
./target/release/crdt-client --listen 0.0.0.0:4000 --peer <machine-B-ip>:4000

# machine B
./target/release/crdt-client --listen 0.0.0.0:4000 --peer <machine-A-ip>:4000

mDNS auto-discovery works on LANs. On networks with client isolation like eduroam, mDNS is blocked - use --peer with the explicit IP instead.

To disable mDNS: --no-mdns.

Or use the tmux demo (requires tmux):

just demo

Quit with Ctrl-C. Restart with the same --journal directory and the node restores from disk and rejoins.

Running tests

just                  # fmt + clippy + all tests
just ci               # fmt + clippy + tests + doc + preflight check
cargo test            # unit tests only
cargo test -p transport -- --ignored three_peers_converge  # slow convergence test

The convergence test spins up three nodes, fires concurrent inserts from all three, and checks the final text is identical on all nodes.

API documentation

yazanzarka1.github.io/crdt-collab

Or generate locally:

cargo doc --no-deps --open

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages