Files
2026-05-27 23:29:53 +01:00

8.7 KiB
Raw Permalink Blame History

@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 the PeerRegistry for 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-chunks signature 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 upstream NetworkBus consumers 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 serverFallback is configured
  • THEN requestChunk MUST reject with DriveMeshError(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): () => void
  • peers(): 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 GET to ${baseUrl}/${fileId}/${chunkId} via an injectable fetchFn (defaults to globalThis.fetch).
  • Run every fetched chunk through verifyChunkSig(serverPubKey, …) from @suaveplan/drive-plugin-chunks before 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, and backoffMaxMs.
  • Thread an AbortSignal through every retry.
  • Surface every failure as a DriveMeshError(FALLBACK_FAILED) with the underlying HTTP status / fetch error preserved in cause.

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 fails verifyChunkSig(serverPubKey, …)
  • THEN requestChunk MUST reject with DriveMeshError(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 fetchFn to ServerFallback (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.