Boson

Short-lived background work

Boson is a durable Rust background job runtime with typed tasks, retries, rate limits, leases, and pluggable backends for memory, SQLite, PostgreSQL, Redis, and NATS. Enqueue and worker dispatch stay out of ad hoc scripts.

Why this family exists

Boson owns enqueue orchestration, worker dispatch, leases, retries, and task configuration. Your application owns business logic, identity mapping, and backend choice. That split keeps checkout fanout, invoice batches, and similar short-lived work observable and retryable without reinventing a queue per product.

Where it lands

Marketing workload recipes and counter-app demos enqueue Boson tasks for background fanout while the UI stays responsive. They use the same Valence models and durable retries instead of fire-and-forget.
Register tasks with #[task], boot via Boson::builder() with a queue backend and ExecutionContext factory, then enqueue with send_with. Scale from embedded memory to durable stores and remote workers without changing the task surface. The capacity campaign measures completed durable tasks with retries, leases, and persisted run rows on SQLite and Redis. Resources keeps the qualified Redis enqueue and paced SQLite component measurements. Axum-friendly wiring and broker e2e coverage (Postgres, Redis, and NATS) land in CI; see docs.rs/uf-boson for the guided embedded versus remote-worker setup.

In the workspace

Marketing workload recipes and counter-app demos enqueue Boson tasks for background fanout while the UI stays responsive. They use the same Valence models and durable retries instead of fire-and-forget.

Code sample

From the upstream getting-started docs — see also all code recipes on Resources.
Boson taskBoson
Typed #[task] + enqueue
use std::sync::Arc;
use boson::{
    configure, task, Boson, ExecutionContext, JsonExecutionContextFactory,
    MemQueueBackend,
};

#[task(name = "process_order")]
async fn process_order(
    ctx: Box<dyn ExecutionContext>,
    order_id: String,
    amount_cents: u64,
) -> boson_core::Result<()> {
    tracing::info!(actor = ctx.label(), %order_id, amount_cents);
    Ok(())
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let boson = Boson::builder()
        .queue_backend(Arc::new(MemQueueBackend::new()))
        .execution_context_factory(JsonExecutionContextFactory)
        .auto_registry()
        .build()?;
    configure(boson);

    ProcessOrder::send_with(
        serde_json::json!({"System": {"operation": "checkout"}}),
        ProcessOrderParams {
            order_id: "ord-42".into(),
            amount_cents: 9900,
        },
    )
    .await?;
    Ok(())
}