---
title: TypeScript API
description: Concise reference for the high-level TypeScript configuration, endpoint methods, streams, events, and public errors on Node.js and React Native.
---

The TypeScript SDK has one interface on two runtimes. Install `@minip2p/node` or `@minip2p/react-native` and import application APIs from that package.

Choose the import for the application's runtime:

**Node.js**

```ts
import {
  Minip2p,
  circuitAddress,
  generateSecretKey,
  peerIdFromSecretKey,
} from "@minip2p/node";
```

`@minip2p/node` is ESM-only and requires Node.js 24 or newer.

**React Native**

```ts
import {
  Minip2p,
  bindAppState,
  circuitAddress,
  generateSecretKey,
  peerIdFromSecretKey,
  useMinip2p,
} from "@minip2p/react-native";
```

`useMinip2p` and `bindAppState` exist only here.

Setup per runtime: [Node.js](/docs/typescript/setup-node), [React Native](/docs/typescript/setup-react-native).

## Configuration

`Minip2p.create(config)` constructs and starts an endpoint synchronously.

| `Minip2pConfig` field | Type | Purpose |
| --- | --- | --- |
| `secretKey` | `ArrayBuffer \| Uint8Array` | Required 32-byte Ed25519 secret key. |
| `agentVersion` | `string` | Identify agent string. |
| `listen` | `readonly [string, ...string[]]` | Listen multiaddresses; each address's shape selects QUIC or TCP. Omitted means dual-stack QUIC on ephemeral ports. |
| `protocols` | `readonly string[]` | Custom protocols accepted on inbound streams. |
| `relays` | `readonly string[]` | Direct QUIC or TCP relay peer addresses. |
| `autonatServers` | `readonly string[]` | Reachability probe-server addresses. |
| `forceRelay` | `boolean` | Route outbound connectivity through relays only. |
| `allowUnsigned` | `boolean` | Accept unsigned inbound application pubsub messages; default `false`. |
| `discovery` | `Minip2pDiscoveryOptions` | Signed-beacon topic, timing, and auto-dial policy. |
| `mdns` | `boolean \| Minip2pMdnsOptions` | Enable local-link discovery with defaults or explicit options. |

`discovery.topic` is required when signed discovery is enabled. Both discovery options objects accept `autoDial`; remaining timing and capacity fields are typed in the exported declarations and use defaults when omitted. Signed discovery and mDNS share one automatic-dial policy. If both set `autoDial`, the values must match.

`listen` is the only listen setting. A `/udp/<port>/quic-v1` address binds QUIC and a `/tcp/<port>` address binds TCP; a transport with no listen address is not bound. An explicit array replaces the defaults exactly, and QUIC accepts at most one listener per IP family.

```ts
// Omitted: QUIC on /ip4/0.0.0.0/udp/0/quic-v1 and /ip6/::/udp/0/quic-v1

// QUIC + TCP on fixed IPv4 ports
listen: ["/ip4/0.0.0.0/udp/4001/quic-v1", "/ip4/0.0.0.0/tcp/4001"];

// TCP only, IPv4 + IPv6
listen: ["/ip4/0.0.0.0/tcp/4001", "/ip6/::/tcp/4001"];
```

## Identity and lifecycle

| API | Result |
| --- | --- |
| `generateSecretKey()` | New `Uint8Array` containing a 32-byte Ed25519 secret. |
| `peerIdFromSecretKey(secretKey)` | Base58 peer ID without starting an endpoint. |
| `Minip2p.create(config)` | Started `Minip2p` endpoint. |
| `useMinip2p(createConfig)` | React Native Hook state plus an idempotent `close` callback. |
| `bindAppState(endpoint)` | React Native Idempotent unsubscribe for manual lifecycle ownership. |
| `endpoint.close(timeoutMs?)` | Terminal, idempotent shutdown; resolves with an `EndpointCloseReport` once native drained for up to `timeoutMs` (default 500). |
| `endpoint[Symbol.asyncDispose]()` | Awaits `close()`; supports `await using`. |
| `endpoint[Symbol.dispose]()` | Starts `close()` without waiting; supports `using`. |
| `endpoint.onClose(handler)` | One-shot close observer, called with the same report once the drain finishes; returns an unsubscribe. |
| `endpoint.isRunning()` | Whether the endpoint still accepts work. |
| `endpoint.setActive(active)` | Choose normal behavior or reduced background work. |

