Collaboration Architecture
Taloflow's real-time collaboration is built on CRDTs (Conflict-free Replicated Data Types) via Y-WebSocket. This means multiple users can edit an evaluation simultaneously and their changes will always converge to a consistent state — with no merge conflicts and no data loss.
What Is a CRDT?
A CRDT (Conflict-free Replicated Data Type) is a data structure specifically designed so that concurrent edits from multiple clients can always be merged automatically and deterministically. Unlike traditional operational transformation or "last write wins" strategies, CRDTs preserve every edit and reconcile them through mathematical guarantees rather than server-side arbitration.
Architecture Overview
User Browser A User Browser B
| |
| wss:// (TLS) | wss:// (TLS)
v v
+---------------------------+
| Y-WebSocket Server |
| - Receives CRDT updates |
| - Broadcasts to peers |
| - Persists to Redis |
+---------------------------+
|
| HTTP POST (changes only)
v
+---------------------------+
| Eval-Update Lambda |
| - Writes DB delta |
| - Stores S3 snapshot |
| (encrypted, SSE-C) |
+---------------------------+
|
PostgreSQL + S3Component Responsibilities
Y-WebSocket Server
Receives CRDT update messages from connected browsers, broadcasts them to all peers in the session, and persists live state to Redis
Redis
Holds the authoritative in-memory CRDT state for active collaboration sessions
Eval-Update Lambda
Receives change notifications via HTTP POST, writes a structured delta to PostgreSQL, and periodically stores an encrypted full-document snapshot to S3
PostgreSQL
Stores evaluation metadata, structure, and deltas as durable relational records
S3
Stores encrypted full-document snapshots for point-in-time recovery
Privacy Design of the Collaboration Layer
Transport Security
All WebSocket connections use wss:// — WebSocket Secure over TLS. Unencrypted WebSocket connections (ws://) are not permitted.
Identity Within the Collaboration Layer
All change attribution within an evaluation uses
member_id(UUID) only.Email addresses, display names, and other PII are never embedded in CRDT update messages.
Services that do not require user identity (evaluation resolver, catalog resolver) never receive PII.
Application-Level Encryption for Snapshots
S3 snapshots are encrypted at two layers:
Application layer: The evaluation document is gzip-compressed, base64-encoded, and AES-encrypted before being written to S3.
Server-side encryption: S3 applies SSE-C (Server-Side Encryption with Customer-Provided Keys) as an additional layer.
Lambda Data Isolation
The eval-update Lambda includes a doNotProcessUpdates flag that prevents the Lambda from re-ingesting its own output. This eliminates feedback loops in which a Lambda-written change triggers a new CRDT update, which triggers another Lambda invocation.
What Is Persisted and When
Live CRDT state
Redis
Continuously, during active collaboration session
Evaluation delta (structure + metadata)
PostgreSQL
On each change, written by eval-update Lambda
Full-document snapshot
S3 (encrypted)
Periodically, triggered by eval-update Lambda
Multi-User Scenarios
Two users edit the same section simultaneously
Each browser generates a CRDT update locally and transmits it to the Y-WebSocket server. The server broadcasts both updates to all connected peers. Each client applies the remote update to its local CRDT. Because CRDTs are mathematically guaranteed to converge, both clients reach the same final state regardless of which update arrived first. No conflict dialog is shown; no data is lost.
A user disconnects and reconnects
On reconnection, the Y-WebSocket server sends the client the full current CRDT state from Redis. If the Redis state has been evicted (session expired), the Lambda retrieves the most recent S3 snapshot, decrypts it, and restores the session state before reconnecting the client.
A workspace with 50 concurrent editors
Y-WebSocket broadcasts updates as compact binary CRDT diff messages, not full document copies. Message size scales with the change, not the document size, so 50 concurrent editors produce 50 small diff broadcasts per change — not 50 full-document transmissions. Redis holds a single authoritative state object regardless of the number of connected clients.
Was this helpful?