# @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` — 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` — 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` - `advertise(peerId, fileId, bitset): Promise` - `onAdvertisement(handler): () => void` - `peers(): Iterable` - `dispose(): Promise` `@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.