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.

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:
- 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).
- 4.WebRTC Data Channel: Establish a direct peer-to-peer UDP socket link between browsers.
- 6.Sync Protocol: When a user modifies a row locally, our engine extracts the binary changeset from
crsqliteand 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.
javascriptimport 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
nameand Peer B modifiedquantity, 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.

SQLite on the Edge: Replicating Databases with LiteFS and Fly.io
A technical dive into distributed edge storage, exploring how LiteFS replicates SQLite databases across global Fly.io regions using FUSE and lease-based consensus.

Implementing Post-Quantum Cryptography in Next.js: Securing APIs against Future Decryption
Future-proof your web applications today. Learn how to secure Next.js API routes using Post-Quantum Cryptography (PQC) algorithms like ML-KEM and Kyber.