11 KiB
OpenSpec Change: drive-mesh-protocol-v1
- Change ID: drive-mesh-protocol-v1
- Package: — (cross-cutting wire-protocol spec consumed by
@suaveplan/drive-meshand@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, network-plugin-content-delivery) describe cd:request per chunk. This produces:
- 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.
- No standardized advertisement encoding.
MESH-GOSSIP-01says "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. - 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-01only triggers on bad signatures. - 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.
- Empty-array semantics ambiguous. "Empty
chunkIdsarray" 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. 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—ChunkRequestdiscriminated union, three forms, encoding selection threshold (>64 chunks → bitset).DMP-06..09—ChunkRequestEnvelopepriority tier,maxChunkscap, 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 inlinecd:request/cd:advertisedescriptions 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/<pkg>.mdreference 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/mainanalogue 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-meshitself. Handled bydrive-mesh-portwhich 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-relaycarries these envelopes across mesh boundaries; loop-prevention and TTL are its concern, not this protocol's.
Verification approach
tests/three-form-roundtrip.test.ts— everyChunkRequestform roundtrips through encode/decode.tests/bitset-rle.test.ts— fast-check property:decode(encode(bitset)) === bitsetfor random bit-densities.tests/generation-monotonic.test.ts— out-of-order advertisements drop correctly.tests/piggyback-presence.test.ts— receiving acd:responseupdates peer-index without a separatecd:advertise.tests/trust-decay.test.ts— repeatedly-advertising-but-not-delivering peer loses trust score predictably; bad sig terminates.tests/cap-respected.test.ts—maxChunksis honored by responders; over-cap requests rejected withDMP.MAX_CHUNKS_EXCEEDED.tests/empty-specific-rejected.test.ts—kind: 'specific'withchunkIds: []rejected asDMP.EMPTY_REQUEST.tests/bitset-length-invariant.test.ts— advertisement withpresence.length≠totalChunksrejected.tests/error-envelope-shape.test.ts— everyChunkErrorCoderound-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 viaCrossMeshEnvelope)@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 and network-plugin-content-delivery MUST update their proposals to:
- Replace any inline description of "cd:request per chunk" with a reference to
DMP-01..05. - Replace any inline description of "gzipped bitset advertisement" with a reference to
DMP-08..09and the@suaveplan/bitpacker-compressedcodec mandate (DMP-20). - Replace any per-chunk reputation logic with the
(peerId, fileId)-scoped model inDMP-14..17.
Search command:
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
ChunkAdvertiseEnvelopeperdrive-mesh-protocol/spec.mdDMP-08. Receivers SHALL handle generation ordering perDMP-09. Encoding via@suaveplan/bitpacker-compressedperDMP-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 - System review:
docs/system-review.md§1, §2, §10, §11 - Bitpacker package map:
docs/drive/mesh-protocol.md§10 - Companion change:
drive-template-v1