Expand description
Boson is a Rust job-work runtime: durable background tasks, retries, rate limits, and
pluggable persistence behind QueueBackend.
Wire a backend once with Boson::builder(), define handlers with task, then enqueue
with typed send_with (after configure) or Boson::enqueue. Swap mem, sqlite,
postgres, or fleet crates (boson-backend-redis / boson-backend-nats) without changing
task code.
§Features
- Typed task handlers —
taskmacro with policy attributes and generatedsend_with - Composable persistence — inject
MemQueueBackend,SqliteQueueBackend,PostgresQueueBackend, or fleet backends onBosonBuilder - Embedded or remote workers — one process can enqueue and drain, or many hosts can enqueue while a separate worker binary claims jobs (see Remote worker)
- Leases and pools — multi-process coordination via
BosonBuilder::lease_ttl_secsandWorkerSettings - HTTP admin (optional) — nest
boson_routeratNEST_PATHwhen theaxumfeature is enabled; installAdminAuthand setBOSON_REQUIRE_ADMIN_AUTH=1for fail-closed production mounts - Lease heartbeats — workers refresh leases during long handlers when
lease_ttl_secs > 0 - Actor provenance — HTTP enqueue uses a non-System service marker; optional
ActorJsonPolicyrejects System-shaped actors on external paths
Background jobs without locking you into one queue store.
This crate ships with no default features (default = []). Enable explicitly:
mem—MemQueueBackendfor tests and local embedded bootssqlite—SqliteQueueBackenddurable single-host (or shared-file remote worker)postgres—PostgresQueueBackendshared durable statetelemetry-console— marker for console ops log (ConsoleOpsLogis always re-exported)axum— HTTP admin API (boson_router,BosonState,AdminAuth,NEST_PATH)
Fleet backends (boson-backend-redis, boson-backend-nats) are separate workspace crates.
§Getting started
You always define and enqueue tasks the same way (#[task], send_with). What changes is
which process runs the worker loop.
§Choose your topology
- Embedded (one binary) — one binary enqueues and drains. Start here. (Formerly called Mode 1.)
- Remote worker (two binaries) — API/host processes enqueue; a separate worker binary (or fleet) claims and runs jobs. (Formerly Mode 2.)
After you pick a topology, continue with define tasks (shared by every topology).
§Embedded (one binary)
This process enqueues jobs and runs the worker loop (or drives ManualWorker in tests).
There is no second binary. Default lease TTL is 0 (no distributed lease coordination).
Your app ──enqueue──► Boson ──worker loop──► mem / `SQLite` / Postgres / …| Backend | Type | Feature / crate | Topology | When to use |
|---|---|---|---|---|
| In-memory | MemQueueBackend | mem | embedded only | Local experiments and tests |
SQLite | SqliteQueueBackend | sqlite | embedded (or remote worker on one host) | Durable single host |
| Postgres | PostgresQueueBackend | postgres | embedded or remote | Shared durable state |
| Redis | RedisQueueBackend | boson-backend-redis | remote / fleet | Broker-backed multi-host |
| NATS | NatsQueueBackend | boson-backend-nats | remote / fleet | Broker-backed multi-host |
In-memory first run (feature mem):
use std::sync::Arc;
use boson::{
configure, task, Boson, ExecutionContext, JsonExecutionContextFactory, MemQueueBackend,
};
#[task(name = "greet")]
async fn greet(ctx: Box<dyn ExecutionContext>, name: String) -> boson_core::Result<()> {
let _ = (ctx, name);
Ok(())
}
let boson = Boson::builder()
.queue_backend(Arc::new(MemQueueBackend::new()))
.execution_context_factory(JsonExecutionContextFactory)
.auto_registry()
.build()?; // background worker loop
configure(boson);
Greet::send_with(
serde_json::json!({"System": {"operation": "demo"}}),
GreetParams { name: "world".into() },
)
.await?;For step-driven tests, use BosonBuilder::without_worker + BosonBuilder::build_manual
and ManualWorker::try_run_next instead of BosonBuilder::build.
Runnable: task_macro, minimal_enqueue, idempotency_and_rate_limit
(cargo run -p uf-boson --example <name> --features mem).
Then continue with define tasks.
§Remote worker (two binaries)
Use this when HTTP/API processes should enqueue only, and a dedicated worker process (or many workers) should claim and run jobs against shared persistence.
MemQueueBackend cannot cross process boundaries — remote worker needs SQLite (shared path),
Postgres, Redis, or NATS.
Enqueue binary(ies) ──send_with──► shared QueueBackend ◄──claim── Worker binary(ies)§What you create
| Piece | Purpose |
|---|---|
| Shared task crate (recommended) | Same #[task] handlers + inventory on the worker |
| Enqueue binary | Boots Boson without a worker loop; auto_registry so |
descriptors exist for send_with; calls configure + send_with | |
| Worker binary | Same backend URL/path; auto_registry; unique |
worker_id; lease_ttl_secs > 0; build | |
| Shared backend | SQLite path, Postgres URL, or Redis/NATS fleet |
§Enqueue binary
This process must not spawn the drain loop. Use BosonBuilder::without_worker then
BosonBuilder::build, still call BosonBuilder::auto_registry (enqueue looks up task
descriptors for priority/pool/policies), install with configure, and enqueue.
Pick a shared backend (each link has a remote-worker enqueue-binary example):
| Backend | Feature / crate | Enqueue example |
|---|---|---|
SQLite | sqlite | SqliteQueueBackend — enqueue |
| Postgres | postgres | PostgresQueueBackend — enqueue |
| Redis | boson-backend-redis | Redis — enqueue |
| NATS | boson-backend-nats | NATS — enqueue |
SQLite sketch (same pattern on every backend page above):
use std::sync::Arc;
use boson::{
configure, Boson, JsonExecutionContextFactory, SqliteQueueBackend,
};
let path = std::env::var("BOSON_SQLITE_PATH").unwrap_or_else(|_| "/tmp/boson-remote.db".into());
let backend = SqliteQueueBackend::new(&path).await?;
let boson = Boson::builder()
.queue_backend(Arc::new(backend))
.execution_context_factory(JsonExecutionContextFactory)
.auto_registry() // descriptors for send_with — no claim loop
.without_worker()
.build()?;
configure(boson);
// Greet::send_with(...).await?; // same API as embeddedAPI detail: BosonBuilder::without_worker, BosonBuilder::auto_registry, configure.
§Worker binary
A different binary owns the drain loop. Link every crate that defines #[task] handlers
(use my_tasks as _;) so inventory discovery works.
Pick the same shared backend (each link has a remote-worker worker-binary example):
| Backend | Feature / crate | Worker example |
|---|---|---|
SQLite | sqlite | SqliteQueueBackend — worker |
| Postgres | postgres | PostgresQueueBackend — worker |
| Redis | boson-backend-redis | Redis — worker |
| NATS | boson-backend-nats | NATS — worker |
SQLite sketch (same pattern on every backend page above):
use std::sync::Arc;
use boson::{Boson, JsonExecutionContextFactory, SqliteQueueBackend};
let path = std::env::var("BOSON_SQLITE_PATH").unwrap_or_else(|_| "/tmp/boson-remote.db".into());
let backend = SqliteQueueBackend::new(&path).await?;
let _boson = Boson::builder()
.queue_backend(Arc::new(backend))
.execution_context_factory(JsonExecutionContextFactory)
.worker_id(std::env::var("BOSON_WORKER_ID").unwrap_or_else(|_| "worker-1".into()))
.lease_ttl_secs(30) // required when multiple processes share the backend
.auto_registry()
.build()?; // background claim + dispatch loop
// keep the process alive (await shutdown, serve health, …)API detail: BosonBuilder::worker_id, BosonBuilder::lease_ttl_secs, WorkerSettings,
BosonBuilder::auto_registry.
§Run both
- Start the worker (so claims are ready).
- Start one or more enqueue hosts.
- Each worker process needs a unique
BOSON_WORKER_ID(or builderworker_id) and a positive lease TTL (BOSON_LEASE_TTL_SECSorlease_ttl_secs).
Runnable (local SQLite): remote_worker, remote_enqueue. Shared durable: postgres_worker,
postgres_enqueue. Full multi-terminal runbooks: crate README How to run examples.
export BOSON_SQLITE_PATH=/tmp/boson-remote.db
# Terminal 1 — worker (Ctrl-C to stop; or BOSON_WORKER_RUN_SECS=5 for smoke)
cargo run -p uf-boson --example remote_worker --features sqlite
# Terminal 2 — enqueue
cargo run -p uf-boson --example remote_enqueue --features sqlite§3. Define tasks
When the worker is already booted (embedded) or the worker binary discovers inventory (remote worker), adding a handler is the macro plus an enqueue call:
use boson::{task, ExecutionContext};
#[task(name = "notify")]
async fn notify(ctx: Box<dyn ExecutionContext>, message: String) -> boson_core::Result<()> {
let _ = (ctx, message);
Ok(())
}
Notify::send_with(
serde_json::json!({"System": {"operation": "notify"}}),
NotifyParams { message: "hello".into() },
)
.await?;Policy attributes (priority, pool, max_attempts, …) are documented on task.
Persisted overrides use TaskConfig.
§4. Choose persistence
Pick from the embedded backend table. Connect examples live on
each backend type. Fleet Redis/NATS: see those crate docs for connect_fleet_from_env and
URL precedence (BOSON_*_POOL_ROUTING → BOSON_*_URLS).
Custom adapters implement QueueBackend (start from MemQueueBackend or the trait’s
How to implement section).
§5. Mount HTTP admin (optional)
With feature axum, nest boson_router at NEST_PATH (/api/boson) using BosonState.
Install a host AdminAuth verifier and set BOSON_REQUIRE_ADMIN_AUTH=1
(or BosonStateBuilder::require_admin_auth)
so production mounts fail closed without a verifier. HTTP enqueue stamps
{"Service":{"name":"boson_api"}} (not System). List limit is hard-capped at 500.
Runnable: cargo run -p uf-boson --example axum_admin --features mem,axum
(BOSON_EXAMPLE_SERVE=1 to listen; set BOSON_ADMIN_TOKEN for the example verifier).
§Prerequisites and gotchas
- Enable the backend feature (or fleet crate) that matches your topology —
memis embedded only. - Remote workers need
lease_ttl_secs > 0and uniqueworker_idvalues. - With leases enabled, workers heartbeat via
extend_leaseduring handlers; set TTL=0 only for single-process labs (heartbeats are skipped). - Do not treat HTTP admin as authenticated unless you installed
AdminAuth(and preferablyBOSON_REQUIRE_ADMIN_AUTH=1). - Worker binaries must link every crate that submits
#[task]inventory. configureis required in any process that calls macrosend_with(including enqueue-only hosts).- Host identity kits that map
actor_json→ privileges must not elevate the HTTP service marker to System; seeSECURITY.md.
§Configuration precedence
| Layer | Resolution order |
|---|---|
| Worker settings | BosonBuilder field → environment variable → hardcoded default |
| Task config at enqueue | Persisted backend config → macro/descriptor defaults |
| Idempotency mode | Per-task override → BosonBuilder::idempotency_mode (default lease-backed) |
| Queue backend | Explicit BosonBuilder::queue_backend → global router |
| Ops log | BosonBuilder::ops_log → NoOpsLog; or ops_log_from_env separately |
| Fleet URLs (Redis/NATS) | BOSON_*_POOL_ROUTING → BOSON_*_URLS |
See WorkerSettings and TaskConfig for field-level defaults.
§Runnable examples
| Example | Topology | Features |
|---|---|---|
task_macro | Embedded (manual drain) | mem |
minimal_enqueue | Embedded | mem |
idempotency_and_rate_limit | Embedded | mem |
axum_admin | Embedded + HTTP admin | mem,axum |
remote_worker / remote_enqueue | Remote worker | sqlite |
postgres_worker / postgres_enqueue | Remote worker | postgres |
cargo run -p uf-boson --example task_macro --features memMulti-terminal recipes: see the crate README How to run examples.
Modules§
- prelude
- Convenient re-exports for application code.
Structs§
- Admin
Auth Error - Rejection from
AdminAuth::authorize. - Allow
AllAdmin Auth - Shared always-allow verifier for local tests (not for production).
- Boson
- Boson work engine — enqueue, admin reads, and worker orchestration.
- Boson
Builder - Builder for
Boson. - Boson
State - Extractable state holding a
Bosonruntime and optional admin auth. - Boson
State Builder - Build
BosonStatewith admin auth and actor overrides. - Console
OpsLog - stderr structured lines (default dev adapter).
- Job
- An enqueued unit of work (one task invocation).
- Json
Execution Context Factory - Default factory that wraps actor JSON in a labeled
ExecutionContext. - Manual
Worker - Manual single-step worker for tests (no background task).
- MemQueue
Backend - Process-local queue backend (not durable).
- NoOps
Log - Zero-cost no-op (benchmark
telemetry=offand minimal CI). - Postgres
Queue Backend - PostgreSQL-backed queue backend.
- Queue
Router - Registry for named queue backends (multi-backend hosts).
- Rate
Limit Policy - Rate limiting for enqueue backpressure.
- Reject
External System Actor - Rejects well-known System-shaped actors on
EnqueueTrust::Externalpaths. - Require
Admin - Extractor that enforces
BosonState::admin_auth/ require-flag before the handler runs. - Retry
Policy - Retry policy for a task.
- Run
- One execution attempt of a job.
- Sqlite
Queue Backend SQLite-backed queue backend.- Static
Token Admin Auth - Header-based verifier: require
x-boson-admin-tokenequal to the configured secret. - Task
Config - Per-task config persisted for admin UI and enqueue defaults.
- Task
Descriptor - Descriptor for a registered task.
- Task
Registry - Registry of tasks discovered via inventory (
auto_discover) orregister. PreferBosonBuilder::auto_registryat boot. - Task
RunStats - Run outcome counts for one task name.
- Worker
Settings - Resolved worker settings for claim, lease, and telemetry labels.
Enums§
- Boson
Error - Errors that can occur in Boson operations.
- Enqueue
Trust - Trust level for an enqueue call site.
- Identity
Error - Identity reconstruction failure at handler boundary.
- JobEnqueue
Disposition - Whether
QueueBackend::enqueue_with_policiesinserted or reused a job. - JobStatus
- Status of a job in the queue.
- RunStatus
- Status of a run.
Constants§
- MAX_
LIST_ LIMIT - Hard maximum rows returned by list endpoints.
- NEST_
PATH - Nest path for the Boson API router (
/api/boson). - REQUIRE_
ADMIN_ AUTH_ ENV - Environment variable: when
1/true/yes, admin routes require a configuredAdminAuth.
Traits§
- Actor
Json Policy - Validates
actor_jsonbefore a job is persisted. - Admin
Auth - Host-supplied verifier for Boson admin HTTP.
- Execution
Context - Opaque execution context for task handlers.
- Execution
Context Factory - Builds handler execution context from captured actor JSON at enqueue time.
- OpsLog
- Structured ops metrics and events for enqueue, runs, leases, and runtime health.
- Queue
Backend - Stable async trait for queue persistence (jobs, runs, config, leases).
Functions§
- boson_
router - Create the Boson API router (mount at
NEST_PATH). - configure
- Install the process-wide default
Bosoninstance. - default
- Return the configured default
Bosoninstance, if any. - default_
backend_ from_ global - Resolve the default backend from the process-global
QueueRouter. - default_
http_ enqueue_ actor - Default HTTP / external enqueue actor (non-System service marker).
- install_
default_ mem_ backend - Install a new
MemQueueBackendas the process-global default backend. - install_
default_ postgres_ backend - Install a new
PostgresQueueBackendas the process-global default backend. - install_
default_ sqlite_ backend - Install a new
SqliteQueueBackendas the process-global default backend. - install_
isolated_ postgres_ backend - Install postgres backend with an isolated schema for test sessions.
- install_
ops_ log - Install the process-wide ops log (typically at server boot before Boson runtime).
- map_
backend_ connect_ err - Backend connect failure labeled with a redacted endpoint and redacted source text.
- ops_log
- Resolved ops log —
NoOpsLoguntilinstall_ops_log. - ops_
log_ from_ env - Resolve from
BOSON_TELEMETRY(off|console; defaultconsole). - postgres_
test_ url - Resolve postgres URL from env (test preferred, then bench, then default).
- redact_
credentials_ in_ text - Redact
scheme://userinfo@hostsubstrings embedded in free-form error text. - redact_
endpoint - Remove URL userinfo before placing an endpoint in an error or log message.
- require_
admin_ auth_ from_ env - Read
REQUIRE_ADMIN_AUTH_ENV:1,true, oryes(case-insensitive) ⇒ required.
Type Aliases§
- Invoke
Fn - Invokes a registered task with execution context and JSON parameters.
Attribute Macros§
- task
- Background task handler — typed params,
send_withenqueue, and link-time registration.