8.5 KiB
Drive Mesh and Sharing
This document covers two of the most-asked-about pieces of the drive layer:
- How n drives find each other and replicate chunks (
drive-mesh). - How files become shareable between specific drives (
drive-sharing).
Both are plugins that compose with the chunks plugin and the network layer.
drive-mesh — per-file scheduling
drive-mesh makes a drive participate in a chunk-sharing mesh. It does not own peer identity or per-peer health — those live in network-plugin-registry. Mesh's job is, for one specific file:
- Advertise which chunks of which fileIds this drive holds (gzip-compressed bitset).
- Discover which peers hold the chunks this drive needs (read advertisements off the bus).
- Schedule a fetch plan: which chunks to pull from which peers, with parallel races and per-chunk timeouts.
- Score peers per file: a peer that gave you a bad signature gets
-Infinity; a peer that consistently delivers fast bumps up.
Spec excerpts:
MESH-DEPS-01: mesh consumesChunkSourcefrom the chunks plugin; it does not import drive core.MESH-SCHED-01..03: parallel-pull window, per-chunk timeout, server fallback.MESH-SCORE-01: bad sig → score-Infinitypermanently.MESH-GOSSIP-01: bitset advertisements over the network bus.MESH-FETCH-FILE-01: end-to-end happy path — mesh resolves, requests, verifies, hands plaintext up the chain.
The mesh fetch flow (one file)
drive-mesh.fetchFile(fileId)
│
├── 1. fetch manifest (peer-first, server-fallback)
├── 2. local check: which chunks do we already have?
├── 3. for each missing chunk:
│ ├── pick top-N candidates from per-peer score
│ ├── race them; first verifying response wins
│ ├── on bad sig: blacklist that peer, retry next candidate
│ ├── on timeout: try the next candidate
│ └── on full miss: fall through to drive-transport-https (server)
├── 4. verify each chunk's Ed25519 sig + AEAD tag
├── 5. reassemble; verify whole-file BLAKE3
└── 6. emit `cd:advertise` so other peers know we now have these chunks
drive-mesh does NOT do
- Peer discovery. That's the network discovery plugins (
network-plugin-registry,network-discovery-*). - Mesh-wide redundancy (ensuring N copies of every chunk exist). That's
network-plugin-content-delivery. - Cross-mesh relay. That's
network-plugin-relay. - The actual transport. That's
drive-transport-webrtc/drive-transport-https.
Mesh sits in the middle: scheduler for one specific file's chunks, leaving identity, redundancy, and bytes-on-wire to the layers around it.
drive-sharing — partitions and access control
drive-sharing is the access-control layer. The unit of access is a partition.
Mental model
Think of a drive as a tree of partitions. The root partition belongs to the drive's owner. Sub-partitions can be created and granted to other drives. Each partition has its own key-encryption-key (KEK); files inside a partition are encrypted with per-file fileKeys, each of which is wrapped with the partition's KEK.
drive (alice@example.com)
└── root partition (KEK_root)
├── /personal/... (file keys wrapped by KEK_root)
└── shared partition "wedding" (KEK_wedding)
├── /wedding/photo1.jpg (file key wrapped by KEK_wedding)
└── /wedding/photo2.jpg
Granting bob@example.com access to "wedding" means: rewrap KEK_wedding with bob's public key (X25519 sealed-box) and hand bob the wrapped blob. Bob's drive unwraps with its private key, caches KEK_wedding, and can now unwrap any fileKey in that partition.
Public API (from @suaveplan/drive-sharing proposal)
| Component | Role |
|---|---|
PartitionManager |
Create/delete/list partitions. Each has an id and a KEK. |
KeyRing |
Holds the unwrapped KEKs the current drive has access to. |
WebCryptoProvider |
KEK wrap/unwrap. X25519 sealed-box plus AES-KW for symmetric rewrap. |
GrantManager |
grant(partition, peerPubKey) / revoke(partition, peerPubKey). |
TokenManager |
Time-limited share tokens (URL-safe) — give someone read access for 24h without exchanging keys. |
TransferManager |
Move a file between partitions: rewrap its fileKey from the source KEK to the destination KEK. |
What revocation actually does
Naive revocation is impossible after the fact: a peer who has already decrypted a chunk will always have the plaintext. Revocation in this design is forward:
revoke(partition, bob)removes bob from the partition's authorized list.- The next
write()to that partition rotatesKEK_weddingtoKEK_wedding'. - New
fileKeys are wrapped withKEK_wedding'. OldfileKeys remain wrapped withKEK_wedding. - Files written before revocation are re-keyed lazily: on first read after revocation, the drive rewraps the
fileKeywith the new KEK and re-uploads the wrap blob (not the data — just the small wrap).
Bob can read what he already cached. He cannot read anything new, including content uploaded after revocation. This is the standard envelope-encryption revocation pattern.
Tokens vs grants
- Grants are per-peer-identity (X25519 public key). Revocable. Use for stable relationships.
- Tokens are bearer (anyone with the token can use it for the validity window). Self-expiring. Use for one-off shares ("here's a link to the wedding photos for 24 hours").
TokenManager builds tokens server-side (or client-side with a server signature, depending on the deployment). The token contains the partition id, an expiry, and a wrapped KEK. The receiver's drive unwraps and uses it like any other KEK until expiry.
React UI
@suaveplan/drive-react-sharing ships:
<ShareDialog drive={drive} path={...} />— full UI for granting/revoking/tokenizing.<PartitionBadge>— show which partition a file belongs to.<FileAccessList>— render the current grant list.useSharing()— hook that exposes the imperative API.
It is a thin UI layer over @suaveplan/drive-sharing — no business logic of its own.
Composition — mesh + sharing + chunks
The four plugins (chunks, sharing, mesh, transport-*) compose cleanly because they each touch a different concern:
| Plugin | Concern | Where it intercepts |
|---|---|---|
chunks |
Confidentiality + integrity of bytes on disk and on wire | drive:read (verify+decrypt), drive:write (encrypt+sign) |
sharing |
Authorisation: which drives can read which partition | drive:read (unwrap fileKey), drive:write (wrap fileKey) |
mesh |
Where to fetch missing chunks | drive:read (when local store misses) |
transport-* |
Bytes on the wire to/from peers | Below mesh; serves chunks-by-id |
Chunks alone gives you encryption-at-rest. Chunks + sharing gives you encryption-at-rest with multi-drive access control. Chunks + sharing + mesh + transport gives you n drives that share files end-to-end-encrypted across a tiered network.
Cross-references with the network layer
The drive-network-integration change folder (proposal) is the canonical contract. Summary:
| Direction | Event | Producer | Consumer | Meaning |
|---|---|---|---|---|
| Out | cd:advertise |
drive-mesh |
network-plugin-content-delivery |
"I have these chunks of these fileIds" |
| Out | cd:request |
drive-mesh |
network-plugin-content-delivery |
"Anyone got chunk X of file Y?" |
| In | cd:response |
network-plugin-content-delivery |
drive-mesh |
"Here's the chunk" (verifies via chunks plugin) |
| In | peer:joined/peer:left/peer:updated |
network-plugin-registry |
drive-mesh |
Peer identity lifecycle |
drive-mesh knows nothing about which transport delivers a chunk. network-plugin-content-delivery knows nothing about which file a chunk belongs to. The bus carries content-addressed chunk requests; everything else is layer-local.
Reading further
plugins.md— the full plugin chain.encryption.md— the chunks plugin in detail.../network/plugins-catalog.md—network-plugin-content-deliveryandnetwork-plugin-relay.../network/topology-and-failover.md— how the mesh tolerates partitions and server outages.