dig_store_cache/lib.rs
1//! # dig-store-cache
2//!
3//! The DIG Node's on-disk cache of already-verified `.dig` capsules — the **cache + reshare** leg of
4//! the content-replication flywheel (`install → connect → discover → read → CACHE → reshare`). When
5//! the node fetches and verifies a capsule, it admits the finished bytes here; the cache holds them
6//! under a bounded, evicted (pluggable policy, LRU by default), pin-aware, crash-safe store and reports
7//! the resulting [`holdings`](Cache::holdings) so the node can announce itself as a provider. Every
8//! read therefore makes content MORE available.
9//!
10//! ## Boundary — a pure filesystem primitive
11//!
12//! This crate has NO chain, NO network, and NO key dependencies. It **trusts the caller to have
13//! verified content** before [`put_file`](Cache::put_file)/[`put_bytes`](Cache::put_bytes) (dig-node
14//! runs the NC-9 merkle/chain verify), and it does not choose where it lives — the caller passes the
15//! `root`. It re-exports [`dig_store::CapsuleIdentity`] as THE content id so the whole ecosystem speaks
16//! one identity type. The optional [`PutOptions::check_identity`] adds a cheap structural sanity check
17//! (the bytes' declared identity equals the claim) — not a substitute for the caller's real verify.
18//!
19//! ## Durability contract
20//!
21//! Disk is authoritative; the `index.json` manifest is an advisory overlay. Admission is atomic (stage
22//! to `tmp/`, fsync, rename into `capsules/`), so a crash never leaves a half file among the capsules.
23//! On [`open`](Cache::open) the index is rebuilt by SCANNING `capsules/` and overlaying the manifest
24//! for recency + pin state; a lost manifest costs only recency ordering (recovered from mtimes) and
25//! pins, never a capsule. See [`SPEC.md`](https://github.com/DIG-Network/dig-store-cache/blob/main/SPEC.md).
26//!
27//! ## Example
28//!
29//! ```no_run
30//! use dig_store_cache::{Cache, CacheConfig, PutOptions, CapsuleIdentity};
31//! use std::path::Path;
32//!
33//! # fn demo(id: CapsuleIdentity, verified: &Path) -> Result<(), dig_store_cache::CacheError> {
34//! let cache = Cache::open(Path::new("/var/lib/dig/cache"), CacheConfig::default())?;
35//! let admission = cache.put_file(id, verified, PutOptions::default())?;
36//! // `admission.evicted` lists capsules the node must stop advertising.
37//! if let Some(hit) = cache.get(&id) {
38//! // stream from `hit.path()` — capsules can be ~1 GiB, never assume they fit in RAM
39//! let _path = hit.path();
40//! }
41//! # Ok(())
42//! # }
43//! ```
44
45#![forbid(unsafe_op_in_unsafe_fn)]
46#![warn(missing_docs)]
47
48mod cache;
49mod config;
50mod error;
51mod index;
52mod layout;
53mod policy;
54
55pub use cache::{Cache, CachedCapsule};
56pub use config::{Admission, CacheConfig, CacheStats, PutOptions, DEFAULT_MAX_BYTES};
57pub use error::CacheError;
58pub use policy::{EvictionContext, EvictionEntry, EvictionPolicy, LruPolicy};
59
60/// The content id a capsule is addressed by, re-exported from `dig-store` so the ecosystem speaks ONE
61/// identity type (`{ store_id, root_hash }`).
62pub use dig_store::CapsuleIdentity;