Skip to content

Repository files navigation

Syncraft

A robust, modular, local-first collaborative state engine for JavaScript & TypeScript applications.

Syncraft enables web and Node.js applications to support multi-user collaboration with zero central lock-in. Replicas can modify shared state concurrently, operate completely offline, persist mutations to local storage (IndexedDB), and automatically synchronize with peer replicas via WebSockets upon reconnection.

License: MIT CI Tests: 62 Passing Type: Pure ESM


Table of Contents


Key Highlights

  • 🔄 Deterministic Convergence: Resolves concurrent conflicts mathematically with zero state forks across arbitrary network replay permutations.
  • 🕒 Lamport Logical Clocks: Guarantees causal ordering without relying on physical wall clocks or server NTP synchronization.
  • 📴 Offline-First & Auto-Flush: Local operations update the UI immediately, persist to IndexedDB, queue offline, and automatically flush in batches upon reconnecting.
  • 🌳 Safe Immutable Path Navigation: Deep property manipulation (doc.tags.0), copy-on-write immutability, and built-in prototype pollution protection (__proto__, constructor, prototype).
  • ⚛️ React 18 Concurrent Rendering: Custom hooks built with useSyncExternalStore for tearing-free reactive state binding and granular slice subscriptions.
  • 🛡️ Tombstone & Ancestor Pruning: Automatically prunes empty intermediate container objects on key deletion, guaranteeing identical structural tree equivalence across replicas.

Core Architecture & Diagrams

1. System Layer Topology

graph TD
    subgraph UI ["User Interface Layer"]
        ReactApp["React Application / UI Components"]
        Hooks["@bhoomash/syncraft-react (useSyncStore, useSyncState, useConnectionStatus)"]
        ReactApp --> Hooks
    end

    subgraph ClientLayer ["Client Orchestration Layer (@bhoomash/syncraft-client)"]
        SyncClient["SyncClient Manager"]
        PendingQueue["Pending Operation Queue"]
        ReconnectMgr["Exponential Backoff & Reconnect"]
        Hooks --> SyncClient
        SyncClient --> PendingQueue
        SyncClient --> ReconnectMgr
    end

    subgraph CoreLayer ["Core CRDT Engine (@bhoomash/syncraft-core)"]
        Clock["Lamport Logical Clock"]
        ConflictResolver["Deterministic LWW Resolver (Total Order)"]
        PathEngine["Immutable Safe Path Engine"]
        OpLog["Operation Log & Deduplication Set"]
        SyncClient --> Clock
        SyncClient --> ConflictResolver
        ConflictResolver --> PathEngine
        ConflictResolver --> OpLog
    end

    subgraph StorageLayer ["Storage Layer (@bhoomash/syncraft-storage)"]
        StorageAdapter["IStorageAdapter Interface"]
        IDBAdapter["IndexedDB Storage Adapter (Browser)"]
        MemAdapter["Memory Storage Adapter (Node / Tests)"]
        StorageAdapter --> IDBAdapter
        StorageAdapter --> MemAdapter
        SyncClient --> StorageAdapter
    end

    subgraph ServerLayer ["Relay Transport Layer (@bhoomash/syncraft-server)"]
        WSServer["WebSocket Server Relay"]
        RoomMgr["Room & Client Manager"]
        MsgValidator["Protocol & Schema Validator"]
        WSServer --> RoomMgr
        WSServer --> MsgValidator
    end

    SyncClient <== "WebSocket Protocol (JSON envelopes)" ==> WSServer
Loading

