Skip to content
minip2p
Esc
navigateopen⌘Jpreview
On this page

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.

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.

Last updated on September 7, 2026

Was this page helpful?