Files
suaveplan/openspec/archive/2026-05-12-enterprise-browser-network-libp2p/proposal.md
2026-05-27 23:29:53 +01:00

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.