Networking and events
Use typed events, pubsub, signed discovery, mDNS, and relay-assisted NAT traversal from TypeScript.
Minip2p.create returns a running endpoint. Configuration selects the network
capabilities it uses.
Prefer named events
Named subscriptions are typed by event name and receive a flattened payload:
const removeReady = endpoint.on("peerReady", ({ peerId, protocols }) => {
console.log("ready", peerId, protocols);
});
const removePath = endpoint.on("pathUpgraded", ({ peerId, from, to }) => {
console.log("upgraded", peerId, from.kind, "→", to.kind);
});
removeReady();
removePath();
For one result, use a Promise:
const message = await endpoint.waitFor("message", {
predicate: (event) => event.topics.includes("/example/chat/1"),
timeoutMs: 10_000,
});
once(name, options?) is the same pattern without a predicate.
Use async iteration when one loop handles every event:
const controller = new AbortController();
for await (const event of endpoint.events({ signal: controller.signal })) {
console.log(event.type, event);
}
The iterator ends when the endpoint closes or its signal aborts. Each iterator
buffers independently. A slow consumer receives queueOverflow with the number
of events it missed. Set bufferCap only when the application needs a different
per-iterator limit.
Use the catch-all form for logging and diagnostics:
const removeLog = endpoint.on((event) => {
console.log(event.type, event);
});
Catch-all events are a discriminated union with a type field. Inbound streams
appear there as safe inboundStream metadata only. Subscribe specifically to
"stream" to receive and claim a live Stream.
Handler exceptions are isolated from event delivery and from other handlers.
The endpoint emits handlerError afterward with safe metadata and the thrown
value.
Pubsub
Gossipsub is enabled by default. Messages published by this endpoint are signed, and inbound validation rejects unsigned messages by default.
const topic = "/example/chat/1";
endpoint.subscribe(topic);
const removeMessages = endpoint.on("message", (message) => {
if (message.topics.includes(topic)) {
console.log(new TextDecoder().decode(message.data));
}
});
endpoint.publish(topic, "hello from minip2p");
// Later:
removeMessages();
endpoint.unsubscribe(topic);
There is no self-delivery. Apply the local UI action directly and use the event
for remote messages. Queue pressure throws BackpressureError
synchronously; later forwarding failures arrive as pubsubOutboundFailure.
Select floodsub only for interoperability:
import { PubsubRouter } from "@minip2p/node";
const endpoint = Minip2p.create({
secretKey,
pubsubRouter: PubsubRouter.Floodsub,
});import { PubsubRouter } from "@minip2p/react-native";
const endpoint = Minip2p.create({
secretKey,
pubsubRouter: PubsubRouter.Floodsub,
});allowUnsigned: true accepts unsigned inbound application messages. When
message.signed is false, both the bytes and fromPeerId are
attacker-controlled; do not treat either as authenticated identity.
Signed discovery
Enable application-scoped signed beacons with a topic:
const endpoint = Minip2p.create({
secretKey,
discovery: {
topic: "/example/peer-discovery/1",
autoDial: true,
},
});
Discovery maintains a bounded, multi-source peer book and can automatically
dial advertised direct addresses. Inspect it with knownPeers() and observe
peerDiscovered, peerUpdated, peerExpired, and discoveryDialFailed.
Discovery beacons stay signed even when allowUnsigned is true for application
pubsub messages.
Local-link mDNS
Use mDNS to find peers on the same multicast-capable network:
const endpoint = Minip2p.create({
secretKey,
mdns: {
autoDial: true,
enableIpv6: false,
},
});
mdns: true uses the defaults. mDNS observations join the same
knownPeers() book as signed discovery. The event source distinguishes
DiscoverySource.Mdns from DiscoverySource.SignedBeacon.
Signed discovery and mDNS share one automatic-dial policy. If both options set
autoDial, the values must match. Set it only on discovery when mDNS should
inherit that choice.
Discovery stays local to the link and depends on the network allowing multicast. React Native apps must also declare the iOS local-network and Android multicast permissions.
Relay and NAT traversal
minip2p is a Circuit Relay v2 client and traversal orchestrator. Supply a reachable relay server as a direct QUIC or TCP peer multiaddress:
const endpoint = Minip2p.create({
secretKey,
relays: [relayAddress],
autonatServers: [probeServerAddress],
});
| Configuration | What it adds |
|---|---|
relays |
Reservations, relayed connections, direct/relay races, and relay-assisted DCUtR. |
autonatServers |
Reachability probing only. It does not create a traversal path. |
forceRelay: true |
Routes outbound connectivity through configured relays. |
Use connect* to run NAT orchestration. dial* remains direct-only.
const result = await endpoint.connectAddr(remoteAddress);
if (result.path.kind === "relayed") {
console.log("relay", result.path.relayPeerId);
}
console.log(endpoint.reachability());
console.log(endpoint.activeReservation());
console.log(endpoint.circuitAddress);
circuitAddress is available after a usable reservation matches one of the
configured relay addresses. The exported circuitAddress(relayAddress, peerId) helper builds the same address explicitly.
Watch relayReserved, relayReservationLost, pathEstablished,
pathUpgraded, holePunchFailed, fellBackToRelay, and connectFailed for
diagnostics and UI state. Keep using ordinary peer and stream APIs when a path
upgrades. The peer identity does not change.
See the TypeScript API reference for the full configuration and event-family map.