6.1 KiB
OpenSpec Change: enterprise-browser-network-libp2p
- Change ID: enterprise-browser-network-libp2p
- Package:
@suaveplan/network-libp2p - Category: browser
- Target path:
browser/network/network-libp2p - Status: SUPERSEDED-BY-GENESIS-WAVE
- Date: 2026-04-24
- Reference spec:
/mnt/s/web/suaveplan/enterprise/network-libp2p.md
Summary
Deliver @suaveplan/network-libp2p on the genesis branch from scratch at enterprise quality. The package provides the browser-side libp2p transport for @suaveplan/network, stitching together the canonical libp2p stack (WebSockets, WebRTC, Noise, Yamux, identify, ping, bootstrap, circuit-relay-v2) into a Libp2pTransport class with connection lifecycle, peer addressing, streaming messaging, and metrics. No React dependencies — demo UI moves to a companion package.
Current-state audit (genesis)
The package does not exist on genesis yet — fresh build.
Available workspace deps on genesis
| Package | Role |
|---|---|
@suaveplan/network (workspace:^) |
Transport interface, peer/connection types |
@suaveplan/error (workspace:^) |
Typed libp2p errors |
@suaveplan/types (workspace:^) |
LoggerLike |
@suaveplan/events (workspace:^) |
Event maps |
@suaveplan/serialize (workspace:^) |
Wire message encoding |
@suaveplan/schema-registry (workspace:^) |
Config schema registration |
zod (catalog:forms) |
Libp2pConfig schema |
@opentelemetry/api (catalog:observability) |
Connection/stream counters, latency histograms |
tslib (catalog:build) |
Runtime helpers |
External libp2p deps (from Bun catalog): libp2p, @libp2p/websockets, @libp2p/webrtc, @libp2p/bootstrap, @libp2p/circuit-relay-v2, @libp2p/identify, @libp2p/ping, @libp2p/interface, @chainsafe/libp2p-noise, @chainsafe/libp2p-yamux, @multiformats/multiaddr.
No react / react-dom in runtime deps.
Dev deps: vitest, @vitest/coverage-istanbul, @playwright/test (catalog:testing), @suaveplan/testing workspace:^.
Preserved public API surface (from main branch)
class Libp2pTransport {
constructor(config: Libp2pConfig)
start(): Promise<void>
stop(): Promise<void>
dial(addr: Multiaddr | string): Promise<Libp2pConnection>
send(peerId: PeerId, protocol: string, message: Uint8Array): Promise<void>
request(peerId: PeerId, protocol: string, message: Uint8Array): Promise<Uint8Array>
registerHandler(protocol: string, handler: StreamHandler): void
unregisterHandler(protocol: string): void
getConnectedPeers(): PeerId[]
getMetrics(): Libp2pMetrics
on(event: 'peer:connect', handler: (peer: Libp2pPeerInfo) => void): void
on(event: 'peer:disconnect', handler: (peer: Libp2pPeerInfo) => void): void
on(event: 'error', handler: (error: Error) => void): void
}
interface Libp2pConfig {
listenAddresses?: string[]
bootstrapPeers?: string[] // Must be explicit; no public defaults unless strict: false
relayAddrs?: string[]
enableWebRTC?: boolean
enableWebSockets?: boolean
strict?: boolean // Default true; rejects public bootstrap peers
}
interface Libp2pMetrics { connectionsOpened: number; streamsOpened: number; bytesIn: number; bytesOut: number }
interface Libp2pPeerInfo { peerId: PeerId; multiaddrs: Multiaddr[]; protocols: string[] }
type StreamHandler = (stream: Stream, connection: Connection) => Promise<void>
New additions for genesis
1. Structured errors via @suaveplan/error
Error codes: LIBP2P_DIAL_FAILED, LIBP2P_STREAM_RESET, LIBP2P_UNSUPPORTED_TRANSPORT, LIBP2P_BOOTSTRAP_REJECTED, LIBP2P_RELAY_INVALID.
2. Strict-mode bootstrap gate
When strict: true (default), Libp2pConfig.bootstrapPeers must be explicitly supplied; throw LIBP2P_BOOTSTRAP_REJECTED if the list is empty. Users who want public defaults must set strict: false.
3. Transport presets
const presets = {
browserP2P(): Libp2pConfig // WebRTC + WebSocket + Yamux + Noise + identify + ping
browserBrowser(): Libp2pConfig // WebRTC only
browserNode(): Libp2pConfig // WebSocket + circuit-relay
}
4. Libp2pConfig Zod schema
Full runtime validation of config; registered in @suaveplan/schema-registry.
5. OTel metrics
Counters libp2p.connections_opened_total, libp2p.streams_opened_total, libp2p.bytes_in_total, libp2p.bytes_out_total; histograms libp2p.dial_ms, libp2p.stream_ttfb_ms.
6. Relay-addr validator
validateRelayAddrs(addrs) checks multiaddr format and rejects obviously insecure patterns.
File layout
browser/network/network-libp2p/
package.json
tsconfig.json
vitest.config.ts
src/
index.ts
error-codes.ts
lib/
libp2p-connection/
libp2p-connection.ts
libp2p-connection.md
libp2p-connection.test.ts
index.ts
libp2p-messaging/
libp2p-messaging.ts
libp2p-messaging.md
libp2p-messaging.test.ts
index.ts
peer-message/
peer-message.ts
peer-message.md
peer-message.test.ts
index.ts
send-to-peer/
send-to-peer.ts
send-to-peer.md
send-to-peer.test.ts
index.ts
stream-handler/
stream-handler.ts
stream-handler.md
stream-handler.test.ts
index.ts
transport/
libp2p-transport/
libp2p-transport.ts
libp2p-transport.md
libp2p-transport.types.ts
libp2p-transport.test.ts
index.ts
presets/
presets/
presets.ts
presets.md
presets.test.ts
index.ts
config/
config-schema/
config-schema.ts
config-schema.md
index.ts
tests/
integration.test.ts
e2e/
libp2p.spec.ts (two real libp2p nodes, echo protocol)
Impact
Downstream: P2P networking layers, drive distribution, real-time collaboration.
Breaking changes: react / react-dom removed from runtime deps (demo relocated).
Prerequisites: @suaveplan/network, @suaveplan/serialize, @suaveplan/error, @suaveplan/events on Verdaccio.