Expand description
§ferro-blob-store
Foundation crate for content-addressed blob storage in the Ferro
ecosystem. A deliberately tiny async fn trait (5 methods) plus
two reference backends (in-memory + filesystem) plus a
Digest newtype with SHA-256 / SHA-512 support.
🟢 Stable (
v1.0.0). The public API is committed under strict semver: breaking changes require a major bump. The trait surface is minimal on purpose so streaming variants can be added additively in a future minor.
Part of the Ferro ecosystem. Used as the storage abstraction
under ferro-oci-server,
ferro-maven-layout,
and ferro-cargo-registry-server.
§What this crate does
Digest— an<algo>:<hex>content identifier (SHA-256 or SHA-512). Validates hex length and character set on construction. Computes from bytes viaDigest::sha256_of(&[u8]).BlobStore— five-method async trait:put,get,contains,delete,list. Writers verify the SHA-256 of the input matches the supplied digest. Implementations are expected to beSend + Sync.InMemoryBlobStore—Arc<RwLock<HashMap<Digest, Bytes>>>reference implementation. Useful for tests and ephemeral caches.FsBlobStore(default featurefs) — local-filesystem backend that lays out blobs at<root>/<algo>/<2-char-prefix>/<rest-of-hex>. Atomic writes via temp-file + rename.
§What this crate does not do
- Streaming (
put_stream/get_stream). Planned as an additive minor on thev1.xtrack. - Cloud backends (S3, GCS, Azure). Use the
object_storecrate family and write a 50-line adapter; the trait is small enough. - Tiered storage (Hot/Warm/Cold). The Ferro internal repo has a router for this; it is not in the public crate.
- Replication / dedupe / compression. Layer it under your own
BlobStoreimpl that wraps an inner one.
§Quick start
use ferro_blob_store::{BlobStore, Digest, InMemoryBlobStore};
use bytes::Bytes;
let store = InMemoryBlobStore::new();
let body = Bytes::from_static(b"hello world");
let digest = Digest::sha256_of(&body);
store.put(&digest, body.clone()).await?;
assert!(store.contains(&digest).await?);
assert_eq!(store.get(&digest).await?, body);
assert_eq!(store.list().await?.len(), 1);Filesystem variant:
use ferro_blob_store::{BlobStore, Digest, FsBlobStore};
use bytes::Bytes;
let store = FsBlobStore::new("/var/lib/my-registry/blobs")?;
let body = Bytes::from_static(b"layer bytes");
let digest = Digest::sha256_of(&body);
store.put(&digest, body).await?;§Status
| Aspect | Status |
|---|---|
| API stability | stable (v1.x) — strict semver from 1.0.0 |
| Backends | InMemoryBlobStore ✅ / FsBlobStore ✅ (default feature) |
| Streaming I/O | not yet — additive in a future minor |
| MSRV | rustc 1.88 |
| Async runtime | Tokio (for FsBlobStore); the trait itself is runtime-agnostic |
§Used in production by
- ferro-oci-server — OCI Distribution v1.1 server primitives.
- ferro-maven-layout — Maven Repository Layout 2.0 + HTTP handlers.
- ferro-cargo-registry-server — Cargo Alternative Registry sparse-index server.
§License
Apache-2.0. See LICENSE.
Structs§
- Digest
- Content-addressed identifier in
<algo>:<hex>form. - FsBlob
Store fs - Local-filesystem implementation of
BlobStore. - InMemory
Blob Store Arc<RwLock<HashMap<Digest, Bytes>>>reference implementation.
Enums§
- Blob
Store Error - Errors emitted by
crate::BlobStoreimplementations. - Digest
Algo - Hash algorithm used by a
Digest. - Digest
Parse Error - Errors returned when a
<algo>:<hex>string fails validation.
Constants§
- CRATE_
NAME - Crate name, exposed for diagnostics and
/metricslabelling.
Traits§
- Blob
Store - A content-addressed blob store.
Type Aliases§
- Result
- Convenience
Resultalias. - Shared
Blob Store - Convenience type alias:
Arc<dyn BlobStore>.