decdn_cache/lib.rs
1//! Cache engine for deCDN.
2//!
3//! Wraps `iroh-blobs` with origin pull-through logic. The buffered entry
4//! points ([`engine::CacheEngine::get`] / `populate`) fetch from a configured
5//! [`Origin`] backend on a cache miss, verify the BLAKE3 hash, and insert the
6//! bytes before returning them to the caller. The streaming entry points do
7//! not buffer whole-blob first: [`engine::CacheEngine::origin_range_wire`]
8//! streams one verified sub-range per draw, window by window, for the node's
9//! two-leg serve-miss spine. Node-to-node pull-through is one
10//! such backend — the
11//! `node` crate supplies an [`Origin`] (`NodeOrigin`, #831) that pays a peer
12//! over `cdn/client/v1` — so this crate needs no knowledge of the paid path.
13//!
14//! Leaf crate per `adr/appendix-poc-production-seams.md` — no `#[cfg(...)]`
15//! mode branching or feature flags here. The `node` crate's wiring layer
16//! selects which origin backend to construct.
17
18pub mod circuit_breaker;
19pub mod engine;
20pub mod error;
21pub mod fill_session;
22pub mod metrics;
23pub mod origin;
24pub mod origin_probe;
25pub mod origin_range;
26mod outboard_cache;
27pub mod policy;
28pub mod probe_hold;
29pub mod range_pull;
30pub mod ranged_store;
31pub mod retry;
32pub mod serve_store;
33
34pub use circuit_breaker::{
35 Admission, BreakerState, Clock, ManualClock, OriginBreaker, OriginOutcome, SystemClock,
36};
37/// Bao chunk-group granularity (16 KiB, ADR 038), re-exported because it is a
38/// *billing-visible* property of [`CacheEngine::export_bao_range_stream`]: the
39/// export snaps a requested range out to the enclosing group boundaries and the
40/// serve path does not trim back, so the payer is charged for the aligned
41/// superset. Any caller pricing a range before serving it must align first or it
42/// under-reserves by up to two groups.
43pub use decdn_bao_range::CHUNK_GROUP_BYTES;
44pub use engine::{
45 CacheEngine, EvictionCandidates, EvictionPreview, OriginHeldReport, OriginPresence,
46 PresentRanges, ServeAudit,
47};
48pub use error::{
49 CacheError, CacheResult, ErrorChain, OriginError, OriginPullError, SupportedEncoding,
50};
51pub use fill_session::{
52 Demand, DemandSlot, DownstreamWatch, FillClaim, FillError, FillRegistry, FillSession, Frontier,
53 HashOutboard, ObserverLease, SessionOutboardReader,
54};
55/// The blob-store hash. `decdn_cache::Hash` continues to mean
56/// `iroh_blobs::Hash` (the BLAKE3 digest the iroh-blobs store keys on)
57/// so every existing `decdn_cache::Hash` path in `decdn-node` and the
58/// cache crate is unchanged. The *config-vocabulary* hash that crosses
59/// operator-facing config/admin surfaces is the distinct
60/// [`decdn_config_types::Hash`]; [`to_store_hash`]/[`from_store_hash`]
61/// convert between the two (both are the same 32 bytes — issue #578).
62pub use iroh_blobs::Hash;
63pub use metrics::CacheMetrics;
64pub use origin::{
65 FilesystemOrigin, HttpOrigin, Origin, OriginFetch, OriginRangeFetch, OriginRangeRequest,
66 OutboardFetch, S3Credentials, S3Origin, S3OriginConfig,
67};
68pub use origin_range::{MAX_CONCURRENT_RANGE_PULLS, OriginRangeWire, RANGE_PULL_WINDOW_BYTES};
69pub use policy::{
70 AdmissionContext, AdmissionDecision, AdmissionPolicy, AlwaysAdmit, EvictionContext,
71 EvictionPlan, EvictionPolicy, FrequencyEstimator, LruEviction, Segment,
72};
73pub use probe_hold::{
74 PROBE_HOLD_DURATION, PROBE_HOLD_MARGIN, PROBE_SLASH_WINDOW, ProbeHoldOutcome,
75};
76pub use ranged_store::NodeRangedStore;
77pub use serve_store::{EncodeStream, PresentRangeWatch, ServeStore};
78
79// Config-vocabulary types live in the `decdn-config-types` leaf crate
80// (no iroh-blobs / no AWS SDK) so the publisher CLI doesn't link the
81// storage stack — see issue #578 and `adr/appendix-binaries.md`.
82// Re-exported here so existing `decdn_cache::*` paths in `decdn-node`
83// keep resolving unchanged. `decdn_config_types::Hash` is intentionally
84// NOT re-exported as `decdn_cache::Hash` — that name stays the store
85// hash above.
86/// The config-vocabulary hash type (`decdn_config_types::Hash`), re-exported so
87/// callers can build a [`DeniedHashes`] / [`PinnedHashes`] without taking a
88/// direct dependency on the leaf crate. Distinct from [`struct@Hash`], which is
89/// the iroh-blobs store hash — see the `hash_bridge` conversions.
90pub use decdn_config_types::Hash as LeafHash;
91pub use decdn_config_types::{
92 Bytes, CircuitBreakerPolicy, DEFAULT_MAX_PROBE_HOLDS, DEFAULT_USER_AGENT, DecompressMode,
93 DeniedHashes, HashParseError, OriginKind, OriginUrl, PinDiff, PinnedHashes, RetryPolicy,
94 parse_origin_url, redact_for_log,
95};
96
97/// Convert a config-vocabulary [`decdn_config_types::Hash`] into the
98/// blob-store [`struct@Hash`]. Both are 32-byte BLAKE3 digests; this is a pure
99/// byte-for-byte reinterpretation (issue #578). Used at the node's
100/// admin boundary where `parse_hash_arg` yields the config-vocabulary
101/// hash that must then index the store.
102#[must_use]
103pub const fn to_store_hash(h: decdn_config_types::Hash) -> Hash {
104 Hash::from_bytes(h.to_bytes())
105}
106
107/// Convert a blob-store [`struct@Hash`] into a config-vocabulary
108/// [`decdn_config_types::Hash`].
109#[must_use]
110#[allow(clippy::missing_const_for_fn)] // iroh_blobs::Hash::as_bytes is not const
111pub fn from_store_hash(h: Hash) -> decdn_config_types::Hash {
112 decdn_config_types::Hash::from_bytes(*h.as_bytes())
113}
114
115#[cfg(test)]
116#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)] // tests
117mod hash_bridge_tests;