8.7 KiB
@suaveplan/drive-mesh — P2P Mesh Orchestration Capability
P2P mesh layer over @suaveplan/drive-plugin-chunks: peer registry, bitset
gossip, parallel chunk pull (multi-peer racing window), peer scoring, server
fallback (built-in ServerFallback HTTPS path — see "HTTPS chunk fetch via
ServerFallback" below), and bad-signature denylist. Consumes a pluggable list
of peer-to-peer DriveTransport implementations (currently WebRTC, via
@suaveplan/drive-transport-webrtc) without importing @suaveplan/drive
core directly.
Requirements
Requirement: Mesh orchestrator surface
The package SHALL expose a DriveMesh class with:
start(signal?)— begin gossip + listen for chunk requests.stop(reason?)— drain in-flight, close transports. Idempotent.requestChunk(fileId, chunkId, signal?): Promise<Uint8Array>— fetch a chunk from the mesh, falling back to the server if configured.advertise(fileId, bitset)/unadvertise(fileId)— push our local availability through the gossip engine.peers(): Iterable<PeerInfo>— snapshot of the peer registry.registry— direct handle on thePeerRegistryfor application glue.tickGossip()— manual tick (testable / configurable cadence).- TypedEventEmitter for
chunk:*/peer:*/gossip:*events.
Requirement: Server is root of trust
DriveMesh SHALL treat the configured serverPubKey as the only authority
for chunk validity:
- Every chunk delivered by a peer MUST be passed through
drive-plugin-chunkssignature verification before being returned to the caller. - A peer that delivers a chunk with an invalid signature MUST be added to the registry's denylist (per-session by default, persistent if configured).
- A bad-sig peer MUST emit
peer:denied{peerId, reason: "bad-signature"}so upstreamNetworkBusconsumers can take action.
Scenario: Bad-sig peer evicted
- WHEN a peer delivers bytes whose signature fails
verifyChunkSig(serverPubKey, …) - THEN
peerRegistry.recordBadSig(peerId)MUST be called and the peer MUST NOT be selected for subsequent chunk pulls.
Requirement: PeerRegistry overlay
PeerRegistry SHALL track per-peer state including availability bitsets per
fileId, EMA latency (α=0.3), success / failure / bad-sig counts, and a
composite score for rank(fileId, chunkId). The registry is an overlay
that consumes peer-identity events from network-plugin-registry (per the
drive-network-integration boundary spec); it MUST NOT duplicate peer-
identity storage.
Requirement: Gossip engine
GossipEngine SHALL emit periodic outbound advertisements with compressed
bitsets per fileId (using @suaveplan/drive-mesh-protocol framed-bitset
helpers) and SHALL ingest inbound advertisements into the PeerRegistry.
Requirement: Parallel puller with racing window
ParallelPuller SHALL issue chunk requests to the top-K candidates (default
3) within a configurable racing window (default 250 ms), accept the first
verified response, and abort the losers via a shared AbortController.
maxParallelRequests SHALL cap global concurrency (default 8). Failed or
timed-out attempts blacklist that peer for the specific chunkId only;
bad-signature is the only failure that escalates to a registry-wide denylist.
Requirement: Server fallback
ServerFallback SHALL accept a { url, transport } config and execute
equal-jitter exponential backoff retries when all peer attempts fail.
Server-delivered chunks MUST still pass signature verification.
Scenario: All-peers-fail with no fallback
- WHEN every peer attempt fails AND no
serverFallbackis configured - THEN
requestChunkMUST reject withDriveMeshError(ALL_PEERS_FAILED).
Requirement: Wire transports as plugins
DriveMesh SHALL accept a list of DriveTransport implementations through
its constructor and use them interchangeably. The structural DriveTransport
interface MUST expose:
requestChunk(peerId, fileId, chunkId, signal?): AsyncIterable<Uint8Array>advertise(peerId, fileId, bitset): Promise<void>onAdvertisement(handler): () => voidpeers(): Iterable<PeerInfo>dispose(): Promise<void>
@suaveplan/drive-transport-webrtc satisfies this interface. The HTTPS
chunk-fetch path is NOT a DriveTransport plugin — see "HTTPS chunk fetch
via ServerFallback" below.
Requirement: No network-plugin-registry import
DriveMesh MUST NOT import @suaveplan/network-plugin-registry directly.
Applications glue peer:joined / peer:left from NetworkBus to
mesh.registry.addOrUpdate / mesh.registry.forget. This enforces the
layering decision recorded in openspec/specs/drive-network-integration/spec.md.
Requirement: Error registry
The package SHALL export DriveMeshError extends SuaveplanError<…> with 10
stable codes covering chunk unavailable, all-peers-fail, signature failure,
transport failure, fallback failure, invalid bitset, peer-denied paths.
Requirement: AbortSignal threading
Every async surface (start, stop, requestChunk, advertise) SHALL
accept an optional AbortSignal. Pre-aborted signals reject before any
side effect. Mid-flight aborts cancel transport requests and racing
attempts cleanly.
Requirement: Universal-tier-clean tests
Tests MUST NOT use vi.mock or vi.stubGlobal. The package SHALL ship
real-implementation fixtures (in-memory Broker + FakeDriveTransport,
real ed25519 keypairs via @suaveplan/crypto-curve, real
buildSignedChunk from @suaveplan/drive-plugin-chunks, real
encodeFramedBitset from @suaveplan/drive-mesh-protocol). All test
imports come from @suaveplan/testing/runner.
Requirement: Coverage + gate
bunx turbo lint typecheck test:coverage --filter=@suaveplan/drive-mesh
MUST be green and istanbul coverage MUST be 100% on every touched module
(statements / branches / functions / lines) before any release.
ADDED Requirements
Requirement: HTTPS chunk fetch via ServerFallback
The HTTPS / server-fallback chunk-fetch path SHALL be served by the
built-in ServerFallback module that ships inside @suaveplan/drive-mesh
(./server-fallback/server-fallback) — NOT by a separate
DriveTransport-shaped plugin package.
ServerFallback SHALL:
- Issue HTTPS
GETto${baseUrl}/${fileId}/${chunkId}via an injectablefetchFn(defaults toglobalThis.fetch). - Run every fetched chunk through
verifyChunkSig(serverPubKey, …)from@suaveplan/drive-plugin-chunksbefore returning bytes to the scheduler — same root-of-trust contract as peer-served chunks. - Apply equal-jitter exponential backoff with a caller-configurable
maxRetries,backoffBaseMs, andbackoffMaxMs. - Thread an
AbortSignalthrough every retry. - Surface every failure as a
DriveMeshError(FALLBACK_FAILED)with the underlying HTTP status / fetch error preserved incause.
Bearer-token or other authentication SHALL be supplied by the consumer
through the fetchFn closure (e.g. wrapping globalThis.fetch with an
Authorization header) — no transport-level auth plumbing is added.
ServerFallback deliberately does NOT implement the
DriveTransport interface. Advertisement / peer-discovery / receive
semantics do not apply to a server-driven HTTPS fetch path; modelling
HTTPS as a degraded DriveTransport (with no-op advertise,
onAdvertisement, and a single synthetic peer) was rejected because it
adds no consumer-facing capability beyond what ServerFallback already
provides while doubling the surface the scheduler has to reason about.
Scenario: Bad-sig server chunk rejected
- WHEN
ServerFallback.fetchChunk(fileId, chunkId)returns bytes whose signature failsverifyChunkSig(serverPubKey, …) - THEN
requestChunkMUST reject withDriveMeshError(FALLBACK_FAILED)and the chunk MUST NOT be cached.
Scenario: Auth wrapping happens in the consumer's fetchFn
- WHEN a consumer needs bearer-token authentication against the fallback endpoint
- THEN the consumer SHALL pass an authenticated
fetchFntoServerFallback(e.g.(url, init) => fetch(url, { ...init, headers: { ...init?.headers, Authorization: 'Bearer ...' } })) rather than registering a separate transport.
Requirement: No HTTPS DriveTransport plugin
There SHALL NOT be a parallel @suaveplan/drive-transport-https (or
similar) package implementing the DriveTransport interface for the
HTTPS path. The original drive-transport-https-port proposal was
retired on 2026-05-12 because ServerFallback already provides every
documented capability (HTTPS chunk fetch, signature verification,
exponential backoff with jitter, injectable fetchFn for tests,
AbortSignal threading) without inheriting the peer-shaped surface
(advertise / onAdvertisement / peers) that would have to be
no-ops on a unidirectional HTTPS link.