# Drive Plugins The drive core does five things: lifecycle, path resolution, the two indexes, the middleware chain, and capability fallbacks. Everything else is a plugin. This document is the catalogue of plugins (shipped, planned, and conventional) and how they compose. ## The plugin contract ```ts interface DrivePlugin { readonly name: string; readonly hooks: Record; readonly priority?: number; // higher first } type PluginHook = (context: unknown, next: NextFunction) => Promise; ``` Plugins register hooks against operation names — `drive:read`, `drive:write`, `drive:delete`, `drive:stat`, `drive:list`, `drive:initialize`, `drive:dispose`. Hooks run in priority order (high → low) on the request path, then the same order in reverse on the response path. A hook can: - transform the args before delegating (`await next()`), - transform the result after delegating, - short-circuit by *not* calling `next()` and returning a value directly, - throw, propagating up the chain. `useMiddleware(mw)` is a thin sugar for a plugin whose only hook is the universal `drive:*` interceptor. ## Plugin catalogue | Plugin | Status | Responsibility | | --- | --- | --- | | `chunks` (`@suaveplan/drive-plugin-chunks`) | **shipped** | Splits files into signed, encrypted chunks. Owns the `manifest`. Owns layout selection (sparse-file vs chunk-keys). | | `sharing` (`@suaveplan/drive-sharing`) | pending — `main` | Partition-based access control. Envelope encryption. Grant/revoke/rotate. | | `react-sharing` (`@suaveplan/drive-react-sharing`) | pending — `main` | React UI for the sharing engine — ``, ``, `useSharing()`. | | `mesh` (`@suaveplan/drive-mesh`) | planned (genesis-original) | Per-file chunk scheduling: which peer to ask for which chunk. Bitset advertisement. Per-peer scoring. | | `transport-https` (`@suaveplan/drive-transport-https`) | planned (genesis-original) | HTTPS chunk transport — server fallback when WebRTC isn't available. `GET /v1/chunks//` with Range. | | `transport-webrtc` (`@suaveplan/drive-transport-webrtc`) | planned (genesis-original) | WebRTC datachannel chunk transport — browser↔browser. `[uint32 BE frameLen][uint8 frameType][payload]` framing. | | `webrtc` (`@suaveplan/drive-webrtc`) | pending — `main` | A pre-packaged "browser drive" plugin: chunks + transport-webrtc + signalling, in one. | | `cache` (`@suaveplan/cache`) | shipped | Read-through cache middleware. Drop in to cache decrypted plaintext. | | `cow` (planned) | planned | Copy-on-write versioning. Multiple `version` numbers per `fileId`. Snapshot create/restore/diff. | | `dedup` (planned) | planned | Content-addressed dedup. Hashes content, points multiple `fileLocId`s at the same chunks. | | `metadata` (planned, see `streams-metadata`) | planned | Side-channel metadata extraction (EXIF/MIME/audio) on every write. | ## How the chain runs end-to-end A typical browser drive: ```ts const drive = new Drive(opfsStorage) .usePlugin(chunksPlugin({ chunkSize: 65536, signing: { secretKey } })) .usePlugin(sharingPlugin({ partitions, kms })) .usePlugin(meshPlugin({ networkNode })) .usePlugin(transportWebrtcPlugin({ signaller })) .useMiddleware(loggingMiddleware); ``` Registration order matters: ```text drive.read("/photo.jpg") │ ┌────────────▼────────────┐ │ logging (pre) │ │ chunks (pre) │ load manifest │ sharing (pre) │ unwrap envelope key │ mesh (pre) │ prepare peer scoring │ transport (pre) │ open peer connections └────────────┬────────────┘ ▼ storage.get │ ┌────────────▼────────────┐ │ transport (post)│ fetch missing chunks via WebRTC │ mesh (post) │ reassemble │ sharing (post) │ decrypt envelope │ chunks (post) │ verify+decrypt+concat │ logging (post) │ log duration └────────────┬────────────┘ ▼ Uint8Array ``` Each layer is independent. Strip any one and the layers above keep working — `mesh` without `transport-webrtc` falls back to `transport-https`; `sharing` without `mesh` works on a single-node drive; `chunks` without `mesh` works with no network at all. ## Cross-layer ownership boundaries The genesis design is meticulous about which layer owns what. The `drive-network-integration` change folder ([proposal](../../openspec/changes/drive-network-integration/proposal.md)) is the canonical resolution document; the summary is: | Concern | Owner | Why | | --- | --- | --- | | Peer identity & lifecycle | `network-plugin-registry` | Peers exist independent of files; one registry per mesh. | | Per-file chunk-availability bitsets | `drive-mesh` | Specific to a file's manifest; doesn't belong in the generic registry. | | Per-file chunk scheduling (which peer to ask, in what order) | `drive-mesh` | Highly file-specific; uses peer health from registry but decides locally. | | Mesh-wide chunk redundancy (N copies, prefetch, integrity) | `network-plugin-content-delivery` | Cuts across files; lives at the network layer because it's about peers, not files. | | Cross-mesh forwarding for chunks | `network-plugin-relay` | Same as message relay; a chunk request is just another envelope. | The interaction is event-driven via `NetworkBus`: - `peer:joined` / `peer:left` / `peer:updated` from `network-plugin-registry`. - `cd:advertise` from `drive-mesh` (this drive has these chunks). - `cd:request` from `drive-mesh` (this drive needs this chunk). - `cd:response` from `network-plugin-content-delivery` (here it is). That keeps `drive-mesh` blissfully unaware of *which* transport is fetching the chunk; it just listens for `cd:response`. ## Conventions for writing a new plugin 1. **No drive imports.** Use `DrivePlugin`, `MiddlewareHandler`, and `MiddlewareContext` from `@suaveplan/types`. Never import `Drive` itself — that creates a circular dep. 2. **Idempotent install.** A plugin's install hook may run more than once if a drive is reinitialized. Track state on the plugin instance, not in module scope. 3. **Reverse dispose.** If your plugin allocates anything (sockets, workers, watchers), free it in `drive:dispose`. The drive guarantees this hook runs in reverse-registration order, so plugins added later get to clean up first. 4. **Surface errors as `DriveError`.** Wrap thrown errors as `DriveError(DRIVE.PLUGIN_FAILED, { cause })` so consumers see one error type at the boundary. 5. **Use `@suaveplan/types` for backend-agnostic capabilities.** If you need to know what the storage can do, read `storage.capabilities`. If you need to know what the network can do, read it through `NetworkBus` capability events. Don't hardcode. ## Why this is split, not a fat library A drive that runs in a browser without WebRTC just doesn't install the WebRTC plugin. A server drive in a single-node deployment doesn't install `mesh`. A test drive doesn't install any plugin at all and uses `MemoryStorage` directly. None of those configurations cost extra bytes or extra surface area, because the core knows nothing about chunks, peers, or partitions — it just runs whatever middleware you registered. This is the genesis bet: composability over completeness. The `enterprise-drive` monolith design tried the other way and ended up with a 2 MB package that imports half the platform on the import line. The split keeps every drive minimal. ## Reading further - [`encryption.md`](./encryption.md) — the chunks plugin in detail. - [`mesh-and-sharing.md`](./mesh-and-sharing.md) — the mesh and transport plugins. - [`../network/plugins-catalog.md`](../network/plugins-catalog.md) — the network plugins these drive plugins talk to.