Quickstart
This page takes you from nothing to two peers exchanging a message. The first version runs in a single script with an in-memory network, so you can see the whole flow without a relay or a second device. The second version is the same code over real WebRTC.
1. Install
npm install @vaultys/peer-sdk @vaultys/id
# for real connections in the browser:
npm install peerjs
v2 isn't on npm yet (npm install @vaultys/peer-sdk still gives you v1). Until it's published, build it from source and link it, for example pnpm add link:../peer-sdk.
2. Two peers in one script
import { createPeer, memoryNetwork } from "@vaultys/peer-sdk";
import { VaultysId } from "@vaultys/id";
const network = memoryNetwork(); // an in-process network, for trying things out
const alice = await createPeer({
identity: await VaultysId.generatePerson(),
transport: network.transport(),
});
const bob = await createPeer({
identity: await VaultysId.generatePerson(),
transport: network.transport(),
});
// By default a peer only accepts connections from its contacts.
await bob.contacts.add(alice.did, { name: "Alice" });
bob.on("message", ({ from, body }) => {
console.log(`${from} says`, body); // `from` is Alice's proven DID
});
alice.on("delivered", ({ id }) => console.log("delivered", id));
await alice.send(bob.did, { text: "Hello, Bob" });
Run it and you'll see Bob receive the message, then Alice get the delivery receipt. Between those two lines, the peers:
- Opened a link through the transport.
- Ran a mutual VaultysID handshake. Bob checked that Alice is a contact before accepting.
- Derived a session key only they can compute.
- Sent the message encrypted, and Bob acknowledged it.
3. Real connections between browsers
Swap the in-memory network for the PeerJS transport. Peers find each other through a PeerJS relay, then talk over a direct WebRTC data channel.
import { createPeer, indexedDBStore } from "@vaultys/peer-sdk";
import { peerjsTransport } from "@vaultys/peer-sdk/peerjs";
import { VaultysId } from "@vaultys/id";
// Keep the identity across reloads: the SDK never stores secrets for you.
const saved = localStorage.getItem("my-identity");
const identity = saved ? VaultysId.fromSecret(saved) : await VaultysId.generatePerson();
if (!saved) localStorage.setItem("my-identity", identity.getSecret());
const peer = await createPeer({
identity,
transport: peerjsTransport(), // the public PeerJS relay; see "Relays" to run your own
store: indexedDBStore(), // contacts and undelivered messages survive reloads
});
console.log("Share this DID:", peer.did);
Open the page in two browsers, add each other's DID as a contact, and send:
await peer.contacts.add(friendDid, { name: "Friend" });
await peer.send(friendDid, { text: "Hi!" });
If your friend is offline, the message waits in the store and goes out when you next reach each other.
localStorage is fine for a demo. In a real app, the identity secret deserves storage your platform protects. See Identity and trust and Security model.
Next steps
- Messaging: delivery, receipts and message history.
- Custom protocols: typed real-time channels.
- Collaboration: shared documents with Yjs.