2. Package Boundaries & Responsibilities

   ┌──────────────────────────────────────────────────────────┐
   │                    React Application                     │
   │               (useSyncStore, useSyncState)               │
   └─────────────────────────────┬────────────────────────────┘
                                 │
   ┌─────────────────────────────▼────────────────────────────┐
   │                @bhoomash/syncraft-client                 │
   │  - Orchestrates engine, local persistence, & networking  │
   │  - Manages pending operation queue & reconnect backoff   │
   └───────────────┬───────────────────────────┬──────────────┘
                   │                           │
   ┌───────────────▼───────────┐   ┌───────────▼──────────────┐
   │  @bhoomash/syncraft-core  │   │@bhoomash/syncraft-storage│
   │  - Lamport Logical Clock  │   │  - IndexedDB Persistence │
   │  - Operation Generator    │   │  - In-Memory Fallback    │
   │  - Deterministic LWW CRDT │   │  - Pending Queue Store   │
   │  - Path Engine (immutable)│   │  - Snapshots & Metadata  │
   │  - Zero External Deps     │   │  - Zero CRDT Logic       │
   └───────────────────────────┘   └──────────────────────────┘
                   │
                   │ WebSocket Protocol (Relay Only)
                   ▼
   ┌──────────────────────────────────────────────────────────┐
   │                @bhoomash/syncraft-server                 │
   │  - WebSocket Relay & Room Isolation                      │
   │  - Message Validation & Operation Broadcasting           │
   │  - (Relay only: NOT a CRDT conflict authority)           │
   └──────────────────────────────────────────────────────────┘

Monorepo Package Matrix

Package Version Responsibility Dependencies NPM Tarball
@bhoomash/syncraft-core 0.1.0 CRDT state engine, Lamport logical clock, operation evaluation, path engine, immutability. Zero (Pure JS) 7.3 kB
@bhoomash/syncraft-storage 0.1.0 Pluggable local persistence (IndexedDB for browsers, in-memory for Node/tests). Zero 4.3 kB
@bhoomash/syncraft-client 0.1.0 WebSocket client, room manager, reconnect backoff, offline sync queue. @bhoomash/syncraft-core, @bhoomash/syncraft-storage, ws 4.8 kB
@bhoomash/syncraft-server 0.1.0 Node.js WebSocket relay server, client tracking, room broadcast. @bhoomash/syncraft-core, @bhoomash/syncraft-storage, ws 3.8 kB
@bhoomash/syncraft-react 0.1.0 React 18 hooks (useSyncStore, useSyncState, useConnectionStatus, useSyncRoom). @bhoomash/syncraft-client, @bhoomash/syncraft-core, react 2.5 kB

Setup & Installation

Prerequisites

  • Node.js: v18.0.0 or higher (tested on Node 18, 20, and 22 LTS)
  • Package Manager: npm (v9+) or pnpm (v8+)

Installation

# 1. Clone the repository
git clone https://github.com/bhoomash/synccraft.git
cd synccraft

# 2. Install workspace dependencies
npm install

# 3. Run full automated test suite (62 tests across 12 suites)
npm test

# 4. Verify package bundling and tarball generation
npm run publish:dry-run

CRDT & Convergence Model

Syncraft employs an Operation-based Last-Write-Wins Map/Document CRDT:

1. Atomic Operation Structure

{
  operationId: "node-A-42-9a8f12c", // Globally unique operation ID
  nodeId: "node-A",                // Originating replica ID
  logicalClock: 42,                // Lamport scalar timestamp
  operationType: "SET",            // 'SET' | 'DELETE'
  path: "document.theme",          // Canonical dot-separated path
  value: "dark",                   // Value payload (undefined for DELETE)
  wallTime: 1740000000000          // Informational physical timestamp
}

2. Lamport Clock Progression

  • Local Mutation: $L_{\text{local}} \leftarrow L_{\text{local}} + 1$
  • Remote Operation Received: $L_{\text{local}} \leftarrow \max(L_{\text{local}}, L_{\text{op}}) + 1$

3. Deterministic Total Ordering ($\succ$)

Given two operations $Op_1$ and $Op_2$ targeting the exact same canonical path: $$Op_1 \succ Op_2 \iff \begin{cases} L_1 > L_2 \ L_1 = L_2 \land \text{nodeId}_1 > \text{nodeId}_2 \ L_1 = L_2 \land \text{nodeId}_1 = \text{nodeId}_2 \land \text{opId}_1 > \text{opId}_2 \end{cases}$$

4. Permutation Replay Invariance (Formally Verified)

Even when operations are received out of order across different network partitions:

$$\begin{aligned} \text{Replica A receives: } & 1 \to 2 \to 3 \to 4 \\ \text{Replica B receives: } & 4 \to 2 \to 1 \to 3 \\ \text{Replica C receives: } & 3 \to 1 \to 4 \to 2 \\ & \Downarrow \\ & \textbf{Exact Same Final State Tree} \end{aligned}$$


Offline-First Workflow

