Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,163 @@
# @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 to `globalThis.WebSocket`) for testability.
- Zod-validated inbound messages via `SignalingMessageSchema`; malformed
payloads MUST emit `"error"` (not close the socket) with
`DriveWebRtcError(SIGNALING_MESSAGE_INVALID)`.
- Reconnect with configurable backoff array + `maxReconnectAttempts`; emits
`"reconnect:attempt"`, `"reconnect:success"`, `"reconnect:exhausted"` events.
- `send` while not open MUST throw `DriveWebRtcError(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 via `WebRTCPluginConfigSchema`; on
failure throws `DriveWebRtcError(CONFIG_INVALID, { cause: zodError })`.
- `WebRTCClient` lazy-creates one `RTCPeerConnection` per `remotePeerId` and
caches in an internal map.
- On `iceConnectionState === "failed"`, automatically calls `pc.restartIce()`
up to `maxIceRestartAttempts` (default 3) and emits `"ice:restart"` with
`{ peerId, attempt, timestamp }`.
- After exhaustion, no further restart attempts fire; caller decides to evict.
- `createOffer` / `handleOffer` / `handleAnswer` / `addIceCandidate` translate
DOMExceptions into `DriveWebRtcError(PEER_HANDSHAKE_FAILED | ICE_FAILED)`
with original error in `cause`.
### 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** `verifySignature` is 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.addPart` returns a discriminated union (`incomplete` /
`complete` / `duplicate`).
- `assertComplete(chunkId)` throws `DriveWebRtcError(SUB_CHUNK_MISSING)` when
any index is absent.
- `cleanup()` purges expired in-progress assemblies past `timeoutMs`;
`assertComplete` then throws `SUB_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 `RTCDataChannel`s 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:
1. De-duplicate concurrent requests for the same `chunkId`.
2. Rank candidate peers by `PeerBandwidthTracker` score.
3. Iterate candidates in order, sending `request-chunk` and awaiting reassembly
within `requestTimeoutMs`.
4. Verify via `ChunkIntegrityManager.verify(chunk, publicKey?)`.
5. On any candidate failure (timeout / disconnect / integrity), emit
`"request:fail"` and move to next candidate.
6. After all candidates exhausted, emit `"request:exhausted"` and throw
`DriveWebRtcError(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.