momo originally assumed that blob bytes and blob metadata lived on the same machine, in the same local directory. That assumption was fine until the project grew an S3-compatible gateway and remote replication. At that point the placement of bytes had to diverge from the node holding the metadata — and we did not want that divergence to require rewriting the storage core.
The storage layer therefore exposes one interface and a single configuration switch. A backend is simply the thing that stores and retrieves opaque blob bytes. A seam is a narrow, compile-time interface we can substitute without a plugin runtime: no shared library, no RPC hop, no serialization boundary.
The Store interface stays stable, and a [storage] backend config value
chooses the implementation:
| Backend | Notes |
|---|---|
local | filesystem path, default |
nfs | shared filesystem volume |
s3 | remote object store over SigV4, TLS-gated (see 011) |
raw | block-device direct I/O |
Local metadata stays per-node; only the blob bytes route through the chosen backend.
The seam, not the plugin
Backends are compiled in and selected by declarative config — the embryo of
the project’s seam-over-plugins principle. There is no external .so to load
and no RPC plugin protocol to negotiate. The blob interface is small and stable
(PutBlob/GetBlob/DeleteBlob/stat/path), and each backend implements it with
its own guarantees. That keeps the trust boundary explicit and the binary
self-contained.
The trap this avoids
A single hard-coded local blob store was the original design. Once remote replication arrived, “metadata here, bytes there” had to become a configuration change rather than a rewrite. Pluggable backends are what made that possible: the CAS core keeps naming blobs by hash and recording metadata locally, while the bytes can live on NFS, in an S3 bucket, or on a raw block device.
⚡ Bolt lens
- The
GetBlobPathguard rejects non-local backends, because “a filesystem path” is a concept only the local backend can honor. - Combined metadata reads collapse three separate key/value views into one, cutting write-path transactions and CPU (perf work referenced in 024).
- The encryption layer later wrapped the store so end-to-end encryption could sit under the backend seam transparently (013) — a seam earning its keep.
See docs/CORE/STANDARDS.md for the ⚡ Bolt / 🛡 Sentinel mindsets.
References / Dig deeper
- Configuration reference (the
[storage]section): docs/GUIDES/CONFIGURATION.md. - Spec:
openspec/changes/add-pluggable-storage; tracking issue #820. - Related: 004 → this post → 008, 011, 013.