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 Mode 2)
- Leases and pools — multi-process coordination via
BosonBuilder::lease_ttl_secsandWorkerSettings - HTTP admin (optional) — nest
boson_routeratNEST_PATHwhen theaxumfeature is enabled
Background jobs without locking you into one queue store.
This crate ships with no default features (default = []). Enable explicitly:
mem—MemQueueBackendfor tests and local Mode 1sqlite—SqliteQueueBackenddurable single-host (or shared-file Mode 2)postgres—PostgresQueueBackendshared durable statetelemetry-console— marker for console ops log (ConsoleOpsLogis always re-exported)axum— HTTP admin API (boson_router,BosonState,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
- Mode 1 — Embedded — one binary enqueues and drains. Start here.
- Mode 2 — Remote worker — API/host processes enqueue; a separate worker binary (or fleet) claims and runs jobs.
After you pick a mode, continue with define tasks (shared by every mode).
§Mode 1 — 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 Mode 2 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.
§Mode 2 — 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 — Mode 2 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 Mode 2 enqueue-binary example):
| Backend | Feature / crate | Mode 2 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 Mode 1API 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 Mode 2 worker-binary example):
| Backend | Feature / crate | Mode 2 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: remote_worker, remote_enqueue
export BOSON_SQLITE_PATH=/tmp/boson-remote.db
cargo run -p uf-boson --example remote_worker --features sqlite &
cargo run -p uf-boson --example remote_enqueue --features sqlite§3. Define tasks
When the worker is already booted (Mode 1) or the worker binary discovers inventory (Mode 2), 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 Mode 1 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:
Runnable: cargo run -p uf-boson --example axum_admin --features mem,axum
(BOSON_EXAMPLE_SERVE=1 to listen).
§Prerequisites and gotchas
- Enable the backend feature (or fleet crate) that matches your topology —
memis Mode 1 only. - Mode 2 workers need
lease_ttl_secs > 0and uniqueworker_idvalues. - Worker binaries must link every crate that submits
#[task]inventory. configureis required in any process that calls macrosend_with(including enqueue-only hosts).
§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 | Mode 1 (manual drain) | mem |
minimal_enqueue | Mode 1 | mem |
idempotency_and_rate_limit | Mode 1 | mem |
axum_admin | Mode 1 + HTTP | mem,axum |
remote_worker | Mode 2 worker | sqlite |
remote_enqueue | Mode 2 enqueue | sqlite |
cargo run -p uf-boson --example task_macro --features memModules§
- prelude
- Convenient re-exports for application code.
Structs§
- Boson
- Boson work engine — enqueue, admin reads, and worker orchestration.
- Boson
Builder - Builder for
Boson. - Boson
State - Extractable state holding a
Bosonruntime. - 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.
- Retry
Policy - Retry policy for a task.
- Run
- One execution attempt of a job.
- Sqlite
Queue Backend SQLite-backed queue backend.- 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.
- 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§
- NEST_
PATH - Nest path for the Boson API router (
/api/boson).
Traits§
- 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. - 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).
- 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).
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.