12 releases
| new 0.6.6 | Apr 10, 2026 |
|---|---|
| 0.6.4 | Mar 23, 2026 |
| 0.5.13 | Mar 1, 2026 |
| 0.5.11 | Feb 26, 2026 |
#1330 in Network programming
Used in 4 crates
(3 directly)
200KB
4K
SLoC
Zentinel Common
Shared types, utilities, and infrastructure for all Zentinel components.
Overview
The zentinel-common crate provides the foundational building blocks used across the Zentinel platform:
- Type-safe identifiers - Compile-time safety for IDs and scopes
- Error handling - Comprehensive error types with HTTP mapping
- Resource limits - Hard bounds for predictable behavior
- Observability - Prometheus metrics and structured logging
- Circuit breakers - Failure isolation patterns
- Registries - Thread-safe component storage with hot reload
- Budget tracking - Token budgets and cost attribution for inference
Modules
| Module | Description |
|---|---|
ids |
Type-safe identifiers (RouteId, UpstreamId, AgentId) and hierarchical scoping |
types |
Common types (HttpMethod, TlsVersion, LoadBalancingAlgorithm, etc.) |
errors |
Error types with HTTP status mapping and client-safe messages |
limits |
Resource limits and rate limiting infrastructure |
observability |
Prometheus metrics and tracing initialization |
circuit_breaker |
Circuit breaker state machine |
registry |
Generic thread-safe component registry |
scoped_registry |
Scope-aware hierarchical registry |
scoped_metrics |
Namespace/service-scoped metrics |
budget |
Token budgets and cost attribution for inference |
inference |
Inference health check configurations |
Quick Start
use zentinel_common::{
// Identifiers
RouteId, UpstreamId, Scope, QualifiedId, CorrelationId,
// Types
HttpMethod, LoadBalancingAlgorithm, CircuitBreakerConfig,
// Errors
ZentinelError, ZentinelResult,
// Limits
Limits, RateLimiter,
// Observability
RequestMetrics, init_tracing,
// Patterns
CircuitBreaker, Registry, ScopedRegistry,
};
// Initialize tracing
init_tracing();
// Create metrics collector
let metrics = RequestMetrics::new();
// Use type-safe identifiers
let route_id = RouteId::new("api-v1");
let scope = Scope::Service {
namespace: "production".to_string(),
service: "payments".to_string(),
};
// Resolve names through scope chain
let qualified = QualifiedId::new("backend", scope.clone());
println!("Canonical: {}", qualified.canonical()); // "production:payments:backend"
Documentation
Detailed documentation is available in the docs/ directory:
- Identifiers & Scoping - Type-safe IDs and hierarchical scopes
- Types Reference - Common type definitions
- Error Handling - Error types and HTTP mapping
- Limits & Rate Limiting - Resource bounds
- Observability - Metrics and logging
- Patterns - Circuit breakers and registries
Hierarchical Scoping
Zentinel supports hierarchical configuration with scope-based resolution:
Global
└── Namespace (e.g., "production")
└── Service (e.g., "payments")
Names resolve through the scope chain (most specific wins):
use zentinel_common::{Scope, ScopedRegistry};
let registry: ScopedRegistry<Config> = ScopedRegistry::new();
// Insert at different scopes
registry.insert(QualifiedId::global("timeout"), global_config);
registry.insert(QualifiedId::namespaced("production", "timeout"), prod_config);
registry.insert(QualifiedId::in_service("production", "payments", "timeout"), payments_config);
// Resolve from service scope
let scope = Scope::Service {
namespace: "production".to_string(),
service: "payments".to_string(),
};
// Finds "production:payments:timeout" first
let config = registry.resolve("timeout", &scope);
Error Handling
All errors map to appropriate HTTP status codes:
use zentinel_common::{ZentinelError, ZentinelResult};
fn process_request() -> ZentinelResult<Response> {
// Errors automatically map to HTTP status
Err(ZentinelError::RateLimit {
message: "Too many requests".to_string(),
retry_after_secs: Some(60),
})
}
// In handler
match result {
Ok(response) => response,
Err(e) => {
let status = e.to_http_status(); // 429
let message = e.client_message(); // Safe for client
// ...
}
}
Resource Limits
Hard bounds prevent resource exhaustion:
use zentinel_common::Limits;
// Production defaults
let limits = Limits::for_production();
// Check before processing
if body.len() > limits.max_body_size_bytes {
return Err(ZentinelError::LimitExceeded {
limit_type: LimitType::BodySize,
message: "Request body too large".to_string(),
current_value: body.len() as u64,
limit: limits.max_body_size_bytes as u64,
});
}
Observability
Prometheus metrics with automatic registration:
use zentinel_common::RequestMetrics;
let metrics = RequestMetrics::new();
// Record request
metrics.record_request("api", "GET", 200, Duration::from_millis(50));
// Track active requests
metrics.inc_active_requests();
// ... process ...
metrics.dec_active_requests();
// Circuit breaker state
metrics.set_circuit_breaker_state("upstream", "api", true);
Circuit Breakers
Failure isolation with automatic recovery:
use zentinel_common::{CircuitBreaker, CircuitBreakerConfig};
let config = CircuitBreakerConfig {
failure_threshold: 5,
success_threshold: 2,
timeout_seconds: 30,
half_open_max_requests: 1,
};
let breaker = CircuitBreaker::new(config);
// Check before calling
if !breaker.is_closed() {
return Err(ZentinelError::CircuitBreakerOpen { ... });
}
// Record result
match upstream_call().await {
Ok(response) => {
breaker.record_success();
Ok(response)
}
Err(e) => {
breaker.record_failure();
Err(e)
}
}
Token Budgets
For inference routes with usage limits:
use zentinel_common::{TokenBudgetConfig, BudgetPeriod, BudgetCheckResult};
let config = TokenBudgetConfig {
period: BudgetPeriod::Daily,
limit: 1_000_000,
enforce: true,
alert_thresholds: vec![0.80, 0.90, 0.95],
..Default::default()
};
// Check budget before processing
match budget_tracker.check(estimated_tokens) {
BudgetCheckResult::Allowed { remaining } => {
// Process request
}
BudgetCheckResult::Exhausted { retry_after_secs } => {
return Err(ZentinelError::LimitExceeded { ... });
}
BudgetCheckResult::Soft { remaining, over_by } => {
// Allowed via burst allowance
}
}
Design Principles
- Type Safety - Distinct ID types prevent accidental confusion
- Fail-Safe Defaults - Secure, bounded defaults everywhere
- Observability First - Metrics for every significant operation
- Lock-Free Hot Paths - Atomic operations where possible
- Graceful Degradation - Clear failure modes with fallbacks
Usage by Other Crates
| Crate | Uses |
|---|---|
config |
Types, Limits, Error handling, ID types |
proxy |
All modules - core runtime infrastructure |
agent-protocol |
Error types, ID types |
Minimum Rust Version
Rust 1.92.0 or later (Edition 2021)
Dependencies
~7–14MB
~183K SLoC