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). Store 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 } from "@cuitty/store/client";
const store = await createStore({
module: "notes",
profile: {
id: "server-sync",
name: "Server Sync",
local: { adapter: "local.sqlite", path: ".cuitty/store/notes.sqlite" },
remotes: [{ id: "bridge", adapter: "cuitty.storage.bridge", endpoint: "https://sync.example.com", authRef: "secret://device/token" }],
routes: [{ namespace: "notes", class: "record", localAdapter: "local.sqlite", remoteAdapters: ["bridge"] }],
encryption: { atRest: "preferred", remote: "required", p2p: "disabled" },
sync: { mode: "manual", flushEveryMs: 5000 },
},
schema: { notes: { class: "record" } },
});
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.
const p2pRemote = {
id: "mesh",
adapter: "p2p.libp2p",
endpoint: "memory://notes",
path: ".cuitty/store/p2p",
};
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 invite = await controller.invitePeer({
name: "Alice's laptop",
namespaces: ["notes"],
role: "trusted",
});
console.log(invite.code);
Step 2: Enter the code on device B
const peers = await controller.peers();
console.log(peers.map((peer) => peer.name));
Step 3: Verify the connection
const peers = await controller.peers();
for (const peer of peers) {
console.log(`${peer.name} — ${peer.lastSeenAt ?? "not seen yet"}`);
}
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 controller.peers();
// Revoke trust (stops syncing with this device)
await controller.revokePeer("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 store.record("notes").put("note:1", {
text: mergedText,
tags: [...new Set([...localTags, ...remoteTags])],
}, {
idempotencyKey: "resolve:note:1",
});
For most multi-device scenarios, last-write-wins is sufficient. Use custom merge when you need to preserve changes from both sides.