Node.js A started endpoint keeps the Node.js process alive until
`close()`. See [Endpoint lifecycle](/docs/typescript/lifecycle) for signal
handling and shutdown order.

## State queries

| API | Result |
| --- | --- |
| `peerId()` | Local peer ID. |
| `listenAddrs()` | Currently bound peer multiaddresses. |
| `connectedPeers()` | Peer IDs with live transport connections. |
| `isPeerReady(peerId)` | Whether the current connection completed Identify. |
| `peerInfo(peerId)` | Latest remote Identify snapshot, if known. |
| `knownPeers()` | Multi-source discovery address book. |
| `discoveryNowMs()` | Discovery monotonic time, when enabled. |
| `path(peerId)` | The current connection's `Path` (`direct`, `punched`, or `relayed`), if connected. |
| `connectionInfo(peerId)` | The current connection's `{ peerId, connId, remoteAddr, path, readyProtocols? }`, if connected; `readyProtocols` is set once that connection is ready. |
| `reachability()` | Latest AutoNAT reachability verdict. |
| `activeReservation()` | Active relay reservation metadata. |
| `circuitAddress` | Usable local circuit address, when reserved. |

## Operations

Promise operations use `{ timeoutMs?, signal? }`. Their default timeout is 65 seconds; `timeoutMs: 0` disables it. For Connection attempts a timeout ends only the wait (set `cancelOnTimeout: true` to also cancel), while an aborted `signal` cancels the attempt and the wait settles from its terminal.

| API | Result |
| --- | --- |
| `connect(target, options?)` | First usable path to a `ConnectTarget`: a peer ID, one complete peer address, or a list of complete addresses naming one peer. |
| `startConnect(target)` / `waitConnectResult(connectId, options?)` | Split-phase connection flow using a `connectId`. |
| `cancelConnect(connectId)` | Cancel an attempt; its terminal event settles any pending wait. |
| `waitPeerReady(peerId, options?)` | Peer ID and advertised protocol list once the peer's current connection completes Identify. A `peerReady` for a replaced connection does not resolve it. |
| `ping(peerId, options?)` | RTT in milliseconds. |
| `openStream(peerId, protocol, options?)` | Negotiated `Stream`. |
| `disconnect(peerId)` | Close the active peer connection. |

## Pubsub and protocols

| API | Result |
| --- | --- |
| `subscribe(topic)` | Whether the subscription set changed. |
| `unsubscribe(topic)` | Whether the subscription set changed. |
| `publish(topic, stringOrBytes)` | Queue an application pubsub message. |
| `addProtocol(protocolId)` | Accept future inbound streams for a protocol. |
| `circuitAddress(relayAddress, peerId)` | Build a relay circuit multiaddress. |

## Stream

| Member | Meaning |
| --- | --- |
| `peerId`, `protocolId` | Remote identity and negotiated protocol. |
| `streamId`, `connId` | Endpoint-local numeric identifiers. |
| `initiatedLocally` | Whether this endpoint opened the stream. |
| `read()` | Next `Uint8Array`, or `undefined` after remote half-close. Returning a chunk consumes it, which lets the sender continue. |
| `on("data", handler)` | Flowing read mode; mutually exclusive with `read()`. A chunk is consumed once every handler returns, so an `async` handler is consumed when it returns its Promise, not when the Promise settles. |
| `write(stringOrBytes)` | Send UTF-8 text or bytes; resolves once every byte is accepted. Rejects with `WriteBufferFullError` when bytes are already buffered and the write would push them past `writeHighWaterMark`. A queued write reads its buffer only when it reaches native, so leave the buffer untouched until the Promise settles. |
| `writeHighWaterMark` | Bytes a stream buffers before `write` rejects (default 256 KiB, counting the in-flight write). A write into an empty buffer is always admitted, whatever its size. |
| `closeWrite()` | Graceful local half-close after every earlier write. Throws if native refuses it while no write is pending; a close queued behind writes reports failure through the stream's terminal event. |
| `reset()` | Abrupt reset. |
| `abandon()` | Reset and relinquish the handle. |

