P
Persist

P2P Adapter

Direct device-to-device replication using libp2p with peer discovery, trust management, and NAT traversal.

Overview

The P2P adapter enables direct device-to-device replication without a central server. Built on libp2p, it handles peer discovery, connection management, and NAT traversal. Data flows directly between trusted devices over encrypted channels.

Setup

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

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

// Enable P2P sync alongside local storage
await store.enableP2P({
  listenAddrs: ["/ip4/0.0.0.0/tcp/0"],
  bootstrapPeers: [
    "/dns4/bootstrap.persist.one/tcp/4001/p2p/QmPeer1...",
  ],
});

The P2P adapter works alongside a local adapter (typically SQLite). Local storage handles reads and writes while P2P handles replication between devices.

Peer discovery

Persist uses three discovery mechanisms:

  1. mDNS — automatic discovery of peers on the same local network. No configuration needed.
  2. Bootstrap peers — well-known peers that help new devices find each other.
  3. DHT — distributed hash table for discovering peers across the internet.
await store.enableP2P({
  discovery: {
    mdns: true,                // LAN discovery
    dht: true,                 // internet discovery
    bootstrapPeers: ["..."],   // initial peer list
  },
});

Trust management

Not every discovered peer should be allowed to sync. Persist uses a trust model where devices must be explicitly paired before data is exchanged.

// On device A: generate a pairing code
const code = await store.p2p.createPairingCode();
console.log(`Pairing code: ${code}`); // e.g., "ABCD-1234"

// On device B: accept the pairing code
await store.p2p.pair(code);

Once paired, devices exchange Ed25519 public keys. All subsequent sync payloads are signed and verified. Unpaired peers are rejected at the transport level.

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

// Revoke trust
await store.p2p.revokeTrust(peerId);

NAT traversal

Most devices sit behind NAT routers that block incoming connections. Persist uses several techniques to establish connectivity:

  • UPnP/NAT-PMP — automatic port mapping on supported routers
  • Relay servers — traffic routes through a relay when direct connection fails
  • Hole punching — coordinated connection attempts to traverse symmetric NATs

These mechanisms are enabled by default. No manual port forwarding is required for most networks.

Supported store classes

The P2P adapter supports records, events, and kv. Blob replication over P2P is not currently supported due to bandwidth constraints. Use the S3 adapter for blob storage with a sync server.