Modern Web

Synchronizing SQLite Databases Over WebRTC: Building a Fully Decentralized Sync Engine

Master peer-to-peer web systems. Sync in-browser SQLite databases directly between clients over WebRTC Data Channels using CRDT binary deltas.

Sachin Sharma
Sachin SharmaCreator
Jun 1, 2026
4 min read
Synchronizing SQLite Databases Over WebRTC: Building a Fully Decentralized Sync Engine
Featured Resource
Quick Overview

Master peer-to-peer web systems. Sync in-browser SQLite databases directly between clients over WebRTC Data Channels using CRDT binary deltas.

Synchronizing SQLite Databases Over WebRTC: Building a Fully Decentralized Sync Engine

Most modern collaborative systems rely on a central server to coordinate sync. Even local-first applications usually connect back to a server-side WebSocket gateway to relay update deltas between clients.

But what if you want to bypass the server entirely? What if you want to sync databases directly between browsers on a local network or across the globe with zero server infrastructure costs and absolute privacy?

By combining SQLite running in WebAssembly with WebRTC Data Channels, you can build a fully decentralized, peer-to-peer (P2P) database synchronization engine. Changes sync between devices in real-time with microsecond local speeds.

In this guide, we'll design a decentralized sync architecture, configure SQLite crds (Conflict-Free Replicated Relations), and implement WebRTC binary streaming synchronization.


⚡ 1. The P2P Synchronization Architecture

In a peer-to-peer database sync system:

  1. 2.
    crsqlite (Conflict-free SQLite): A specialized SQLite WebAssembly extension that turns standard SQL tables into CRDTs. Every insert, update, or delete is recorded as a mathematical delta (changeset).
  2. 4.
    WebRTC Data Channel: Establish a direct peer-to-peer UDP socket link between browsers.
  3. 6.
    Sync Protocol: When a user modifies a row locally, our engine extracts the binary changeset from crsqlite and streams it directly over the WebRTC Data Channel to the peer, who applies it instantly.
[Peer A SQLite (crsqlite)] ──(Local SQL Mutation)──> [Extract Changeset]
                                                               │
                                                 (Send over WebRTC DataChannel)
                                                               ▼
[Peer B SQLite (crsqlite)] <──(Apply Changeset) <──────────────┘

🏗️ 2. Setting Up crsqlite (Conflict-Free SQL)

Standard SQLite tables will throw primary key conflicts if merged from multiple sources. To solve this, we use the compiled cr-sqlite WebAssembly extension, which adds CRDT column tracking to standard SQL tables.

sql
-- 1. Enable CRR (Conflict-Free Replicated Relation) on your table CREATE TABLE products ( id TEXT PRIMARY KEY, name TEXT, quantity INTEGER ); -- Turn the standard table into a CRDT SELECT crsql_as_crr('products');

Once turned into a CRR, SQLite automatically creates internal tracking triggers that record changesets, vector clocks, and deleted rows in system tables.


💻 3. Implementing WebRTC Binary Sync in JavaScript

Let's write our peer connection orchestrator that initializes the WebRTC Data Channel and wires it directly to the SQLite changeset stream.

javascript
import initWasm from '@vlcn.io/crsqlite-wasm'; let db; async function initDecentralizedNode(roomId) { // 1. Initialize cr-sqlite WebAssembly const sqlite = await initWasm(); db = await sqlite.open('decentralized.db'); // Turn on conflict-free tracking await db.exec("CREATE TABLE IF NOT EXISTS notes (id TEXT PRIMARY KEY, content TEXT);"); await db.exec("SELECT crsql_as_crr('notes');"); // 2. Establish WebRTC Peer Connection const peerConnection = new RTCPeerConnection({ iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] }); // 3. Create raw binary Data Channel const dataChannel = peerConnection.createDataChannel('db-sync-channel', { ordered: true // Ensure database packets arrive in chronological order }); dataChannel.binaryType = 'arraybuffer'; setupDataChannelHandlers(dataChannel); } function setupDataChannelHandlers(dataChannel) { // 4. Capture inbound peer changesets and apply to local SQLite database dataChannel.onmessage = async (event) => { const changesetBinary = new Uint8Array(event.data); console.log("📥 Received changeset packet from peer!"); // Apply the remote changeset to SQLite. // cr-sqlite resolves column conflicts mathematically! await db.exec( "INSERT INTO crsql_changes VALUES (?, ?, ?, ?, ?, ?);", parseChangesetFields(changesetBinary) ); console.log("✔️ Remote changeset successfully merged!"); }; // 5. Observe local database mutations and stream out changes db.onMutation(async () => { if (dataChannel.readyState === 'open') { // Query cr-sqlite for any changesets unsynced (greater than peer's clock) const changesets = await db.execO( "SELECT * FROM crsql_changes WHERE site_id != crsql_site_id();" ); for (const row of changesets) { const binaryPayload = serializeRowToBinary(row); dataChannel.send(binaryPayload); console.log("📤 Sent local mutation changeset to peer!"); } } }); }

🚀 4. Resolving Conflicts: Last-Write-Wins CRDT

Under the hood, cr-sqlite resolves overlapping row updates using a Last-Write-Wins (LWW) Element-Set CRDT model.

If Peer A and Peer B both update the name of a product with ID "142" offline:

  • Peer A updates name to "Fluid Dynamics" (Timestamp: 1779344246290).
  • Peer B updates name to "WebGPU Liquid Physics" (Timestamp: 1779344246850).
  • When Peer A and B reconnect and sync over WebRTC, the engine compares column-level timestamps.
  • Peer B's update has the higher timestamp, so the column value is updated to "WebGPU Liquid Physics" on both databases deterministically.
  • Replication occurs at the column level, so if Peer A modified name and Peer B modified quantity, both changes merge successfully without overwriting each other!

🏁 5. Conclusion

Decentralized peer-to-peer database synchronization represents the next major milestone in web application resilience. By combining the offline performance of in-browser WebAssembly SQLite databases with direct, zero-server WebRTC UDP data streams, you construct collaborative systems with zero server infrastructure overhead and absolute user data privacy.

Sachin Sharma

Sachin Sharma

Software Developer

Building digital experiences at the intersection of design and code. Sharing weekly insights on engineering, productivity, and the future of tech.