Skip to main content
Brainbox is built on a local-first architecture that enables real-time collaboration, offline editing, and automatic conflict resolution using CRDTs (Conflict-free Replicated Data Types).

How It Works

Brainbox’s collaboration system operates on four core principles:
1

Local-first storage

Every client maintains a full SQLite database. All changes write locally first, ensuring instant responsiveness and offline capability.
2

Background sync

When connected, changes automatically sync to the server via WebSocket. The server persists data to PostgreSQL as the source of truth.
3

CRDT merge

Concurrent edits from multiple users merge automatically using Yjs CRDTs. No manual conflict resolution required.
4

Real-time updates

All connected clients receive live updates through WebSocket broadcasts, showing changes as they happen.

Architecture Overview

The sync architecture consists of multiple layers:
┌─────────────────────────────────────────────────────────┐
│                    Client (Browser)                      │
├─────────────────────────────────────────────────────────┤
│  UI Components (React)                                   │
│  ↓                                                       │
│  Client Database (SQLite - WASM)                         │
│  ↓                                                       │
│  Sync Engine (@brainbox/client)                         │
│  ↓                                                       │
│  WebSocket Connection                                    │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│                    Server (Node.js)                      │
├─────────────────────────────────────────────────────────┤
│  WebSocket Handlers (Fastify)                           │
│  ↓                                                       │
│  Synchronizers (Broadcasters)                           │
│  ↓                                                       │
│  Server Database (PostgreSQL)                            │
│  ↓                                                       │
│  CRDT Engine (Yjs)                                      │
└─────────────────────────────────────────────────────────┘

Synchronization Types

Brainbox syncs different types of data through specialized synchronizers:

Node Updates

Changes to node attributes (name, fields, etc.)

Document Updates

Rich text content changes in pages and records

Collaborations

User presence and active collaborators

Node Reactions

Emoji reactions on messages

Node Interactions

User interactions and activity

Node Tombstones

Deleted nodes and cleanup

User Updates

User profile and settings changes

Real-Time Editing

Multiple users can edit the same document simultaneously without conflicts:

Presence Awareness

See who else is viewing or editing:
  • Active users - Avatar indicators show who’s in the document
  • Live cursors - See where others are typing in real-time
  • Selections - View other users’ text selections
  • Activity status - Online/offline indicators

CRDT-Based Merging

Brainbox uses Yjs for conflict-free collaborative editing:
CRDTs (Conflict-free Replicated Data Types) are data structures that can be updated independently on multiple replicas and automatically merged:Key properties:
  • Commutative - Order of operations doesn’t matter
  • Associative - Grouping of operations doesn’t matter
  • Idempotent - Applying an operation twice has the same effect as once
Example:
// User A inserts "Hello" at position 0
// User B inserts "World" at position 0
// Traditional merge: conflict!
// CRDT merge: "HelloWorld" or "WorldHello" (deterministic)

// With Yjs:
import * as Y from 'yjs'

const doc = new Y.Doc()
const yText = doc.getText('content')

// User A's edit
yText.insert(0, 'Hello')

// User B's edit (concurrent)
yText.insert(0, 'World')

// Result: Automatic merge with preserved intent

Offline Mode

Brainbox works fully offline with automatic sync when reconnected:

Offline Capabilities

Full CRUD

Create, read, update, and delete nodes offline

File Uploads

Queue file uploads for processing when online

Search

Search locally cached content

Optimistic UI

Changes appear instantly in the UI

Offline Workflow

1

Connection lost

WebSocket disconnects. UI shows offline indicator.
2

Continue working

All changes write to local SQLite database. No interruption to user experience.
3

Queue sync

Changes queue with pending sync status.
4

Reconnect

WebSocket reconnects automatically when internet returns.
5

Sync queued changes

Pending changes upload to server in order.
6

Merge conflicts

CRDTs merge any concurrent changes from other users.
7

Broadcast updates

Server broadcasts merged state to all clients.

Handling Conflicts

In most cases, conflicts resolve automatically via CRDTs. Manual intervention is rarely needed.
When conflicts do occur:
Most conflicts resolve without user action:Text edits:
  • Concurrent insertions: Both apply, merged by CRDT
  • Concurrent deletions: Deleted content removed
  • Format conflicts: Both formats apply (text can be bold AND italic)
