Distributed context propagation for Rust.
dcontext provides a scoped, type-safe key–value store that travels with the
execution flow — across function calls, async/sync boundaries, thread spawns,
and even process boundaries via serialization.
- Scoped context — Enter/leave scopes with automatic rollback (RAII guards)
- Type-safe — Compile-time checked access via generics over
Anystorage - Cross-thread — Snapshot and attach context when spawning threads
- Cross-async — Helpers for propagating context across Tokio tasks
- Runtime-agnostic async —
ContextFuturepoll-wrapper works with any executor - Serializable — Serialize context to bytes/string for cross-process propagation
- Local-only entries — Non-serializable context that stays within the process
- Version migration — Rolling upgrades with automatic old→new schema conversion
- Scope chain — Named scopes with queryable distributed call chain (
scope_chain()) - O(1) lookups — Index-based read cache for fast context access
[dependencies]
dcontext = "0.3"use dcontext::{register, enter_scope, enter_named_scope, get_context, set_context, scope_chain};
use serde::{Serialize, Deserialize};
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
struct RequestId(String);
fn main() {
register::<RequestId>("request_id");
{
let _guard = enter_named_scope("ingress");
set_context("request_id", RequestId("req-123".into()));
handle_request(); // sees "req-123"
// Query the scope chain
let chain = scope_chain(); // e.g. ["ingress"]
println!("scope chain: {:?}", chain);
}
// scope reverted — request_id is back to default
}┌─────────────────────────────────────────┐
│ Application Code │
├─────────────────────────────────────────┤
│ register / get_context / set_context │
├─────────────────────────────────────────┤
│ Scope Tree │ Registry │ Snapshot │
├──────────────┼────────────┼─────────────┤
│ Thread-local storage / Task-local │
└─────────────────────────────────────────┘
- Context — A
HashMap<String, Any>of named, type-erased values - Scope — A stack frame that overlays the parent; changes revert on exit
- ScopeGuard — RAII guard returned by
enter_scope()/enter_named_scope(); drops → reverts - Scope Chain — Ordered list of named scope names (local + remote prefix); query with
scope_chain() - Snapshot — A captured, cloneable, sendable copy of the current context
- Registry — Global type registration for serialization support
- ContextKey<T> — Optional typed key wrapper for compile-time safe access
| Crate | Description |
|---|---|
| dcontext-dactor | Automatic context propagation through dactor actor messages |
| dcontext-tracing | Automatic context scoping via tracing spans |
| Feature | Default | Description |
|---|---|---|
tokio |
yes | Tokio task-local storage, scope_async, and async spawn helpers |
base64 |
yes | Base64 string serialization for HTTP headers/gRPC metadata |
context-key |
yes | ContextKey<T> typed key wrapper for compile-time safe access |
context-future |
no | ContextFuture poll-wrapper for runtime-agnostic async (non-Tokio executors) |
- Usage Guide — Comprehensive guide covering all features with examples
- Design Document — Internal architecture and design decisions
Run any sample with cargo run --bin <name>:
| Sample | Description |
|---|---|
basic_scope |
Core get/set/scope API |
cross_thread |
Thread propagation via spawn_with_context |
async_tasks |
Tokio propagation via with_context |
async_scopes |
scope_async across .await points |
cross_process |
Serialization (bytes/base64) |
typed_keys |
ContextKey<T> type safety |
macros |
register_contexts! & with_scope! |
worker_pool |
Context-aware work dispatch |
feature_flags |
Per-request feature flag overrides |
size_limits |
set_max_context_size cap |
tracing_scopes |
dcontext-tracing integration |
scope_chain |
Named scopes and scope chain query |
dactor_propagation |
dcontext-dactor propagation flow |
MIT