7.3 KiB
@suaveplan/drive-webrtc — WebRTC P2P Mesh Capability
Browser-tier WebRTC peer-to-peer mesh plugin for @suaveplan/drive: signalling
client, RTC client (offer/answer/ICE/reconnect), peer manager with bandwidth
tracking + topology strategies, data-channel pool, chunk discovery + integrity
verification, sub-chunk splitter/reassembler, LRU cache, rate limiter, security
validators, and a WebRTCDrivePlugin orchestrator that wires them all together.
Requirements
Requirement: Signalling client surface
The package SHALL expose SignalingClient over a WebSocket transport:
- Injectable
webSocketCtor(defaults toglobalThis.WebSocket) for testability. - Zod-validated inbound messages via
SignalingMessageSchema; malformed payloads MUST emit"error"(not close the socket) withDriveWebRtcError(SIGNALING_MESSAGE_INVALID). - Reconnect with configurable backoff array +
maxReconnectAttempts; emits"reconnect:attempt","reconnect:success","reconnect:exhausted"events. sendwhile not open MUST throwDriveWebRtcError(SIGNALING_UNREACHABLE).- AbortSignal threaded through
connect.
Scenario: Pre-aborted connect rejects
- WHEN
client.connect(signal)is called with an already-aborted signal - THEN the returned promise MUST reject with
DriveWebRtcError.
Requirement: WebRTC client + ICE-restart policy
The package SHALL expose WebRTCClient and resolveConfig:
resolveConfig(input)Zod-validates viaWebRTCPluginConfigSchema; on failure throwsDriveWebRtcError(CONFIG_INVALID, { cause: zodError }).WebRTCClientlazy-creates oneRTCPeerConnectionperremotePeerIdand caches in an internal map.- On
iceConnectionState === "failed", automatically callspc.restartIce()up tomaxIceRestartAttempts(default 3) and emits"ice:restart"with{ peerId, attempt, timestamp }. - After exhaustion, no further restart attempts fire; caller decides to evict.
createOffer/handleOffer/handleAnswer/addIceCandidatetranslate DOMExceptions intoDriveWebRtcError(PEER_HANDSHAKE_FAILED | ICE_FAILED)with original error incause.
Requirement: Real signature verification
ChunkIntegrityManager.verifySignature MUST perform real Ed25519 verification
via @suaveplan/crypto-curve ed25519.verify(signature, message, publicKey)
over canonical hash || utf8(chunkId). NO placeholder, NO "forward-looking"
wording allowed in implementation or docs.
Scenario: Tampered signature rejected
- WHEN
verifySignatureis called with a signature whose any byte has been flipped - THEN it MUST return
{ ok: false, reason: "bad-signature", code: "CHUNK_INTEGRITY_FAILED" }.
Requirement: Hash verification
computeChunkHash MUST compute SHA-256 over the chunk bytes; verifyChunkIntegrity
MUST recompute the hash and compare constant-time via constantTimeEqual from
@suaveplan/crypto-utils.
Requirement: Sub-chunk splitter + reassembler
The package SHALL expose splitIntoSubChunks, sendSubChunks, and
SubChunkReassembler:
- Default
maxBytes = 16_000(DataChannel safe payload). - Returns parts tagged
{ chunkId, partId, index, total, bytes }. sendSubChunks(parts, callback, { signal?, delayMs? })is transport-agnostic, AbortSignal-aware on both pre-iteration and inter-part delays.SubChunkReassembler.addPartreturns a discriminated union (incomplete/complete/duplicate).assertComplete(chunkId)throwsDriveWebRtcError(SUB_CHUNK_MISSING)when any index is absent.cleanup()purges expired in-progress assemblies pasttimeoutMs;assertCompletethen throwsSUB_CHUNK_TIMEOUT.
Requirement: Peer manager + topology strategies
The package SHALL expose PeerManager, PeerBandwidthTracker, and three
TopologyStrategy implementations: FullMeshStrategy, SuperPeerStrategy,
HubAndSpokeStrategy. Each strategy implements selectConnections and
admitOrEvict for capacity-bound peer admission.
PeerBandwidthTracker MUST compose score = 0.5*(1-bytesInLoad) + 0.3*(1-requestsInLoad) + 0.2*rttFactor and expose a snapshot for ranking. Score
MUST default to 1.0 when no samples have been recorded.
Requirement: Data channel manager
DataChannelManager SHALL pool RTCDataChannels per (peerId, label),
back-pressure via bufferedAmount/bufferedamountlow, and route messages by
label. send MUST throw DriveWebRtcError(DATA_CHANNEL_CLOSED) when the
channel is not open. open(peerId) on an unknown peer throws
DriveWebRtcError(PEER_NOT_FOUND).
Requirement: Chunk discovery with peer fallback
ChunkDiscoveryManager.request(chunkId) SHALL:
- De-duplicate concurrent requests for the same
chunkId. - Rank candidate peers by
PeerBandwidthTrackerscore. - Iterate candidates in order, sending
request-chunkand awaiting reassembly withinrequestTimeoutMs. - Verify via
ChunkIntegrityManager.verify(chunk, publicKey?). - On any candidate failure (timeout / disconnect / integrity), emit
"request:fail"and move to next candidate. - After all candidates exhausted, emit
"request:exhausted"and throwDriveWebRtcError(CHUNK_NOT_AVAILABLE).
Requirement: WebRTCDrivePlugin orchestration
The package SHALL expose WebRTCDrivePlugin with name: "drive-webrtc" and
lifecycle methods start(signal?) / stop(reason?) / stats(). The plugin
MUST wire SignalingClient, WebRTCClient, PeerManager, DataChannelManager,
ChunkIntegrityManager, ChunkDiscoveryManager, and WebRTCChunkSource
internally and forward their events through a unified WebRTCEventMap.
Scenario: stop is idempotent
- WHEN
plugin.stop()is invoked twice in succession - THEN the second invocation MUST NOT throw and MUST NOT re-close already- closed transports.
Requirement: Subpath exports + tree-shaking
package.json SHALL expose 11 subpath exports covering ./client/webrtc-client,
./chunk-source/chunk-source, ./plugin/webrtc-plugin,
./signaling/signaling-client, ./peer-manager/peer-manager,
./data-channel/data-channel-manager, ./rate-limiter/rate-limiter,
./lru-cache/lru-cache, ./integrity/chunk-integrity,
./security/security-validator, ./sub-chunk/sub-chunk-splitter. Leaf
subpaths MUST NOT drag in unrelated subsystems (verified via per-subpath bundle-
size snapshot tests).
Requirement: Universal-tier-clean tests
Tests MUST NOT use vi.stubGlobal, vi.mock, or any runtime monkey-patch.
WebSocket/RTCPeerConnection paths are exercised via injected structural
real implementations (FakeWebSocket, FakeRTCPeerConnection,
FakeRTCDataChannel) using the webSocketCtor / rtcPeerConnectionCtor
options. All test imports come from @suaveplan/testing/runner.
Requirement: OTel observability
The package SHALL register the following instruments under tracer
@suaveplan/drive-webrtc:
- Counter
drive.webrtc.peers_connected_total - Counter
drive.webrtc.chunks_served_total{outcome} - Counter
drive.webrtc.chunks_rejected_total{reason} - Counter
drive.webrtc.signaling_messages_total{direction,outcome} - Histogram
drive.webrtc.peer_rtt_ms - Histogram
drive.webrtc.chunk_transfer_ms - Histogram
drive.webrtc.signaling_reconnect_ms
Requirement: Coverage + gate
bunx turbo lint typecheck test:coverage --filter=@suaveplan/drive-webrtc MUST
be green and istanbul coverage MUST be 100% on every touched module before any
release.