# Drive Mesh and Sharing This document covers two of the most-asked-about pieces of the drive layer: 1. How **n drives** find each other and replicate chunks (`drive-mesh`). 2. 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: 1. **Advertise** which chunks of which fileIds this drive holds (gzip-compressed bitset). 2. **Discover** which peers hold the chunks this drive needs (read advertisements off the bus). 3. **Schedule** a fetch plan: which chunks to pull from which peers, with parallel races and per-chunk timeouts. 4. **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 consumes `ChunkSource` from 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 `-Infinity` permanently. - `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) ```text 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 `fileKey`s, each of which is wrapped with the partition's KEK. ```text 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*: 1. `revoke(partition, bob)` removes bob from the partition's authorized list. 2. The next `write()` to that partition rotates `KEK_wedding` to `KEK_wedding'`. 3. New `fileKey`s are wrapped with `KEK_wedding'`. Old `fileKey`s remain wrapped with `KEK_wedding`. 4. Files written before revocation are *re-keyed* lazily: on first read after revocation, the drive rewraps the `fileKey` with 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: - `` — full UI for granting/revoking/tokenizing. - `` — show which partition a file belongs to. - `` — 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](../../openspec/changes/drive-network-integration/proposal.md)) 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`](./plugins.md) — the full plugin chain. - [`encryption.md`](./encryption.md) — the chunks plugin in detail. - [`../network/plugins-catalog.md`](../network/plugins-catalog.md) — `network-plugin-content-delivery` and `network-plugin-relay`. - [`../network/topology-and-failover.md`](../network/topology-and-failover.md) — how the mesh tolerates partitions and server outages.