Production-ready caching for Rust — dual-layer L1/L2, zero-knowledge encryption, multi-backend.
Features · Quick Start · Encryption · Backends · Architecture
cachekit-rs is the Rust SDK for cachekit.io. Plug in a backend, get dual-layer caching with optional client-side encryption. Bytes never leave your process unencrypted unless you say so.
| Component | What it does |
|---|---|
| CacheKit | get / set / delete / exists with automatic L1 → L2 layering |
| SecureCache | Transparent AES-256-GCM encryption before storage (zero-knowledge) |
| Backend | Pluggable trait — cachekit.io SaaS, Redis, Cloudflare Workers |
| L1 Cache | In-process moka cache with write-through + backfill |
Tip
For the Python SDK with decorators, see cachekit.
For the low-level compression/encryption primitives, see cachekit-core.
| Feature | Default | Description |
|---|---|---|
cachekitio |
✅ | HTTP backend for api.cachekit.io via reqwest + rustls |
encryption |
✅ | Zero-knowledge AES-256-GCM via cachekit-core |
l1 |
✅ | In-process L1 cache via moka |
redis |
❌ | Redis backend via fred (native only) |
workers |
❌ | Cloudflare Workers backend via worker |
macros |
❌ | #[cachekit] proc-macro decorator |
# Defaults: SaaS + encryption + L1
[dependencies]
cachekit-rs = "0.2"
# With Redis backend
[dependencies]
cachekit-rs = { version = "0.2", features = ["redis"] }
# For Cloudflare Workers (no L1, no Redis)
[dependencies]
cachekit-rs = { version = "0.2", default-features = false, features = ["workers", "encryption"] }Warning
Mutually exclusive features:
workers+redis— Workers runtime cannot use fredworkers+l1— moka requires std threads unavailable in wasm32
use cachekit::prelude::*;
#[tokio::main]
async fn main() -> Result<(), CachekitError> {
let cache = CacheKit::from_env()?.build()?;
cache.set("greeting", &"Hello, world!").await?;
let val: String = cache.get("greeting").await?.unwrap();
println!("{val}");
Ok(())
}use std::sync::Arc;
use std::time::Duration;
use cachekit::prelude::*;
use cachekit::backend::cachekitio::CachekitIO;
let backend = CachekitIO::builder()
.api_key("ck_live_...")
.build()?;
let cache = CacheKit::builder()
.backend(Arc::new(backend))
.default_ttl(Duration::from_secs(600))
.namespace("myapp")
.l1_capacity(5000)
.build()?;Important
Never hardcode API keys or master keys. Use environment variables or a secrets manager.
Call .secure() to get an encrypted cache handle. All values are encrypted client-side with AES-256-GCM before hitting any backend. The backend only ever sees ciphertext.
let cache = CacheKit::from_env()?.build()?;
let secure = cache.secure()?;
// Encrypt → store (backend sees only ciphertext)
secure.set("user:42:ssn", &"123-45-6789").await?;
// Retrieve → decrypt (transparent to caller)
let ssn: String = secure.get("user:42:ssn").await?.unwrap();┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Your Code │────>│ SecureCache │────>│ Backend │
│ │ │ AES-256-GCM │ │ (cachekit.io│
│ plaintext │ │ encrypt / │ │ or Redis) │
│ │<────│ decrypt │<────│ │
└──────────────┘ └──────────────┘ └──────────────┘
L1 stores ciphertext
(zero-knowledge preserved)
Security Properties
| Property | Implementation |
|---|---|
| Encryption | AES-256-GCM (AEAD) via cachekit-core (ring on native, aes-gcm on wasm32) |
| Key Derivation | HKDF-SHA256 — per-tenant cryptographic isolation |
| AAD Binding | Cache key bound to ciphertext (prevents substitution attacks) |
| Memory Safety | zeroize on drop for all key material |
| L1 Guarantee | L1 stores ciphertext, never plaintext |
AAD v0x03 wire format:
[version(0x03)][len(4)][tenant_id][len(4)][cache_key][len(4)][format][len(4)][compressed]
Each field is length-prefixed with a 4-byte big-endian u32 to prevent boundary-confusion attacks. Cross-SDK compatible — ciphertext produced by the Python SDK decrypts with the Rust SDK and vice versa.
HTTP backend targeting api.cachekit.io with session tracking, L1 metrics headers, SSRF-safe URL validation, distributed locking, and TTL inspection.
use cachekit::backend::cachekitio::CachekitIO;
let backend = CachekitIO::builder()
.api_key("ck_live_...")
.api_url("https://api.cachekit.io") // optional, this is the default
.build()?;Native Redis via fred with cluster support. Requires the redis feature flag.
cachekit-rs = { version = "0.2", features = ["redis"] }use cachekit::backend::redis::RedisBackend;
let backend = RedisBackend::builder()
.url("redis://localhost:6379")
.build()?;
backend.connect().await?; // explicit connect requiredwasm32-unknown-unknown backend using worker::Fetch. Requires the workers feature with default features disabled.
cachekit-rs = { version = "0.2", default-features = false, features = ["workers", "encryption"] }Custom Backend
Implement the Backend trait to plug in any storage:
use async_trait::async_trait;
use cachekit::backend::{Backend, HealthStatus};
use cachekit::error::BackendError;
use std::time::Duration;
struct MyBackend;
#[async_trait]
impl Backend for MyBackend {
async fn get(&self, key: &str) -> Result<Option<Vec<u8>>, BackendError> { todo!() }
async fn set(&self, key: &str, value: Vec<u8>, ttl: Option<Duration>) -> Result<(), BackendError> { todo!() }
async fn delete(&self, key: &str) -> Result<bool, BackendError> { todo!() }
async fn exists(&self, key: &str) -> Result<bool, BackendError> { todo!() }
async fn health(&self) -> Result<HealthStatus, BackendError> { todo!() }
}Optional extension traits: TtlInspectable (TTL queries), LockableBackend (distributed locking).
When the l1 feature is enabled (default), CacheKit maintains an in-process moka cache in front of the backend:
┌─────────────────────────────────────────────────────────┐
│ CacheKit Client │
├─────────────────────────────────────────────────────────┤
│ │
│ GET path: │
│ L1 hit (~50ns) ──► return immediately │
│ L1 miss ──► L2 backend ──► backfill L1 (30s cap) │
│ │
│ SET path: │
│ write to L2 backend ──► write-through to L1 │
│ │
│ DELETE path: │
│ invalidate L1 first ──► delete from L2 backend │
│ │
├─────────────┬───────────────────────────────────────────┤
│ L1 (moka) │ L2 (cachekit.io / Redis / Workers) │
│ ~50ns │ ~2–50ms │
└─────────────┴───────────────────────────────────────────┘
| Behavior | Detail |
|---|---|
| Write-through | set() writes to L2 first, then L1 |
| Backfill on miss | L2 hits populate L1 with a capped 30s TTL |
| Invalidate-first | delete() evicts L1 before touching L2 |
| Encrypted L1 | SecureCache stores ciphertext in L1 (never plaintext) |
| Default capacity | 1,000 entries (configurable via .l1_capacity()) |
| Variable | Required | Description |
|---|---|---|
CACHEKIT_API_KEY |
✅ | API key for cachekit.io |
CACHEKIT_API_URL |
❌ | Override API endpoint (default: https://api.cachekit.io) |
CACHEKIT_MASTER_KEY |
❌ | Hex-encoded master key (min 32 bytes) for encryption |
CACHEKIT_DEFAULT_TTL |
❌ | Default TTL in seconds (min 1, default: 300) |
Caution
CACHEKIT_API_URL must use HTTPS and must not point to a private IP address.
Both constraints are enforced at configuration time.
cachekit-rs/
├── crates/
│ ├── cachekit/ # Main SDK crate
│ │ └── src/
│ │ ├── lib.rs # Public API + prelude
│ │ ├── client.rs # CacheKit, SecureCache, CacheKitBuilder
│ │ ├── config.rs # CachekitConfig + from_env()
│ │ ├── encryption.rs # AES-256-GCM + AAD v0x03
│ │ ├── error.rs # CachekitError, BackendError
│ │ ├── key.rs # Blake2b-256 cache key generation
│ │ ├── metrics.rs # L1 hit-rate metrics headers
│ │ ├── session.rs # SDK session tracking
│ │ ├── url_validator.rs # SSRF-safe URL validation
│ │ ├── serializer/ # MessagePack serialization
│ │ ├── l1/ # moka-based L1 cache (feature = "l1")
│ │ └── backend/
│ │ ├── mod.rs # Backend + TtlInspectable + LockableBackend traits
│ │ ├── cachekitio.rs # cachekit.io HTTP backend
│ │ ├── cachekitio_lock.rs # Distributed locking
│ │ ├── cachekitio_ttl.rs # TTL inspection
│ │ ├── redis.rs # Redis backend (feature = "redis")
│ │ └── workers.rs # Workers backend (feature = "workers")
│ │
│ └── cachekit-macros/ # Proc-macro crate
│ └── src/lib.rs # #[cachekit] decorator
│
├── Cargo.toml # Workspace root
└── Makefile # Development commands
make quick-check # fmt + clippy + test (run before every commit)
make test # cargo test --all-features
make build # cargo build --release
make build-wasm # wasm32-unknown-unknown (workers feature)Rust 1.85 or later (Edition 2021).
MIT — see LICENSE for details.