Database edits:
  • Different fields: Both changes apply
  • Same field: Last write wins (by timestamp)
  • Record deletion: Tombstone propagates, record removed for all users

Sync Engine Implementation

The sync engine coordinates data flow between client and server:

Client-Side Handlers

Located in packages/client/src/handlers/:
// Example: Node updates handler
export async function handleNodeUpdates(
  input: NodesUpdatesInput
): Promise<void> {
  const db = await openDatabase();
  
  for (const node of input.data) {
    await db
      .insertInto('nodes')
      .values({
        id: node.id,
        type: node.type,
        attributes: JSON.stringify(node.attributes),
        updatedAt: node.updatedAt,
      })
      .onConflict((oc) =>
        oc.column('id').doUpdateSet({
          attributes: JSON.stringify(node.attributes),
          updatedAt: node.updatedAt,
        })
      )
      .execute();
  }
}

Server-Side Broadcasters

Located in apps/server/src/synchronizers/:
// Example: Broadcasting node updates
export async function broadcastNodeUpdate(
  nodeId: string,
  workspaceId: string
): Promise<void> {
  const node = await getNode(nodeId);
  
  await broadcastToWorkspace(workspaceId, {
    type: 'nodes-updates',
    data: [node],
  });
}

Performance Optimization

Brainbox optimizes sync performance through:

Batching

Multiple changes batch into single sync messages

Debouncing

Rapid edits debounce before syncing

Compression

WebSocket messages use compression

Delta Sync

Only changes sync, not full documents

Indexing

SQLite indexes speed up queries

Connection Pooling

Reuse database connections

Sync Strategies

High-priority changes sync immediately:
  • Chat messages
  • Node creation/deletion
  • Permission changes
  • User presence updates

Data Consistency

Brainbox ensures data consistency across clients:

Consistency Guarantees

All clients eventually converge to the same state:
  • Guarantee: Given no new updates, all replicas converge
  • Timeline: Typically within seconds of reconnection
  • Verification: Vector clocks track causal ordering
Operations preserve cause-and-effect relationships:
  • Guarantee: If operation A causes operation B, all clients see A before B
  • Implementation: Logical timestamps and dependency tracking
  • Example: Reply to message always appears after original message
Server database maintains strong consistency:
  • Guarantee: Server always has authoritative state
  • Implementation: PostgreSQL ACID transactions
  • Use: Conflict resolution and audit trail

Conflict-Free Operations

Certain operations are guaranteed conflict-free:
OperationConflict-Free?Reason
Insert textCRDT positions are unique
Delete textTombstones prevent resurrection
Create nodeUUIDs prevent ID collision
Update different fieldsIndependent field state
Add reactionSet-based CRDT
Update same fieldLast-write-wins
Reorder items~Fractional indexing reduces conflicts

Scaling Collaboration

Brainbox scales to support large teams:

WebSocket Rooms

Users join workspace-specific rooms for targeted broadcasts

Redis Pub/Sub

Horizontal scaling across multiple server instances

Connection Pooling

Efficient database connection management

Rate Limiting

Prevent abuse with per-user rate limits

Best Practices

Don’t manually refresh to see changes. The sync engine automatically propagates updates in real-time.
Brainbox is designed for offline work. Don’t wait for internet connection - keep working and changes will sync later.
Changing field types (e.g., text to number) can cause data loss if users have offline edits. Plan schema changes carefully.
Watch the sync indicator in the UI. If changes aren’t syncing, check your connection.

Troubleshooting

Changes not syncing:
  1. Check internet connection
  2. Verify WebSocket status in network tab
  3. Check browser console for errors
  4. Try disconnecting and reconnecting
  5. Clear local cache and re-sync
Slow sync:
  1. Check network speed
  2. Reduce concurrent edits
  3. Limit file upload size
  4. Check server load

Technical Deep Dive

For developers interested in the implementation:

CRDT Package

packages/crdt/ - Yjs document handling

Client Sync

packages/client/ - Sync engine and handlers

Server Sync

apps/server/src/synchronizers/ - Broadcasters

WebSocket API

apps/server/src/api/ - WebSocket routes

Next Steps

Pages

Real-time collaborative editing

Databases

Collaborative data management

Chat

Real-time messaging

Development

Build with Brainbox

Build docs developers (and LLMs) love