sequenceDiagram
    autonumber
    participant UI as User / UI
    participant Client as @bhoomash/syncraft-client
    participant Core as @bhoomash/syncraft-core
    participant Storage as @bhoomash/syncraft-storage (IndexedDB)
    participant Server as @bhoomash/syncraft-server (WS Relay)

    Note over Client,Server: Client is Offline (Airplane Mode)
    UI->>Client: store.set("doc.title", "Draft 1")
    Client->>Core: Apply local SET
    Core-->>Client: New immutable state snapshot
    Client->>Storage: Persist op to pendingQueue
    Client-->>UI: Immediate state notification (zero lag)

    Note over Client,Server: Connection Restored
    Client->>Server: Connect & JOIN_ROOM
    Server-->>Client: ACK join
    Client->>Storage: Read pending operations
    Storage-->>Client: [Op_1, Op_2]
    Client->>Server: BATCH_OPERATIONS / SYNC_REQUEST
    Server-->>Client: OPERATION broadcast & ACK
    Client->>Core: Apply remote operations (deterministic LWW)
    Client->>Storage: Mark pending ops acknowledged
    Client-->>UI: Converged state updated
Loading

Quick Start Guide

1. Core CRDT Engine (@bhoomash/syncraft-core)

npm install @bhoomash/syncraft-core
import { createSyncStore } from '@bhoomash/syncraft-core';

// Initialize a standalone CRDT store
const store = createSyncStore({ nodeId: 'node-alpha' });

// Subscribe to state changes
const unsubscribe = store.subscribe((state) => {
  console.log('Current state:', state);
});

// Mutate local state
store.set('project.title', 'Syncraft');
store.set('project.settings.theme', 'dark');

console.log(store.get('project.title')); // "Syncraft"

// Hydrate remote operation
store.applyRemoteOperation({
  operationId: 'node-beta-1-abc',
  nodeId: 'node-beta',
  logicalClock: 2,
  operationType: 'SET',
  path: 'project.settings.notifications',
  value: true,
});

console.log(store.getState());
unsubscribe();

2. WebSocket Sync Client (@bhoomash/syncraft-client)

npm install @bhoomash/syncraft-client @bhoomash/syncraft-core @bhoomash/syncraft-storage
import { createSyncClient } from '@bhoomash/syncraft-client';
import { IndexedDBAdapter } from '@bhoomash/syncraft-storage';

const client = createSyncClient({
  url: 'ws://localhost:8080',
  roomId: 'workspace-room-1',
  nodeId: 'user-alice',
  storage: new IndexedDBAdapter({ dbName: 'my-app-db' }),
  autoConnect: true,
});

// Synchronous UI updates + background persistence and sync
await client.set('doc.title', 'Collaborative Doc');
await client.set('doc.tags.0', 'crdt');

client.onStatusChange((status) => {
  console.log('Sync status:', status); // 'connecting' | 'connected' | 'reconnecting' | 'disconnected'
});

3. WebSocket Relay Server (@bhoomash/syncraft-server)

npm install @bhoomash/syncraft-server
import { createSyncServer } from '@bhoomash/syncraft-server';

const server = createSyncServer({
  port: 8080,
  heartbeatInterval: 30000,
});

console.log(`Relay server running on ws://localhost:${server.getPort()}`);

4. React 18 Bindings (@bhoomash/syncraft-react)

npm install @bhoomash/syncraft-react @bhoomash/syncraft-client @bhoomash/syncraft-core
import React from 'react';
import { createSyncClient } from '@bhoomash/syncraft-client';
import { SyncProvider, useSyncState, useConnectionStatus, useSyncClient } from '@bhoomash/syncraft-react';

const client = createSyncClient({
  url: 'ws://localhost:8080',
  roomId: 'collab-room-1',
  autoConnect: true,
});

function TitleEditor() {
  const client = useSyncClient();
  const [title, setTitle] = useSyncState(client.getStore(), 'document.title');
  const status = useConnectionStatus(client);

  return (
    <div>
      <p>Status: <strong>{status}</strong></p>
      <input
        type="text"
        value={title || ''}
        onChange={(e) => setTitle(e.target.value)}
        placeholder="Type a collaborative title..."
      />
    </div>
  );
}

export function App() {
  return (
    <SyncProvider client={client}>
      <TitleEditor />
    </SyncProvider>
  );
}

