Important
Under active development. Not production-ready.
This workspace provides the storage abstraction layer and JWT authentication library used by InferaDB Engine and InferaDB Control. It defines a common interface for persistent storage with pluggable backends and a hardened EdDSA-only JWT validator with three-tier key caching.
- Overview
- Crates
- Prerequisites
- Quick Start
- Architecture
- Usage
- Development
- Testing
- Benchmarks
- Contributing
- Community
- License
InferaDB Common consists of three crates that together provide:
- Storage abstraction (
StorageBackendtrait) with get, set, delete, range scans, compare-and-set, TTL, and transactions - In-memory backend for testing and development
- Ledger-backed backend for production use with cryptographic auditability, retry/timeout, circuit breaking, and distributed tracing
- JWT authentication with EdDSA-only enforcement (per RFC 8725), three-tier key caching, replay detection, and key material zeroing
| Crate | Package | Description |
|---|---|---|
crates/storage |
inferadb-common-storage |
Storage backend trait, in-memory implementation, batch writer, metrics, rate limiting, size limits |
crates/storage-ledger |
inferadb-common-storage-ledger |
Ledger-backed storage with retry, timeout, circuit breaker, CAS |
crates/authn |
inferadb-common-authn |
JWT validation, signing key cache, replay detection, audit logging |
- Rust 1.92+ (stable) — for building and clippy
- Rust nightly — for
cargo fmtonly - mise — synchronized development tooling (
mise trust && mise install) - just — task runner for common development commands
- protobuf 29+ and buf 1+ — for Ledger SDK proto compilation (installed via mise)
Install the Rust toolchains:
rustup install 1.92
rustup install nightlygit clone https://github.com/inferadb/common.git
cd common
mise trust && mise install
just build
just testThat's it. just check runs the full CI suite (build + clippy + test + format check).
inferadb-common-authn
├── inferadb-common-storage (PublicSigningKeyStore trait, error types)
└── moka, jsonwebtoken, ed25519-dalek (JWT validation)
inferadb-common-storage-ledger
├── inferadb-common-storage (StorageBackend trait)
└── inferadb-ledger-sdk (Ledger gRPC client)
| Abstraction | Location | Purpose |
|---|---|---|
StorageBackend |
storage/src/backend.rs |
Core trait — get, set, delete, range, CAS, TTL, transactions, health check |
Transaction |
storage/src/transaction.rs |
Atomic multi-operation commit |
MemoryBackend |
storage/src/memory.rs |
In-memory implementation for tests |
LedgerBackend |
storage-ledger/src/backend.rs |
Production backend with retry, timeout, circuit breaker |
PublicSigningKeyStore |
storage/src/auth/store.rs |
Trait for key storage (memory and ledger implementations) |
SigningKeyCache |
authn/src/signing_key_cache.rs |
Three-tier cache: L1 (moka TTL) → L2 (Ledger) → L3 (bounded fallback) |
JwtValidator |
authn/src/jwt.rs |
EdDSA JWT validation with configurable claims, replay detection |
JWT Request
→ JwtValidator::validate()
→ SigningKeyCache::get_decoding_key()
→ L1 (moka TTL cache, ~ms)
→ L2 (Ledger via PublicSigningKeyStore, ~10-100ms)
→ L3 (bounded fallback cache, disaster recovery)
→ signature verification (EdDSA only)
→ claims validation (exp, iat, aud, iss, kid)
→ replay detection (optional, JTI-based)
→ JwtClaims
Storage Operation
→ RateLimitedBackend (optional)
→ LedgerBackend
→ circuit breaker check
→ with_retry_timeout()
→ SDK call with W3C Trace Context
→ circuit breaker state update
The workspace uses newtype wrappers for identifiers to prevent accidental mixing:
| Type | Purpose |
|---|---|
NamespaceId |
Tenant/namespace identifier |
VaultId |
Vault (key collection) identifier |
ClientId |
API client identifier |
CertId |
Certificate identifier |
use inferadb_common_storage::{StorageBackend, MemoryBackend};
let backend = MemoryBackend::new();
// Basic CRUD
backend.set(b"key".to_vec(), b"value".to_vec()).await?;
let value = backend.get(b"key").await?;
backend.delete(b"key").await?;
// Range queries
let entries = backend.get_range(b"prefix:".to_vec()..b"prefix:\xff".to_vec()).await?;
// Transactions
let mut tx = backend.transaction().await?;
tx.set(b"key1".to_vec(), b"value1".to_vec());
tx.set(b"key2".to_vec(), b"value2".to_vec());
tx.commit().await?;
// Compare-and-set
backend.set(b"counter".to_vec(), b"1".to_vec()).await?;
backend.compare_and_set(b"counter", Some(b"1".as_slice()), b"2".to_vec()).await?;use inferadb_common_storage_ledger::{LedgerBackend, LedgerBackendConfig};
let config = LedgerBackendConfig::builder()
.servers(["http://ledger.example.com:50051"])
.build()?;
let backend = LedgerBackend::new(config).await?;use inferadb_common_authn::{verify_with_signing_key_cache, SigningKeyCache};
// Set up signing key cache with your key store
let cache = SigningKeyCache::new(key_store, l1_ttl, l3_capacity);
// Validate a JWT
let claims = verify_with_signing_key_cache(
&token,
&cache,
&expected_audience,
&expected_issuer,
).await?;
println!("org: {}, vault: {}", claims.org_id, claims.vault_id);- Implement the
StorageBackendtrait frominferadb-common-storage - All 10 methods are required:
get,set,compare_and_set,delete,get_range,clear_range,set_with_ttl,transaction,health_check - Run the conformance test suite against your implementation:
use inferadb_common_storage::conformance;
#[tokio::test]
async fn conformance() {
let backend = MyBackend::new();
conformance::run_all(&backend).await;
}Error enums use #[non_exhaustive] and constructor methods:
- Add the variant to the enum in the appropriate
error.rs(includespan_id: Option<tracing::span::Id>) - Add a constructor method that calls
current_span_id()for automatic span capture - Update the manual
Displayimpl - Update
is_transient()classification - Update
detail()if the variant carries internal context
- No
unsafe,.unwrap(),panic!(),todo!(),unimplemented!() - Builders via
bon— use#[builder(into)]forStringfields - Fallible builders return
Result<Self, ConfigError>with validation at construction - Doc examples use
```no_run(skip execution, verify compilation) - All errors carry optional
span_idfor distributed tracing correlation
# Run all tests
just test
# Run tests for a specific crate
cargo +1.92 test -p inferadb-common-storage
cargo +1.92 test -p inferadb-common-authn
cargo +1.92 test -p inferadb-common-storage-ledgerThe workspace uses proptest for property-based testing (key encoding round-trips, range bound normalization, size limit enforcement):
# Property tests run as part of the normal test suite
just testJWT parsing fuzz targets live in crates/authn/fuzz/:
# Install cargo-fuzz
cargo install cargo-fuzz
# Run JWT parsing fuzzer
cd crates/authn
cargo +nightly fuzz run fuzz_jwt_parsing -- -max_total_time=60
# Run JWT claims fuzzer
cargo +nightly fuzz run fuzz_jwt_claims -- -max_total_time=60Deterministic fault injection tests are gated behind the failpoints feature:
cargo +1.92 test -p inferadb-common-storage --features failpoints
cargo +1.92 test -p inferadb-common-authn --features failpoints,testutilConcurrency stress tests are #[ignore]-gated to avoid slowing CI:
# Run stress tests explicitly
cargo +1.92 test --all -- --ignoredTests in crates/storage-ledger/tests/real_ledger_integration.rs require a running Ledger instance and are #[ignore]-gated:
# Start a local Ledger instance first, then:
cargo +1.92 test -p inferadb-common-storage-ledger -- --ignoredThe inferadb-common-storage crate includes Criterion benchmarks:
# Run all benchmarks
cargo bench -p inferadb-common-storage
# Run a specific benchmark group
cargo bench -p inferadb-common-storage -- get_operations
# Save a baseline for comparison
cargo bench -p inferadb-common-storage -- --save-baseline main
# Compare against a baseline
cargo bench -p inferadb-common-storage -- --baseline main| Group | Description |
|---|---|
get_operations |
Single key lookups (existing, missing, varying sizes) |
set_operations |
Single key writes (new, overwrite, varying value sizes) |
delete_operations |
Key deletion (existing, missing) |
get_range_operations |
Range scans with varying result sizes |
clear_range_operations |
Range deletion with varying sizes |
transaction_operations |
Transaction commit with single/multiple operations |
concurrent_operations |
Parallel read/write workloads |
ttl_operations |
Time-to-live key operations |
health_check |
Backend health check overhead |
get_operations/get_existing_key
time: [1.234 us 1.256 us 1.278 us]
change: [-2.34% +0.12% +2.56%] (p = 0.89 > 0.05)
No change in performance detected.
- time: [lower bound, estimate, upper bound] at 95% confidence
- change: percentage change from baseline
- p-value: p < 0.05 indicates statistically significant change
See the Contributing Guide for commit message conventions (Conventional Commits), PR process, and code of conduct.
Before submitting a PR, run the full check suite:
just checkThis runs:
cargo +1.92 build --all-targets— compilationcargo +1.92 clippy --all-targets -- -D warnings— lints (zero warnings)cargo +1.92 test --all— all tests passcargo +nightly fmt --check— formatting
Join us on Discord for questions and discussions.
Dual-licensed under MIT or Apache 2.0.
