Skip to content

Collaboration Protocol

Diagrammer's real-time collaboration uses Protected Local mode — one user hosts a WebSocket server via the Tauri backend, and other users on the same network connect as clients. All document sync uses Yjs CRDTs for conflict-free merging.

Architecture Overview

UnifiedSyncProvider

The UnifiedSyncProvider (/src/collaboration/) multiplexes three concerns over a single WebSocket connection:

  1. CRDT sync — Yjs document updates
  2. Document CRUD — list, get, save, delete documents
  3. Authentication — JWT token exchange

Wire Protocol

Messages are binary (ArrayBuffer). The first byte is the message type tag.

WARNING

The TypeScript protocol (/src/collaboration/protocol.ts) and the Rust protocol (src-tauri/src/server/protocol.rs) must stay in sync. A mismatch will cause silent data corruption or connection drops.

Message Types

TagNameDirectionPurpose
0SYNCBidirectionalYjs sync messages (step 1, step 2, update)
1AWARENESSBidirectionalCursor positions, selections, user presence
2AUTHClient → ServerAuthentication token
3DOC_LISTClient → ServerRequest document list
4DOC_GETClient → ServerRequest document content
5DOC_SAVEClient → ServerSave document
6DOC_DELETEClient → ServerDelete document
7DOC_EVENTServer → ClientDocument change notification
10JOIN_DOCClient → ServerJoin a collaborative editing session
11AUTH_LOGINClient → ServerLogin with credentials

Sync Flow

Yjs Integration

Yjs provides the CRDT data structures that enable conflict-free merging. Diagrammer maps its document model onto Yjs types:

  • Y.Map for shape properties
  • Y.Array for shape ordering and collections
  • Document updates are compact binary diffs, not full snapshots

When a user edits a shape, the change is applied to the local Yjs document, then the binary update is sent to the server, which broadcasts it to all connected clients. Each client applies the update to their local Yjs document and the Zustand stores are updated accordingly.

Authentication

The host sets up JWT-based authentication:

  1. Host configures a password for the session
  2. Clients send AUTH_LOGIN with the password
  3. Server responds with a signed JWT token
  4. All subsequent messages include the JWT for authorization
  5. Tokens have configurable expiry

The Rust backend handles JWT signing and verification using the jsonwebtoken crate, with password hashing via bcrypt.

Offline Support

Diagrammer is offline-first. Collaboration features degrade gracefully when the network is unavailable.

OfflineQueue

When the WebSocket is disconnected, save and delete operations are queued rather than dropped:

User saves document → OfflineQueue stores operation →
  Network reconnects → Queue processes in order → Server receives updates

SyncStateManager

Coordinates the interaction between:

  • The offline queue
  • The storage layer
  • The connection state

Handles automatic retry with exponential backoff.

SyncQueueStorage

Persists the offline queue to IndexedDB so queued operations survive app restarts. On launch, any pending operations are processed once a connection is established.

Server Implementation (Rust)

The WebSocket server lives in src-tauri/src/server/:

FilePurpose
mod.rsServer startup, Axum router setup
protocol.rsMessage type definitions (must match TypeScript)
websocket.rsConnection handling, message routing
auth.rsJWT creation, verification, password validation

The server uses Axum for HTTP/WebSocket handling and Tokio for async I/O. It binds to a configurable port on the local network and detects the host's LAN IP for easy sharing.