P
Persist

Multi-Device Sync

Set up P2P replication, pair devices, establish trust, and keep data in sync across laptops, phones, and servers.

Overview

Multi-device sync lets a single user access their data across multiple devices (laptop, phone, tablet, server). Persist supports two approaches: server-mediated sync through the sync server, and direct P2P sync using libp2p.

Server-mediated sync

The simplest setup uses a central sync server as a relay. All devices push to and pull from the server.

import { createStore, enableSync } from "@cuitty/persist";

const store = await createStore("notes", {
  adapter: "sqlite",
  path: "./notes.db",
});

await enableSync(store, {
  remote: "https://sync.example.com",
  authToken: process.env.PERSIST_DEVICE_TOKEN,
  strategy: "last-write-wins",
  interval: 5000,
});

Each device uses its own auth token. The server associates tokens with a user identity and routes sync traffic between that user’s devices.

P2P setup

For serverless sync, enable the P2P adapter. Devices discover each other on the local network or through bootstrap peers.

await store.enableP2P({
  discovery: {
    mdns: true,           // discover devices on LAN
    dht: true,            // discover devices on internet
    bootstrapPeers: [
      "/dns4/bootstrap.persist.one/tcp/4001/p2p/QmBootstrap...",
    ],
  },
});

Pairing devices

Before two devices can sync over P2P, they must be paired. Pairing exchanges cryptographic keys and establishes mutual trust.

Step 1: Generate a pairing code on device A

const code = await store.p2p.createPairingCode({
  expiresIn: 300, // 5 minutes
  label: "Alice's laptop",
});
console.log(`Enter this code on your other device: ${code}`);
// Output: "KXNR-7842"

Step 2: Enter the code on device B

await store.p2p.pair("KXNR-7842");
console.log("Paired successfully!");

Step 3: Verify the connection

const peers = await store.p2p.trustedPeers();
for (const peer of peers) {
  console.log(`${peer.label} — ${peer.online ? "online" : "offline"}`);
}

Trust management

Paired devices are stored in a local trust registry. You can list, inspect, and revoke trust at any time.

// List trusted peers
const peers = await store.p2p.trustedPeers();

// Revoke trust (stops syncing with this device)
await store.p2p.revokeTrust("peer-id-abc123");

Revoking trust is immediate. The revoked device can no longer push or pull changes. Existing data on the revoked device is not deleted, but it will no longer receive updates.

Conflict handling

When two devices edit the same record offline and then reconnect, a conflict occurs. Configure your preferred strategy:

await enableSync(store, {
  remote: "https://sync.example.com",
  strategy: "merge",
  merge: (local, remote) => {
    // Merge: keep the longer text, combine tags
    return {
      text: local.value.text.length > remote.value.text.length
        ? local.value.text
        : remote.value.text,
      tags: [...new Set([...local.value.tags, ...remote.value.tags])],
    };
  },
});

For most multi-device scenarios, last-write-wins is sufficient. Use custom merge when you need to preserve changes from both sides.