Stream events are `data`, `remoteWriteClosed`, `writeStopped`, and `closed`. Stream data is never dropped: each stream delivers at most one receive window that has not been consumed, so a reader that stops reading stalls its sender.

## Events

`on(name, handler)` is the typed application path. `once` and `waitFor` expose the same named map as Promises. `on(handler)` receives catch-all metadata with a discriminating `type`.

`events({ signal?, bufferCap? })` returns an async iterator over the catch-all event stream. It ends when the endpoint closes or the signal aborts. A slow iterator yields `queueOverflow` before continuing with retained events.

| Family | Named events |
| --- | --- |
| Endpoint and queue | `eventsDropped`, `queueOverflow`, `driverFailed`, `handlerError`, `endpointError` |
| Connection and Identify | `connectionEstablished`, `connectionClosed`, `connectionReplaced`, `peerReady`, `identifyReceived` |
| Ping and streams | `pingRttMeasured`, `pingTimeout`, `stream` |
| Relay and NAT | `reachabilityChanged`, `publicAddressesChanged`, `relayReserved`, `relayReservationLost`, `pathEstablished`, `pathUpgraded`, `holePunchFailed`, `connectFailed`, `connectCancelled`, `inboundDirectUpgrade` |
| Pubsub | `message`, `peerSubscribed`, `peerUnsubscribed`, `gossipsubOutboundFailure`, `gossipsubProtocolViolation` |
| Discovery | `peerDiscovered`, `peerUpdated`, `peerExpired`, `discoveryDialFailed`, `discoveryProtocolViolation` |

`connectionReplaced` (`{ peerId, oldConnId, newConnId }`) reports a newer connection taking over the peer's single connection slot; the peer stays connected, streams and pending opens on `oldConnId` end with `StreamClosedError`, and `peerReady` (which carries `connId`) fires again for `newConnId`. `connectionClosed` (`{ peerId, connId, reason }`) means the peer's last connection closed; `reason` is a `ConnectionCloseReason`: `{ kind: "local" }`, `{ kind: "remote", code }` (the peer's QUIC close or Yamux GoAway code, a `bigint`), `{ kind: "timedOut" }`, `{ kind: "stalled" }`, or `{ kind: "error", message }`. A failure that ended the connection is its reason, not a separate `endpointError`.

The catch-all stream uses `inboundStream` metadata instead of the live `stream` handle. Subscribe to the named `stream` event to claim an inbound stream.

## Public errors

Use `instanceof` for control flow and the typed `kind` fields for telemetry.

| Error | Meaning |
| --- | --- |
| `AbortError` | This caller's wait was cancelled. |
| `ConnectCancelledError` | An `AbortError`: the attempt ended with a `connectCancelled` terminal; includes `connectId` and `peerId`. |
| `ConnectResultLostError` | A dropped event carried the attempt's terminal; recover state with `connectedPeers()` / `path()`. |
| `TimeoutError` | A Promise operation exceeded its timeout. |
| `EventQueueOverflowError` | Event loss made an in-flight result unknowable. Refresh state, then retry the operation. |
| `ClosedError` | The endpoint or stream is already closed. |
| `DriverFailedError` | The endpoint stopped; `kind` identifies the failed subsystem. |
| `ConnectFailedError` | NAT orchestration ended without a path; includes `connectId`, `peerId`, and `kind`. |
| `ConnectResultUnavailableError` | A split-phase result is unknown, already awaited, or consumed. |
| `PeerDisconnectedError` | The peer disconnected during a pending operation. |
| `OpenStreamError` | Opening or negotiation failed, with structured peer/stream context. |
| `StreamClosedError` | A stream closed before outbound negotiation completed. |
| `WriteBufferFullError` | A stream `write` would push buffered bytes past `writeHighWaterMark`; includes `buffered` and `highWaterMark`. Await earlier writes before writing more. |
| `BackpressureError` | The outbound pubsub queue is full. |
| `MessageTooLargeError` | A payload exceeds the protocol limit. |
| `NotPermittedError` | Endpoint policy rejected the operation. |

Other native failures, such as an invalid peer ID or address, are rethrown unchanged. On Node.js they are `Error`s whose `code` is the native `FfiError` variant name (for example `InvalidPeerId`) and whose `detail`, when present, is the variant's detail text.
