Skip to main content

Crate boson

Crate boson 

Source
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 handlerstask macro with policy attributes and generated send_with
  • Composable persistence — inject MemQueueBackend, SqliteQueueBackend, PostgresQueueBackend, or fleet backends on BosonBuilder
  • 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_secs and WorkerSettings
  • HTTP admin (optional) — nest boson_router at NEST_PATH when the axum feature is enabled; install AdminAuth and set BOSON_REQUIRE_ADMIN_AUTH=1 for 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 ActorJsonPolicy rejects 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:

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

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 / …
BackendTypeFeature / crateTopologyWhen to use
In-memoryMemQueueBackendmemembedded onlyLocal experiments and tests
SQLiteSqliteQueueBackendsqliteembedded (or remote worker on one host)Durable single host
PostgresPostgresQueueBackendpostgresembedded or remoteShared durable state
RedisRedisQueueBackendboson-backend-redisremote / fleetBroker-backed multi-host
NATSNatsQueueBackendboson-backend-natsremote / fleetBroker-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

PiecePurpose
Shared task crate (recommended)Same #[task] handlers + inventory on the worker
Enqueue binaryBoots Boson without a worker loop; auto_registry so
descriptors exist for send_with; calls configure + send_with
Worker binarySame backend URL/path; auto_registry; unique
worker_id; lease_ttl_secs > 0; build
Shared backendSQLite 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):

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 embedded

API 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):

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

  1. Start the worker (so claims are ready).
  2. Start one or more enqueue hosts.
  3. Each worker process needs a unique BOSON_WORKER_ID (or builder worker_id) and a positive lease TTL (BOSON_LEASE_TTL_SECS or lease_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_ROUTINGBOSON_*_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 — mem is embedded only.
  • Remote workers need lease_ttl_secs > 0 and unique worker_id values.
  • With leases enabled, workers heartbeat via extend_lease during handlers; set TTL=0 only for single-process labs (heartbeats are skipped).
  • Do not treat HTTP admin as authenticated unless you installed AdminAuth (and preferably BOSON_REQUIRE_ADMIN_AUTH=1).
  • Worker binaries must link every crate that submits #[task] inventory.
  • configure is required in any process that calls macro send_with (including enqueue-only hosts).
  • Host identity kits that map actor_json → privileges must not elevate the HTTP service marker to System; see SECURITY.md.

§Configuration precedence

LayerResolution order
Worker settingsBosonBuilder field → environment variable → hardcoded default
Task config at enqueuePersisted backend config → macro/descriptor defaults
Idempotency modePer-task override → BosonBuilder::idempotency_mode (default lease-backed)
Queue backendExplicit BosonBuilder::queue_backend → global router
Ops logBosonBuilder::ops_logNoOpsLog; or ops_log_from_env separately
Fleet URLs (Redis/NATS)BOSON_*_POOL_ROUTINGBOSON_*_URLS

See WorkerSettings and TaskConfig for field-level defaults.

§Runnable examples

ExampleTopologyFeatures
task_macroEmbedded (manual drain)mem
minimal_enqueueEmbeddedmem
idempotency_and_rate_limitEmbeddedmem
axum_adminEmbedded + HTTP adminmem,axum
remote_worker / remote_enqueueRemote workersqlite
postgres_worker / postgres_enqueueRemote workerpostgres
cargo run -p uf-boson --example task_macro --features mem

Multi-terminal recipes: see the crate README How to run examples.

Modules§

prelude
Convenient re-exports for application code.

Structs§

AdminAuthError
Rejection from AdminAuth::authorize.
AllowAllAdminAuth
Shared always-allow verifier for local tests (not for production).
Boson
Boson work engine — enqueue, admin reads, and worker orchestration.
BosonBuilder
Builder for Boson.
BosonState
Extractable state holding a Boson runtime and optional admin auth.
BosonStateBuilder
Build BosonState with admin auth and actor overrides.
ConsoleOpsLog
stderr structured lines (default dev adapter).
Job
An enqueued unit of work (one task invocation).
JsonExecutionContextFactory
Default factory that wraps actor JSON in a labeled ExecutionContext.
ManualWorker
Manual single-step worker for tests (no background task).
MemQueueBackend
Process-local queue backend (not durable).
NoOpsLog
Zero-cost no-op (benchmark telemetry=off and minimal CI).
PostgresQueueBackend
PostgreSQL-backed queue backend.
QueueRouter
Registry for named queue backends (multi-backend hosts).
RateLimitPolicy
Rate limiting for enqueue backpressure.
RejectExternalSystemActor
Rejects well-known System-shaped actors on EnqueueTrust::External paths.
RequireAdmin
Extractor that enforces BosonState::admin_auth / require-flag before the handler runs.
RetryPolicy
Retry policy for a task.
Run
One execution attempt of a job.
SqliteQueueBackend
SQLite-backed queue backend.
StaticTokenAdminAuth
Header-based verifier: require x-boson-admin-token equal to the configured secret.
TaskConfig
Per-task config persisted for admin UI and enqueue defaults.
TaskDescriptor
Descriptor for a registered task.
TaskRegistry
Registry of tasks discovered via inventory (auto_discover) or register. Prefer BosonBuilder::auto_registry at boot.
TaskRunStats
Run outcome counts for one task name.
WorkerSettings
Resolved worker settings for claim, lease, and telemetry labels.

Enums§

BosonError
Errors that can occur in Boson operations.
EnqueueTrust
Trust level for an enqueue call site.
IdentityError
Identity reconstruction failure at handler boundary.
JobEnqueueDisposition
Whether QueueBackend::enqueue_with_policies inserted 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 configured AdminAuth.

Traits§

ActorJsonPolicy
Validates actor_json before a job is persisted.
AdminAuth
Host-supplied verifier for Boson admin HTTP.
ExecutionContext
Opaque execution context for task handlers.
ExecutionContextFactory
Builds handler execution context from captured actor JSON at enqueue time.
OpsLog
Structured ops metrics and events for enqueue, runs, leases, and runtime health.
QueueBackend
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 Boson instance.
default
Return the configured default Boson instance, 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 MemQueueBackend as the process-global default backend.
install_default_postgres_backend
Install a new PostgresQueueBackend as the process-global default backend.
install_default_sqlite_backend
Install a new SqliteQueueBackend as 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 — NoOpsLog until install_ops_log.
ops_log_from_env
Resolve from BOSON_TELEMETRY (off | console; default console).
postgres_test_url
Resolve postgres URL from env (test preferred, then bench, then default).
redact_credentials_in_text
Redact scheme://userinfo@host substrings 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, or yes (case-insensitive) ⇒ required.

Type Aliases§

InvokeFn
Invokes a registered task with execution context and JSON parameters.

Attribute Macros§

task
Background task handler — typed params, send_with enqueue, and link-time registration.