Skip to content

Repository files navigation

batter

Batter is the public facade for a native Tokio operational foundation. batter-core owns process lifecycle, startup, finite commands, operation budgets, retry, admission, health/readiness, cleanup, and telemetry. Optional adapters own Axum request/server boundaries, PostgreSQL transaction and snapshot scopes, Runledger lifecycle and atomic enqueue integration, and Runlimit quota-before-work. Application futures, domain errors, SQLx queries and pools, and routers remain native.

It is for Tokio services that need owned shutdown, deadlines, and cleanup. It is not a web framework, DI container, or a replacement for application types.

For coding-agent consumers

Batter's integration APIs are designed for autonomous coding agents. Prefer the canonical, library-driven path so operational invariants follow from ownership, constrained interfaces, validated configuration, and executable checks. Repeated caller obligations to coordinate cancellation, joining, cleanup, deadline relationships, registration, or error retention are design debt, even when documented. Keep application-specific protocols in the application or a supported adapter; agent-only consumption does not call for an opaque DSL, extra abstraction layers, or claims that types prove arbitrary remote effects.

On that canonical path, locally expressible invalid operational states must be unrepresentable through the public API. A documented ordering, nesting, paired call, phase transition, nonempty-input rule, cleanup sequence or exhaustive outcome obligation is not enough when Rust ownership, types or library-owned assembly can enforce it. Every new or materially changed public API receives the ADR-010 invalid-state review.

Examples are consumer contracts. When a lower-level escape hatch is necessary, its documentation must state the obligations it leaves with the caller and must not present it as equivalent to the protected path. Assess proposed changes with independent failure scenarios and fresh-agent implementation or modification tasks; clean reviews or test volume alone do not establish agent usability. Those evaluations are proposed and unexecuted unless this repository records specific evidence. See ADR-010, architecture, and usage.

Recurring invariant failures during example review require the implementation agent to assess the consumed API and report the design concern before continuing dependent repairs.

Status

The Batter packages are version 0.0.1 publication candidates. Their manifests target crates.io, but the source tree alone does not claim that an upload or registry-name reservation has completed. Linux x86_64 and macOS arm64 have execution evidence on Rust 1.94.0 and 1.98.1. Hosted Linux verification and focused macOS jobs passed on both supported toolchains in GitHub Actions run 35580602864 for commit 56814038f2a9cf6a34688ee39cd9f0e433487a1e. The documentation, package-description, and rustdoc refresh in this checkout postdates that run. The full workspace verification now requires Docker for native Runledger PostgreSQL 18 tests. Batter adapter/reference live PostgreSQL evidence remains scoped to the separately recorded runs. See current status.

Platform support

Batter targets Unix backends, including Linux and macOS. Windows is unsupported, and there are no plans to support it. This applies to all workspace packages, examples, tests and tooling. There are no Windows implementation branches or CI targets. Other Unix targets remain unverified. See ADR-007.

Quick start

Network access is required to download dependencies on the first run.

Runledger's five native packages are part of this workspace, imported from master 46b5cd085d011e597de9552dfebbed4c19416453. Local dependencies select one SQLx foundation without sibling checkouts or dependency patches. Runlimit's five native packages are also local, imported from master 12e035dac504a1d348c2058ee7ade8e61f2e7974. See consumer configuration and native Runledger / Runlimit provenance. Runledger remains optional for facade consumers and is absent from the default foundation graph. Releasable packages are prepared for crates.io; the two example packages remain unpublished.

cargo run -p batter --features axum --example http_service
# In a second terminal:
curl -i http://127.0.0.1:3000/live
curl -i http://127.0.0.1:3000/ready
curl -i http://127.0.0.1:3000/work

BATTER_BIND defaults to 127.0.0.1:3000; BATTER_REQUEST_TIMEOUT_MS defaults to 2000; BATTER_BULKHEAD_CAPACITY defaults to 32. BATTER_ENV_FILE may name one literal dotenv file; there is no default .env search. Invalid values fail before bind. See operations. This is an integration example, not a business API: /work simulates a 25 ms read under a concurrency bound, and /fail uses the same application error envelope as middleware failures. SIGINT and SIGTERM trigger shutdown through native Unix signal listeners.

The service awaits running.wait_checked().await? after startup handoff, so a failed task, incomplete cleanup, or coordinator error reaches its exit boundary. Successful completion retains a report for application policy and diagnostics.

Bound an operation without erasing its application error

use batter::operation::OperationContext;
use std::time::Duration;

async fn read_count() -> Result<u64, std::io::Error> {
    Ok(42) // Replace with your dependency; preserve its concrete error type.
}

async fn example() -> Result<(), Box<dyn std::error::Error>> {
    let request = batter::operation::OperationOwner::new(Duration::from_secs(2))?.into_context();
    let count = request.run("accounts.count", |_scope| read_count()).await?;
    assert_eq!(count, 42);
    Ok(())
}

run receives a factory, creates a child context, and cancels that child when execution completes or its future is dropped. It does not join tasks spawned by the callback. Compose request-local futures normally; do not turn them into unowned tokio::spawn calls. See usage for retries and ownership.

Other examples

cargo run -p batter --example worker
cargo run -p batter --example process_owned
cargo run -p batter --example operation_budget
cargo run -p batter --example finite_command
DATABASE_URL='postgres://user:password@localhost/database' \
  cargo run -p batter --features sqlx --example owned_pool
cargo run -p batter --features runlimit-memory,runlimit-axum --example quota_service
DATABASE_URL='postgres://user:password@localhost/database' \
  cargo run -p batter-example-postgres-lifecycle --bin postgres_lifecycle

The postgres_lifecycle example connects to an existing database, probes it with SELECT 1, shows partial-startup cleanup, and registers native pool closure. It does not create/drop databases or migrate a Runledger schema. Every DATABASE_URL command above must target a local test database; never commit real connection secrets.

finite_command uses command::Command to own one native loopback operation and retain cleanup independently of its waiter. Service startup is a different ownership path; see usage and the executable Startup example. The eighth facade example, verification, requires an explicit migration manifest and authority policy; see the SQLx verifier guide.

Use before the registry release

Until the coordinated registry release is visible, use one immutable Git revision or local paths. The TOML below assumes this repository is checked out as batter beside the consumer:

[dependencies]
# Public facade; its default graph contains only the native foundation.
batter = { path = "../batter/crates/batter" }
# Select optional toolkit namespaces explicitly, for example:
# batter = { path = "../batter/crates/batter", features = ["axum"] }
# Standalone Rust 1.94 encryption/MAC leaf, also available through feature
# `at-rest` as `batter::at_rest` with identical types.
batter-at-rest = { path = "../batter/crates/batter-at-rest" }
# Direct foundation implementation, when an adapter or focused consumer needs it.
batter-core = { path = "../batter/crates/batter-core" }
# Add this dependency for the HTTP adapter.
batter-axum = { path = "../batter/crates/batter-axum" }
# Add for owned PostgreSQL transaction/snapshot scopes, verification, and
# explicit low-level connection disposition.
batter-sqlx = { path = "../batter/crates/batter-sqlx" }

[dev-dependencies]
batter-test-support = { path = "../batter/crates/batter-test-support" }

Packages

The default batter graph does not bring in encryption, Axum, SQLx, or test utilities. Adapter APIs are also available from their direct packages. The facade exposes batter::at_rest, batter::axum, batter::sqlx, batter::runledger, batter::runlimit, and batter::test_support through additive opt-in features; runlimit-memory, runlimit-postgres, runlimit-axum, and sqlx-test-support select only their documented bridges. Each package declares its own version and Rust minimum. The eight Batter packages are 0.0.1, native Runledger is 0.13.0, and native Runlimit is 0.4.0. All retain Rust 1.94 as their minimum. The standalone batter-at-rest leaf verifies that minimum independently. The default toolchain is 1.98.1. SQLx 0.9.0 sets that floor in the adapter and examples; extracting it does not establish a lower library minimum.

Package Location Job
batter crates/batter Source-compatible public facade and runnable foundation consumers.
batter-at-rest crates/batter-at-rest MIT-licensed synchronous standalone envelope encryption and stable MAC keys.
batter-core crates/batter-core Single native implementation for process ownership, deadlines, retry, admission, cleanup, health/readiness, startup, settings, and telemetry.
batter-axum crates/batter-axum HTTP adapter: request policy, observation, correlation, readiness, browser credential transport, and native serving.
batter-sqlx crates/batter-sqlx Owned PostgreSQL transaction and read-only snapshot scopes, schema verification, explicit low-level connection disposition, and opt-in fixtures.
batter-runledger crates/batter-runledger Native initialization and settlement plus a phase-scoped atomic enqueue runner and schema snapshots built on batter-sqlx.
batter-runlimit crates/batter-runlimit Optional native atomic quota-before-work execution and protected authenticated HTTP assembly.
batter-test-support crates/batter-test-support Generic test utilities; independent of the foundation and adapters.
runlimit-core runlimit/runlimit-core Native validated policies, subject keys and decisions.
runlimit-memory runlimit/runlimit-memory Native bounded process-local quota and attempt storage.
runlimit-postgres runlimit/runlimit-postgres Native replica-safe persistence, migrations and maintenance.
runlimit-http runlimit/runlimit-http Native framework-neutral response metadata.
runlimit-axum runlimit/runlimit-axum Native caller-controlled admission layer.
runledger-core runledger/runledger-core Durable job/workflow types and validation.
runledger-postgres runledger/runledger-postgres Native persistence using the workspace SQLx foundation.
runledger-runtime runledger/runledger-runtime Native workers, scheduling and descendant supervision.
runledger-test-support runledger/runledger-test-support Native PostgreSQL 18 container-backed tests.
runledger-tui runledger/runledger-tui Separate read-only operator binary.
batter-example-postgres-lifecycle examples/postgres-lifecycle Native SQLx composition; an executable, not a library API.
batter-example-reference-service examples/reference-service Atomic authenticated delivery command, provider-effect reconciliation, explicit direct-peer/request correlation, pinned compatibility probes, validated constructors, and an explicit live test target.

PostgreSQL provisioning stays in the external postgres-test-harness repository; it is not a workspace member. The optional batter-sqlx/test-support feature is selected by reference tests; the default adapter graph excludes the harness. The optional batter-runledger adapter owns native initialization and settlement. The reference uses its intent-to-queue run_atomic API, built on Batter-owned SQLx scopes, and registers one application-owned provider-effect handler. Its selected loopback-test protocol uses a stable key, canonical payload matching, lookup reconciliation and an explicit 24-hour retention boundary; this is not an exactly-once or arbitrary provider guarantee. batter-runlimit preserves native quota decisions and consumption certainty under an operation budget; optional HTTP assembly owns auth/quota/body ordering. Its memory, postgres, and axum features are independent and off by default. It does not own PostgreSQL initialization or maintenance. Ownership boundaries are in integrations; delivery tasks live in the Beads backlog. The compatibility manifest records the reference package's compiled graph and executed live probes. It is not a complete durable service.

Non-negotiable limits

  • A timeout or cancellation drops a future. It does not roll back an external effect or prove a write failed. Nothing here supplies exactly-once effects.
  • The supervisor owns registered critical tasks and admitted finite work. A finite task initiates drain only by returning Err(Fatal(error)); ? on a plain application error does not compile there, and expected business rejections belong in the success value. Dropping a receipt does not stop work or release its permit.
  • Task abortion is not preemption. Joining a server wrapper does not prove detached children stopped. After a panic, requested abort, or unjoined direct task, dependent finalizers are skipped and the report is unsuccessful.
  • Cleanup is explicitly awaited. It is not asynchronous Drop, general cancellation shielding, or a promise to survive SIGKILL. The owned driver continues shutdown when a waiter is cancelled, provided the runtime remains alive.
  • The Axum boundary ends when a response is constructed. Streaming bodies and WebSockets require a separate lifetime design.

These are API contracts and limitations, not footnotes. Read guarantees before putting side effects behind a boundary.

Documentation

To use batter: usage, operations, architecture, guarantees, and security. Crate READMEs in the table above own adapter-level detail.

To work in this repository: AGENTS.md for reading order, verification, and change rules; status and testing for coverage; Beads for delivery tasks. The Effect v4 brief and reconciliation record design rationale. Primary references record upstream checks.

bash scripts/verify.sh
# For planned work, reuse passing checks and attach receipts to the plan:
bash scripts/verify.sh --plan-id <id>

Both forms use Jig's complete verification profile: tests, both Clippy feature configurations, formatting, rustdoc, five built HTTP smokes and repository policy. Choose one form; a successful profile does not need a second full test run.

Local verification uses the pinned current release once. CI verifies both that release and exact Rust 1.94.0; a weekly CI run checks floating stable. Update rust-toolchain.toml and the pinned CI entries together when adopting a new release.

Checks preserve Cargo.lock. Use bash scripts/verify.sh --bootstrap only when formatting sources and generating a missing lockfile is intended. Repair compiler, lint, and test failures without weakening the documented contracts.

License

MIT; see LICENSE. The standalone batter-at-rest package carries a crate-local copy of the same license so detached Cargo packages include it. Registry publication remains a separate decision.

About

Native operational foundation for Rust/Tokio: owned lifecycle, deadlines, retries, Axum boundaries, PostgreSQL transaction scopes, Runledger, and Runlimit.

Topics

Resources

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages