Files
2026-05-27 23:47:53 +01:00
..
2026-05-27 23:47:53 +01:00
2026-05-27 23:47:53 +01:00
2026-05-27 23:47:53 +01:00
2026-05-27 23:47:53 +01:00
2026-05-27 23:47:53 +01:00
2026-05-27 23:47:53 +01:00
2026-05-27 23:47:53 +01:00
2026-05-27 23:47:53 +01:00

@suaveplan/drive-webrtc - WebRTC P2P mesh plugin for @suaveplan/drive: signaling, peer management, data channels, chunk discovery, real signature verification.

Summary

Browser-tier WebRTC P2P chunk-distribution plugin for @suaveplan/drive. Provides a complete mesh networking stack — signalling client, ICE-aware WebRTC client, peer manager, data-channel manager, integrity verifier, chunk-discovery engine, and a ChunkSource consumable by Drive — wrapped in a single WebRTCDrivePlugin orchestrator. Every layer is constructed with real implementations (@suaveplan/crypto-curve Ed25519, @suaveplan/crypto-hash SHA-256, @suaveplan/events event bus, @suaveplan/error error codes), and every async path threads AbortSignal so cooperative cancellation works end-to-end.

This package targets the browser (globalThis.WebSocket, globalThis.RTCPeerConnection) but accepts constructor injections so tests and SSR contexts can wire deterministic doubles.


Documentation tree

- [chunk-source](./src/chunk-source/chunk-source/chunk-source.md) — _(no submodules)_- [client](./src/client/client/client.md) — _(no submodules)_- [client](./src/client/client/client.md) — _(no submodules)_- [ice](./src/client/ice/ice.md) — _(no submodules)_- [ice](./src/client/ice/ice.md) — _(no submodules)_- [offer-answer](./src/client/offer-answer/offer-answer.md) — _(no submodules)_- [offer-answer](./src/client/offer-answer/offer-answer.md) — _(no submodules)_- [reconnect](./src/client/reconnect/reconnect.md) — _(no submodules)_- [reconnect](./src/client/reconnect/reconnect.md) — _(no submodules)_- [data-channel-manager](./src/data-channel/data-channel-manager/data-channel-manager.md) — _(no submodules)_- [data-channel-manager](./src/data-channel/data-channel-manager/data-channel-manager.md) — _(no submodules)_- [chunk-discovery-manager](./src/discovery/chunk-discovery-manager/chunk-discovery-manager.md) — _(no submodules)_- [chunk-discovery-manager](./src/discovery/chunk-discovery-manager/chunk-discovery-manager.md) — _(no submodules)_- [typed-event-emitter](./src/events/typed-event-emitter/typed-event-emitter.md) — _(no submodules)_- [typed-event-emitter](./src/events/typed-event-emitter/typed-event-emitter.md) — _(no submodules)_- [chunk-integrity](./src/integrity/chunk-integrity/chunk-integrity.md) — _(no submodules)_- [chunk-integrity](./src/integrity/chunk-integrity/chunk-integrity.md) — _(no submodules)_- [lru-cache](./src/lru-cache/lru-cache/lru-cache.md) — _(no submodules)_- [lru-cache](./src/lru-cache/lru-cache/lru-cache.md) — _(no submodules)_- [peer-bandwidth-tracker](./src/peer-manager/peer-bandwidth-tracker/peer-bandwidth-tracker.md) — _(no submodules)_- [peer-bandwidth-tracker](./src/peer-manager/peer-bandwidth-tracker/peer-bandwidth-tracker.md) — _(no submodules)_- [peer-manager](./src/peer-manager/peer-manager/peer-manager.md) — _(no submodules)_- [peer-manager](./src/peer-manager/peer-manager/peer-manager.md) — _(no submodules)_- [full-mesh](./src/peer-manager/topology/full-mesh.md) — Every peer connects to every other peer up to a configurable cap. This is the default topology and matches the assumpti…

Quick Start

bun add @suaveplan/drive-webrtc
import { Drive } from "@suaveplan/drive";
import { WebRTCDrivePlugin } from "@suaveplan/drive-webrtc";

const plugin = new WebRTCDrivePlugin({
    peerId: crypto.randomUUID(),
    config: {
        signaling: { url: "wss://signal.example.com/p2p" },
        p2p: {
            iceServers: [{ urls: ["stun:stun.l.google.com:19302"] }],
        },
    },
    signalingUrl: "wss://signal.example.com/p2p",
});

await plugin.start();

const drive = new Drive(/* storage */);
// Drive integration: hand the chunk source to your read pipeline.
const bytes = await plugin.chunkSource.fetchChunk("chunk:42");

// Subscribe to any of the plugin-level events.
plugin.on("webrtc:connected", ({ peerId }) => {
    console.info(`p2p peer ${peerId} is up`);
});

// On shutdown:
await plugin.stop();

Subpath Map

Every internal subsystem is published under a dedicated subpath so downstream apps tree-shake to the minimum surface they need. Eleven tree-shake snapshots in tests/bundle-size/ enforce the leaf boundaries.

Subpath Primary export Purpose
. (root) Everything Convenience barrel — re-exports every public surface below.
./plugin/webrtc-plugin WebRTCDrivePlugin Top-level orchestrator. Start here.
./client/webrtc-client WebRTCClient, resolveConfig ICE-aware peer-connection lifecycle.
./signaling/signaling-client SignalingClient WebSocket signalling with bounded-backoff reconnect.
./peer-manager/peer-manager PeerManager Peer admission / eviction + bandwidth-scored ranking.
./data-channel/data-channel-manager DataChannelManager Multiplexed RTCDataChannel lifecycle + backpressure.
./integrity/chunk-integrity ChunkIntegrityManager Real SHA-256 + Ed25519 verify per inbound chunk.
./chunk-source/chunk-source WebRTCChunkSource Drive-facing ChunkSource backed by the discovery manager.
./rate-limiter/rate-limiter RateLimiter Token-bucket rate limiter.
./lru-cache/lru-cache LRUCache Bounded LRU cache for chunk hot-path memoisation.
./security/security-validator validateSignalingUrl, generateSecurePeerId, ... UUID v4 + signalling-URL guards.
./sub-chunk/sub-chunk-splitter splitIntoSubChunks, SubChunkReassembler Sub-chunk fragmentation + reassembly for data-channel transfers.
./errors DRIVE_WEBRTC_CODES, DriveWebRtcError Stable typed error registry.
./schemas Zod schemas Wire-format validation for every signalling + config surface.

ICE / TURN Operations

WebRTC peers exchange ICE candidates over signalling; in production networks behind symmetric NATs or restrictive firewalls, peers cannot reach each other directly and require a TURN relay. The p2p.iceServers list is exhaustively typed as RTCIceServer[]:

{
    p2p: {
        iceServers: [
            // STUN: enough on permissive networks.
            { urls: ["stun:stun.l.google.com:19302"] },

            // TURN: relays the media path through your infra; required
            // for restrictive corp / cell networks. Costs egress.
            {
                urls: [
                    "turn:turn.example.com:3478?transport=udp",
                    "turn:turn.example.com:3478?transport=tcp",
                    "turns:turn.example.com:5349?transport=tcp",
                ],
                username: "user",
                credential: "secret",
            },
        ],
    },
}

TURN URL format

turn://username:credential@host:port?transport={udp,tcp}
turns://username:credential@host:port?transport=tcp

turn: is unencrypted; turns: runs TURN over TLS (required for some mobile carrier networks). Always provide BOTH UDP and TCP transports — some networks block UDP/3478 entirely.

ICE-restart policy

WebRTCClient automatically restarts ICE up to maxIceRestartAttempts times (default 3) when the connection enters the failed state. After the cap is exhausted, ice:state reports failed once more without a further restart and the plugin evicts the peer. Tune the cap with the maxIceRestartAttempts option on WebRTCClient (the plugin forwards this through config).

Exhaustion symptoms

  • Many webrtc:failed events: peers cannot complete ICE — check TURN reachability, credentials, port allowlists.
  • Bandwidth blackholes: high bytesIn, zero bytesOut — direct path blocked, TURN relay forced; verify TURN provider quota.
  • signaling:reconnect storms: signalling URL or TLS misconfiguration — check SignalingClient.state + reconnect:exhausted events.

Topology Strategies

PeerManager consumes a TopologyStrategy. Three are bundled:

  • FullMeshStrategy (default): every peer connects to every other peer. Best for small meshes (<30 peers) where every pair benefits from a direct edge. Memory + CPU scales O(n^2) with mesh size.
  • SuperPeerStrategy: nominate one or more high-bandwidth / high-uptime peers as super peers; other peers only connect to super peers + a small set of equal peers. Best for asymmetric capacity (server-grade super peer + browser leaves). Reduces edge count to O(n).
  • HubAndSpokeStrategy: every peer connects to one of a small configured hub set. Best for centralised topologies (e.g. server- assisted mesh). Lowest edge count, simplest debug surface, no peer- to-peer privacy.

Pick FullMeshStrategy by default. Move to SuperPeerStrategy when peer counts exceed ~20 and HubAndSpokeStrategy when the application is fundamentally hub-mediated.