Example Applications

Syncraft includes three fully working reference implementations in the examples/ directory:

  • examples/vanilla/: Standalone HTML5 + pure JavaScript collaborative notes app featuring live connection indicators and local IndexedDB caching.
  • examples/react/: Real-time collaborative todo list application powered by @bhoomash/syncraft-react with granular path binding.
  • examples/collaborative-editor/: Multi-user shared document editor with offline editing recovery.

Assumptions & Invariants

  1. Logical Clocks over Physical Time: Physical clocks (Date.now()) are subject to drift, skew, and leap seconds. All conflict resolution relies solely on monotonic Lamport clocks with deterministic node ID tie-breaking.
  2. Asynchronous & Partitioned Networks: Messages can be arbitrarily delayed, dropped, reordered, or duplicated. All operations are idempotent under total order replay.
  3. Untrusted Server Relay: The WebSocket server acts strictly as an authenticated packet relay; each client independently verifies and maintains CRDT mathematical invariants.
  4. Path-Level Granularity: Conflict resolution operates at the field/path level (e.g. doc.author and doc.title update concurrently without collision).
  5. Pure & Isolated Core: @bhoomash/syncraft-core has zero external dependencies, zero side-effects, and executes identically across Node.js, browsers, and mobile runtimes.

AI Planning & 10-Phase Roadmap

Phase Milestone Scope & Deliverables Status
Phase 1 Monorepo & Package Boundaries Workspace configuration, package manifests, ESM exports, Vitest test runner, architecture specs, and README. Complete
Phase 2 @bhoomash/syncraft-core Engine Lamport clock, unique operation IDs, SET/DELETE ops, path manipulation, deterministic conflict resolution (LWW + tie-breaking), state reconstruction, deduplication. Complete
Phase 3 CRDT Convergence Testing Multi-replica out-of-order, duplicate, concurrent, and network partition convergence test suites (38 tests). Complete
Phase 4 @bhoomash/syncraft-storage Pluggable storage abstraction, IndexedDB engine (browser), in-memory adapter (testing/Node), pending queue management. Complete
Phase 5 @bhoomash/syncraft-client Client connection manager, room join/leave, offline queue flush, backoff reconnection, state synchronization. Complete
Phase 6 @bhoomash/syncraft-server Node.js WebSocket relay server, room isolation, message validation, client tracking, ACK broadcasting. Complete
Phase 7 Offline Sync & Reconnection End-to-end integration tests for Client A ↔ Server ↔ Client B, offline mutation recovery, and convergence. Complete
Phase 8 @bhoomash/syncraft-react React hooks (useSyncStore, useSyncState, useConnectionStatus, useSyncRoom, SyncProvider). Complete
Phase 9 Example Applications Vanilla JS, React, and Collaborative Text/Document Editor examples (examples/). Complete
Phase 10 Documentation & Polish Full API docs, CRDT architecture guide, npm publishing configuration, benchmarks, and CI setup. Complete

Publishing & CI/CD

Syncraft includes ready-to-use npm publishing scripts and GitHub Actions continuous integration:

# Verify all package bundles (dry run)
npm run publish:dry-run

# Publish all workspaces to the public NPM registry under @bhoomash scope
npm run publish:all

Continuous integration runs on every push and pull request via .github/workflows/ci.yml across Node 18, 20, and 22 LTS.


Technical Limitations & Guarantees

  • Guarantees:
    • Eventual state convergence across all replicas receiving the same valid set of operations.
    • Strict idempotency: duplicate delivery will never corrupt or alter state.
    • Total logical ordering independent of physical clock drift.
  • Limitations:
    • Last-Write-Wins (LWW) resolves conflicting field assignments at the path level; it does not perform character-by-character Operational Transformation (OT) on primitive strings without a sequence CRDT (e.g., RGA or Fugue, planned for future extension).
    • Tombstones and operation histories grow over time and benefit from periodic snapshot compaction.

License

MIT © 2026 Bhoomash and Syncraft Contributors

About

Syncraft is a modular, local-first collaborative state engine for JavaScript that lets applications sync data offline and across users seamlessly. It uses Last-Write-Wins CRDTs and Lamport logical clocks to resolve conflicts deterministically with zero dependencies in its core.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages