9.5 KiB
9.5 KiB
@suaveplan/network-libp2p-server - Server-tier libp2p relay daemon (bootstrap + circuit-relay-v2) paired with @suaveplan/network-transport-libp2p — Node-only HTTP/libp2p host with structural Zod option validation, AbortSignal lifecycle, health/ready/live endpoints, OTel observability, and injectable libp2p factory for real-fake integration tests.
Summary
Server-tier libp2p relay daemon paired with
@suaveplan/network-transport-libp2p.
Hosts a libp2p node configured as a bootstrap + circuit-relay-v2 server
alongside an inline HTTP listener exposing /health, /ready, /live
verbs.
Documentation tree
- errors — (no submodules)
- errors — (no submodules)
- health — (no submodules)
- health — (no submodules)
- otel — (no submodules)
- otel — (no submodules)
- server — (no submodules)
- health-listener — Wraps the asynchronous
http.Server.listen(port, host)flow with the listener-attach /EADDRINUSEclassification / `… - peer-events — Wires the
peer:connectandpeer:disconnectevent listeners onto aLibp2pServerNodeLikeand bridges them onto the… - server — (no submodules)
- health-listener — Wraps the asynchronous
- types — (no submodules)
- types — (no submodules)
- peer-events — Wires the
peer:connectandpeer:disconnectevent listeners onto aLibp2pServerNodeLikeand bridges them onto the… - server — (no submodules)- types — (no submodules)- types — (no submodules)- errors — (no submodules)- health — (no submodules)- health — (no submodules)- otel — (no submodules)- otel — (no submodules)- server — (no submodules)- health-listener — Wraps the asynchronous
http.Server.listen(port, host)flow with the listener-attach /EADDRINUSEclassification / `… - peer-events — Wires the
peer:connectandpeer:disconnectevent listeners onto aLibp2pServerNodeLikeand bridges them onto the… - server — (no submodules)- types — (no submodules)- types — (no submodules)- errors — (no submodules)- health — (no submodules)- health — (no submodules)- otel — (no submodules)- otel — (no submodules)- server — (no submodules)- health-listener — Wraps the asynchronous
http.Server.listen(port, host)flow with the listener-attach /EADDRINUSEclassification / `… - peer-events — Wires the
peer:connectandpeer:disconnectevent listeners onto aLibp2pServerNodeLikeand bridges them onto the… - server — (no submodules)- types — (no submodules)- types — (no submodules)- errors — (no submodules)- health — (no submodules) - health — (no submodules)- otel — (no submodules) - otel — (no submodules)- server — (no submodules) - health-listener — Wraps the asynchronous
http.Server.listen(port, host)flow with the listener-attach /EADDRINUSEclassification / `… - peer-events — Wires the
peer:connectandpeer:disconnectevent listeners onto aLibp2pServerNodeLikeand bridges them onto the… - server — (no submodules)- types — (no submodules) - types — (no submodules)
Run the daemon
import { createLibp2p } from "libp2p";
import { tcp } from "@libp2p/tcp";
import { webSockets } from "@libp2p/websockets";
import { noise } from "@chainsafe/libp2p-noise";
import { yamux } from "@chainsafe/libp2p-yamux";
import { identify } from "@libp2p/identify";
import { circuitRelayServer } from "@libp2p/circuit-relay-v2";
import { bootstrap } from "@libp2p/bootstrap";
import { Libp2pServer } from "@suaveplan/network-libp2p-server";
const server = new Libp2pServer({
listen: ["/ip4/0.0.0.0/tcp/9090", "/ip4/0.0.0.0/tcp/9091/ws"],
bootstrap: ["/dnsaddr/bootstrap.libp2p.io/p2p/QmNnoo..."],
autoRelay: { enabled: true, maxConnections: 500 },
healthPort: 9099,
libp2pFactory: async (cfg) =>
createLibp2p({
addresses: { listen: [...cfg.listen] },
transports: [tcp(), webSockets()],
connectionEncrypters: [noise()],
streamMuxers: [yamux()],
peerDiscovery:
cfg.bootstrap.length > 0
? [bootstrap({ list: [...cfg.bootstrap] })]
: [],
services: {
identify: identify(),
relay: circuitRelayServer({
reservations: {
maxReservations: cfg.autoRelay.maxConnections,
},
}),
},
connectionManager: { maxConnections: cfg.maxConnections },
}),
});
const { peerId, multiaddrs } = await server.start();
console.log(`libp2p relay ${peerId} on:`);
for (const a of multiaddrs) console.log(` ${a}`);
// Graceful shutdown
process.on("SIGTERM", () => server.stop());
Test environment
Tests inject a deterministic FakeLibp2pServerNode so the native
transitive deps of the real libp2p package never enter the test
surface — the daemon never resolves libp2p at module evaluation time
and the integration suite boots a real Libp2pServer inside the test
process.
import { Libp2pServer } from "@suaveplan/network-libp2p-server";
import {
FakeLibp2pHub,
makeFakeServerFactory,
} from "./tests/setup/fake-libp2p-server.js";
const hub = new FakeLibp2pHub();
const { factory } = makeFakeServerFactory(hub);
const server = new Libp2pServer({
libp2pFactory: factory,
listen: ["/ip4/0.0.0.0/tcp/0"],
healthPort: 0,
});
await server.start();
// ... drive the daemon, then:
await server.stop();
Acceptance gates
bunx turbo lint typecheck test --filter=@suaveplan/network-libp2p-serverbunx turbo test:coverage --filter=@suaveplan/network-libp2p-server→ 100/100/100/100bun run docs:check --filter=@suaveplan/network-libp2p-server
API
See per-module .md docs:
src/server/server.md—Libp2pServerdaemonsrc/server/peer-events.md— peer connect/disconnect bridgingsrc/server/health-listener.md— inline-HTTPlistenHttphelpersrc/health/health.md—/health//ready//liveverb pipelinesrc/otel/otel.md— OTel instrumentation surfacesrc/errors/errors.md— error registrysrc/types/types.md— public type contracts