Getting Started
Problem
Two devices on the same network must exchange typed messages with no server. You need the smallest possible pairing loop: invitation out, answer back, channel open — using createMeshHost, createMeshGuest, and meshCodec.
Solution
ts
import { createMeshGuest, createMeshHost, meshCodec, type MeshProtocol } from '@vielzeug/mesh';
// One declaration drives every send/on signature on both sides.
interface AppProtocol extends MeshProtocol {
toHost: { note: string };
toGuest: { ack: { ok: boolean } };
}
const host = createMeshHost<AppProtocol>();
const guest = createMeshGuest<AppProtocol>();
// Subscribe before accepting so early messages are not 'unknown-type'.
host.on('note', (m) => host.send(m.peerId, 'ack', { ok: true }));
guest.on('ack', (m) => console.log('ack:', m.payload.ok));
// Leg 1 — host → guest, out-of-band (copy/paste or navigator.share)
const invitationText = meshCodec.encode(await host.createInvitation());
// Leg 2 — guest → host, out-of-band
const answerText = meshCodec.encode(await guest.acceptInvitation(meshCodec.decode(invitationText)));
// Resolves once the data channel is open.
try {
await host.acceptAnswer(meshCodec.decode(answerText));
guest.send('note', 'hello');
} finally {
host.dispose();
guest.dispose();
}Pitfalls
RTCPeerConnectionneeds a secure context — serve overhttps:orlocalhost, orcreateInvitationfails at the native layer.- An invitation is single-use and expires (
invitationTtlMs, default 5 minutes);acceptAnswerthen throwsMeshPairingError. acceptAnswerwaits for the channel to open — deliver the answer promptly or the guest's open guard marks it'failed'.- On the guest, inbound
peerIdis the invitation'ssessionId; it is not the host node'sid.