Telemetry

Every long-lived component emits OpenTelemetry instrumentation. Names are stable across patch releases:

Metric Type Attributes Description
drive.webrtc.peers_connected_total counter - Peers that have reached ICE connected.
drive.webrtc.chunks_served_total counter outcome Local chunk-serve outcomes (ok/not-found/...).
drive.webrtc.peer_rtt_ms histogram peer_id Round-trip latency per peer (ms).
drive.webrtc.signaling_reconnect_ms histogram attempt,outcome Reconnect wall-time.
drive.webrtc.signaling_messages_total counter direction,outcome Signalling messages by direction (in/out) + outcome.

Every collaborator additionally emits its own span (signaling.connect, plugin.start, webrtc.ice.restart, ...). Wire your OTel exporter at process boot — the package does nothing else to initialise the API.


Architecture (text diagram)

                          +---------------------------+
                          |   WebRTCDrivePlugin       |
                          |  (start / stop / stats)   |
                          +-----+----------+----------+
                                |          |
                  +-------------+          +--------------+
                  v                                       v
        +-------------------+                  +----------------------+
        |  SignalingClient  |   wss frames    |     WebRTCClient     |
        |  (WebSocket)      | <--------------> | (RTCPeerConnection)  |
        +---------+---------+                  +----------+-----------+
                  |                                       |
                  | peer-joined / peer-left               | datachannel
                  v                                       v
        +-------------------+                  +----------------------+
        |   PeerManager     |  selects peers  | DataChannelManager   |
        |   + topology      | <--------------> | + backpressure       |
        +---------+---------+                  +----------+-----------+
                  |                                       |
                  | peer rank, bandwidth                  | sub-chunk
                  v                                       v
        +-------------------+                  +----------------------+
        | PeerBandwidth     |                  | SubChunkReassembler  |
        | Tracker           |                  |                      |
        +-------------------+                  +----------+-----------+
                                                          |
                                                          v
                                              +----------------------+
                                              | ChunkDiscoveryMgr    |
                                              | (ranks, dedups, ver) |
                                              +----------+-----------+
                                                         |
                                                         v
                                              +----------------------+
                                              | ChunkIntegrityMgr    |
                                              | (SHA-256, Ed25519)   |
                                              +----------+-----------+
                                                         |
                                                         v
                                              +----------------------+
                                              | WebRTCChunkSource    |
                                              | (Drive surface)      |
                                              +----------------------+

Leaf utilities (LRUCache, RateLimiter, security-validator, sub-chunk-splitter) are referenced from the components above but have no upward dependency — they remain individually importable.


Configuration Schemas

All wire-format and configuration shapes are validated with Zod under ./schemas. The discriminator-bearing SignalingMessageSchema is the single source of truth for every signalling frame; WebRTCPluginConfigSchema validates plugin construction options. Direct schema access is available via:

import {
    SignalingMessageSchema,
    WebRTCPluginConfigSchema,
} from "@suaveplan/drive-webrtc/schemas";

Error Codes

Every boundary throws DriveWebRtcError carrying one of the codes from DRIVE_WEBRTC_CODES:

  • SIGNALING_UNREACHABLE
  • PEER_HANDSHAKE_FAILED
  • CHUNK_INTEGRITY_FAILED
  • ICE_FAILED
  • PEER_BANDWIDTH_EXCEEDED
  • RATE_LIMIT_EXCEEDED
  • SIGNALING_MESSAGE_INVALID
  • CONFIG_INVALID
  • PEER_NOT_FOUND
  • DATA_CHANNEL_CLOSED
  • SUB_CHUNK_TIMEOUT
  • SUB_CHUNK_MISSING
  • CHUNK_NOT_AVAILABLE

Import them directly from the leaf module:

import {
    DRIVE_WEBRTC_CODES,
    DriveWebRtcError,
} from "@suaveplan/drive-webrtc/errors";

Drive plugin contract deviation

The Drive package exposes the DrivePlugin<THandle> interface with an install(drive) shape. The new WebRTCDrivePlugin orchestrator surface specified by the migration uses start() / stop() / stats() instead — these two contracts are not directly compatible. The orchestrator class therefore exposes its own lifecycle and a name: "drive-webrtc" literal; consumers wire it to Drive with a thin adapter:

const adapter = {
    name: "drive-webrtc",
    async install() {
        await plugin.start();
        return { chunkSource: plugin.chunkSource };
    },
    async dispose() {
        await plugin.stop();
    },
};
await drive.usePlugin(adapter);

This deviation is intentional and documented in webrtc-plugin.md.


License

MIT.