Skip to main content

cas_kit/
lib.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2//! `cas-kit` — a content-addressed storage (CAS) primitive.
3//!
4//! Blobs are stored on the local filesystem and identified by their
5//! BLAKE3 hash (32 bytes / 256 bits). Identical blobs are deduplicated
6//! automatically; integrity can be verified on every read.
7//!
8//! # On-Disk Layout
9//!
10//! ```text
11//! <root>/
12//!   objects/
13//!     ab/           # First 2 hex chars of the hash (256 buckets)
14//!       cdef...     # Remaining 62 hex chars = blob filename
15//!     pack/
16//!       pack-<hex>.pack   # Bundled blobs (always Zstd-compressed)
17//!       pack-<hex>.idx    # Sorted hash -> offset index
18//! ```
19//!
20//! The 2-hex-prefix bucketing avoids any single directory holding too
21//! many entries. Pack files bundle many small blobs into one file to
22//! reduce filesystem overhead; see [`PackFile`].
23//!
24//! # Correctness Properties
25//!
26//! - **Integrity**: `get(H(data)) == data` (BLAKE3 collision resistance),
27//!   enforced by optional-but-default verify-on-read.
28//! - **Deduplication**: storing the same blob twice writes one copy.
29//! - **Lossless**: Zstd compression/decompression is lossless.
30//!
31//! # Thread Safety
32//!
33//! [`BlobStore`] is `Send + Sync` and can be shared across threads via
34//! `Arc`. Interior mutability (the blob cache, pack-index cache and
35//! bucket-directory cache) uses `std::sync::Mutex`; lock poisoning is
36//! reported as [`CasError::LockPoisoned`] rather than unwrapped.
37//!
38//! # Features
39//!
40//! - `zstd` (default): enables Zstd compression of loose blobs and pack
41//!   contents. Builds without this feature store everything raw and
42//!   cannot read stores written by zstd-enabled builds (reads of
43//!   compressed frames fail hash verification rather than silently
44//!   returning wrong bytes).
45//! - `tokio` (optional): async wrappers [`gc::mark_async`] /
46//!   [`gc::sweep_async`] over the blocking thread pool.
47//!
48//! # Garbage collection
49//!
50//! Objects are opaque blobs, so reachability is a host-level concept;
51//! [`gc`] implements mark–sweep over a host-supplied live set, with
52//! dry-run / trash / delete modes and pack-rewrite support. The
53//! `cas-gc` binary (same crate) drives it from the command line.
54//!
55//! # Example
56//!
57//! ```no_run
58//! use cas_kit::BlobStore;
59//!
60//! # fn main() -> Result<(), cas_kit::CasError> {
61//! let store = BlobStore::new("/tmp/my-store")?;
62//! let hash = store.put_blob(b"hello, world")?;
63//! assert_eq!(store.get_blob(&hash)?, b"hello, world".to_vec());
64//! # Ok(())
65//! # }
66//! ```
67
68#![forbid(unsafe_code)]
69#![deny(missing_docs)]
70
71mod compressor;
72mod error;
73pub mod gc;
74mod hash;
75mod hasher;
76pub mod pack;
77pub mod store;
78
79pub use error::CasError;
80pub use gc::{LiveSet, SweepMode, SweepOptions, SweepPlan, SweepReport};
81pub use hash::Hash;
82pub use hasher::{hash_bytes, hash_file, hash_with_context, verify_hash};
83pub use pack::{PackCache, PackError, PackFile, PackIndex};
84pub use store::BlobStore;