# OpenSpec Change: drive-mesh-protocol-v1 - **Change ID**: drive-mesh-protocol-v1 - **Package**: — (cross-cutting wire-protocol spec consumed by `@suaveplan/drive-mesh` and `@suaveplan/network-plugin-content-delivery`) - **Capability**: `drive-mesh-protocol` (new) - **Status**: PROPOSED - **Date**: 2026-05-02 --- ## Summary Define the canonical wire protocol for chunk discovery, request, and delivery across the mesh. Three envelope types — `cd:advertise`, `cd:request`, `cd:response` — with three request forms (`whole` / `specific` / `bitset`), bitpacker-compressed presence bitsets, response-side presence piggyback, and a probabilistic trust model. The protocol is a contract, not an implementation: it lives in its own capability spec so that `@suaveplan/drive-mesh` (the requester side) and `@suaveplan/network-plugin-content-delivery` (the forwarder/redundancy side) can be developed and tested against the same source of truth. --- ## Why The current docs ([`drive-mesh-port`](../drive-mesh-port/proposal.md), [`network-plugin-content-delivery`](../network-plugin-content-delivery/proposal.md)) describe `cd:request` per chunk. This produces: 1. **Wire amplification.** A 1,000-chunk file with 800 missing chunks generates 800 request envelopes — ~16 KiB of pure framing overhead per file before any bytes flow. With a 50-peer mesh gossiping 200 active files, the control plane saturates before the data plane gets a turn. 2. **No standardized advertisement encoding.** `MESH-GOSSIP-01` says "gzipped bitset" without pinning a schema, codec, or framing. The 10 bitpacker packages already shipped (`@suaveplan/bitpacker`, `bitpacker-compressed`, `bitpacker-network`, `bitpacker-schema`, `bitpacker-streaming`, `bitpacker-strategies`, `bitpacker-delta`, `bitpacker-game`, `bitpacker-validation`, `bitpacker-crypto`) cover this exactly — but without a binding contract every implementer would pick a different one. 3. **No trust model beyond bad-sig.** A peer can advertise chunks it doesn't have indefinitely without consequence (until we ask, get nothing, and waste the round-trip). The existing `MESH-SCORE-01` only triggers on bad signatures. 4. **No response piggyback.** Every request that returns chunks is a missed opportunity to learn the holder's full inventory for that file. With piggyback, an active partner avoids a separate advertisement cycle. 5. **Empty-array semantics ambiguous.** "Empty `chunkIds` array" could mean "request nothing" or "request everything." Current docs leave the reader guessing. The architecture this proposal codifies is documented in [`docs/drive/mesh-protocol.md`](../../../docs/drive/mesh-protocol.md). This change folder is the formal OpenSpec mandate for that design. --- ## What changes ### ADDED — `drive-mesh-protocol` capability spec 22 ADDED requirements under `specs/drive-mesh-protocol/spec.md`: - **`DMP-01..05`** — `ChunkRequest` discriminated union, three forms, encoding selection threshold (>64 chunks → bitset). - **`DMP-06..09`** — `ChunkRequestEnvelope` priority tier, `maxChunks` cap, presence-bitset length invariant, generation counter monotonicity. - **`DMP-10..12`** — Advertisement throttling, response-side presence piggyback, chunkId ordering. - **`DMP-13..17`** — Verification (Ed25519 + AEAD + BLAKE3), trust-decay model, per-file scope, sample-verification rate. - **`DMP-18..19`** — Error envelope shape and codes. - **`DMP-20..22`** — Codec mandates (bitpacker-network framing, bitpacker-schema validation), empty-request rejection. ### MODIFIED — references in downstream proposals - `drive-mesh-port/proposal.md` — replace inline `cd:request`/`cd:advertise` descriptions with references to this capability. - `network-plugin-content-delivery/proposal.md` — same for the forwarder/redundancy side. ### REMOVED — none. This is purely additive at the protocol layer; no existing requirements are revoked. --- ## Source mapping > Genesis-original — no `enterprise/.md` reference exists for this protocol because it is being designed for the first time on the genesis branch. Antecedents: BitTorrent's bitfield message (BEP-3), IPFS Bitswap (want-list reconciliation), and Hypercore's wire protocol (have/want). | Requirement | Source / inspiration | Notes | |---|---|---| | Three request forms | Genesis-original; partial parallel to BEP-3 `bitfield` + `request` | We add `kind: 'whole'` to disambiguate the BitTorrent "I have nothing yet" case. | | Bitset encoding via `@suaveplan/bitpacker-compressed` | Genesis — uses an already-shipped foundation library | RLE + DEFLATE; ~12 bytes for an 800-bit map with long runs. | | Generation counter | Hypercore protocol; vector-clock-lite | Wraps mod 2^32; one-cycle anomaly tolerated. | | Response presence piggyback | Genesis-original | Saves a round-trip for active partners. | | Probabilistic trust decay | Genesis-original | Bad-sig still terminal; missed-advertisement is recoverable. | | Error codes | Standard pattern from `@suaveplan/error` | `defineCodes("DMP", [...])`. | ### Requirements with no source line | Requirement | Justification | |---|---| | 64-chunk threshold for bitset cutover | Empirically: 4 bytes/chunkId × 64 = 256 bytes; bitpacker-compressed of 64-bit map is typically <40 bytes; cutover in the 60-70 range is correct. | | 1024-entry generation LRU | Bounded memory while supporting 1k peer × 1k file fan-out. | | 5% miss rate threshold for trust demotion | Allows for clock skew between advertise and request without false positives; aggressive enough to neutralize a buggy peer within ~20 requests. | --- ## Parity Checklist > No upstream `suaveplan/main` analogue exists. Genesis-original protocol. | Capability | Upstream | Migrated | Notes | |---|---|---|---| | Bitset advertisements | n/a | ✅ | Genesis-original; `MESH-GOSSIP-01` ambiguous, this proposal pins it. | | Three request forms | n/a | ✅ | Whole / specific / bitset. | | Per-file trust scoping | n/a | ✅ | New on genesis. | | Response presence piggyback | n/a | ✅ | New on genesis. | | Generation counter | n/a | ✅ | New on genesis. | | Per-(peerId, fileId) trust score | partial (suaveplan had per-peer) | ⬆️ | Enhanced: per-file scope prevents reputation laundering. | --- ## Out of scope - **Implementation of `@suaveplan/drive-mesh` itself.** Handled by [`drive-mesh-port`](../drive-mesh-port/proposal.md) which depends on this protocol. - **Implementation of `@suaveplan/network-plugin-content-delivery`.** Handled by its own proposal which consumes this protocol. - **DHT-style chunk discovery.** Out of scope — this protocol assumes peers are already connected via the mesh; how they got there is a network-layer concern (`network-plugin-registry`, `network-discovery-*`). - **Encryption of envelopes.** Already covered by `network-plugin-protocol` (length-prefix → CryptoTransform → compressStream pipeline). - **Cross-mesh forwarding.** `network-plugin-relay` carries these envelopes across mesh boundaries; loop-prevention and TTL are its concern, not this protocol's. --- ## Verification approach - `tests/three-form-roundtrip.test.ts` — every `ChunkRequest` form roundtrips through encode/decode. - `tests/bitset-rle.test.ts` — fast-check property: `decode(encode(bitset)) === bitset` for random bit-densities. - `tests/generation-monotonic.test.ts` — out-of-order advertisements drop correctly. - `tests/piggyback-presence.test.ts` — receiving a `cd:response` updates peer-index without a separate `cd:advertise`. - `tests/trust-decay.test.ts` — repeatedly-advertising-but-not-delivering peer loses trust score predictably; bad sig terminates. - `tests/cap-respected.test.ts` — `maxChunks` is honored by responders; over-cap requests rejected with `DMP.MAX_CHUNKS_EXCEEDED`. - `tests/empty-specific-rejected.test.ts` — `kind: 'specific'` with `chunkIds: []` rejected as `DMP.EMPTY_REQUEST`. - `tests/bitset-length-invariant.test.ts` — advertisement with `presence.length` ≠ `totalChunks` rejected. - `tests/error-envelope-shape.test.ts` — every `ChunkErrorCode` round-trips. - `tests/codec-mandate.test.ts` — payloads not encoded via bitpacker-network framing fail validation. These belong in a new package `@suaveplan/drive-mesh-protocol` (types + codecs + validators only — zero runtime side effects), which both `drive-mesh` and `network-plugin-content-delivery` will consume. --- ## Impact ### Affected dependents Run `bunx turbo ls --filter=...@suaveplan/drive-mesh-protocol` once the package exists. - `@suaveplan/drive-mesh` — [ ] tests run / [ ] docs reviewed / [ ] no action needed (reason: depends on this protocol; will be implemented after this lands) - `@suaveplan/network-plugin-content-delivery` — [ ] tests run / [ ] docs reviewed / [ ] no action needed (reason: forwards these envelopes; will reference this spec) - `@suaveplan/network-plugin-relay` — [ ] tests run / [ ] docs reviewed / [ ] no action needed (reason: carries these envelopes across meshes via `CrossMeshEnvelope`) - `@suaveplan/drive-transport-webrtc` — [ ] tests run / [ ] docs reviewed / [ ] no action needed (reason: low-level transport; doesn't parse envelope semantics, but framing must round-trip) - `@suaveplan/drive-transport-https` — [ ] tests run / [ ] docs reviewed / [ ] no action needed (reason: server-side fallback; only relevant insofar as the same envelope shapes are accepted via HTTP) This is a **foundation-tier change** for the drive/network boundary: every package that exchanges chunks across the mesh references it. Per AGENTS.md §13 step 10, the dependent sweep is mandatory before archive. --- ## Migration ### From the previous design Authors of [`drive-mesh-port`](../drive-mesh-port/proposal.md) and [`network-plugin-content-delivery`](../network-plugin-content-delivery/proposal.md) MUST update their proposals to: 1. Replace any inline description of "cd:request per chunk" with a reference to `DMP-01..05`. 2. Replace any inline description of "gzipped bitset advertisement" with a reference to `DMP-08..09` and the `@suaveplan/bitpacker-compressed` codec mandate (`DMP-20`). 3. Replace any per-chunk reputation logic with the `(peerId, fileId)`-scoped model in `DMP-14..17`. Search command: ```bash grep -rln "cd:request\|cd:advertise\|cd:response" openspec/changes/ --include="*.md" ``` ### From `MESH-GOSSIP-01` (in `drive-mesh-port` spec) `MESH-GOSSIP-01` becomes a thin wrapper over `DMP-08..09`. Recommended replacement text: > The plugin SHALL emit `ChunkAdvertiseEnvelope` per `drive-mesh-protocol/spec.md` `DMP-08`. Receivers SHALL handle generation ordering per `DMP-09`. Encoding via `@suaveplan/bitpacker-compressed` per `DMP-20`. This keeps `MESH-GOSSIP-01` as the entry point while delegating wire-format details to the protocol spec. --- ## References - Architectural reference: [`docs/drive/mesh-protocol.md`](../../../docs/drive/mesh-protocol.md) - System review: [`docs/system-review.md`](../../../docs/system-review.md) §1, §2, §10, §11 - Bitpacker package map: [`docs/drive/mesh-protocol.md`](../../../docs/drive/mesh-protocol.md) §10 - Companion change: [`drive-template-v1`](../drive-template-v1/proposal.md)