Imported from bpcakes/batter (
crates/batter-axum/AGENTS.md). Install upstream withnpx skills add bpcakes/batter --skill batter-axum. Copyright stays with the author.
Axum adapter guide
Purpose
Translate the foundation's operational contracts into Axum request and probe behavior. Own validated browser credential transport mechanics while keeping credential meaning, authentication, authorization, CORS, business logic, and wire-envelope policy at the composition root.
Follow the root Unix-only platform policy. Windows support and non-Unix fallbacks are out of scope.
Key entrypoints
src/lib.rscontainsRequestPolicy,observe_http,request_admission,ResponseConstructionBudget,HttpObservationLevel, the combinedrequest_scopecompatibility entry point, probes, and failures.src/browser.rsandsrc/browser/own trusted browser-origin validation, duplicate-aware named-cookie transport, exact mutation-signal checks, and fixed private-response headers. They do not own account/session state, CSRF token protocols, route selection, CORS, or application error rendering.src/observation.rsprivately owns response observation and tracing lifetime; its single internal composition entry has no admission policy.src/correlation.rsowns opt-inoperational_http, generatedCorrelationIdand the standard infrastructure renderer; it composes the existing observer once.src/quota_observation.rsowns bounded facts and a single-take writer foroperational_http_with_quota; its consuming start/finish states prevent terminal facts from being downgraded. Native quota execution belongs in batter-runlimit.src/readiness.rstranslates the foundation's valid readiness decision into HTTP status, response extensions and observation severity.src/serving.rsregisters a bound native listener/router with the supervisor, including opt-in direct TCP peerConnectInfo<SocketAddr>through protected authority.../batter/examples/http_service.rsdemonstrates adoption of these public helpers.http_service/config.rsowns its explicit file/environment settings and configured router capacity; example tests run in normal Cargo discovery.tests/http.rs,tests/telemetry.rs,tests/observation.rsandtests/scoped_dispatch.rscover failures, complete-router observations, middleware placement, and future destruction.tests/operational/covers forged/concurrent IDs, all readiness reasons, native startup/drain and a body surviving wrapper abort.tests/browser.rsandtests/browser/cover the public browser transport matrices, sanitized failures, Set-Cookie append/removal, mutation precedence, and real Axum private-response layer placement.tests/http_lifetime.rsandtests/http_lifetime/own real HTTP/1.1 socket, handler/body, direct-server and cleanup comparisons. They reuse only private workspacetest-support/process/mechanics, never another package's fixtures or self-tests; keep report inspection and later body release separate.tests/http_lifetime_observations.rsandtests/http_lifetime_observations/own real HTTP/1.1 connection/upload/stream/disconnect milestones through shutdown. Both targets usetests/support/http_process.rsfor PID-bound launch and scenario completion, plus the shared Unix watchdog, including under focused Cargo discovery. The observationdriver.rsowns startup/exercise/teardown; both targets use shared complete-phase budgets. Keep report and running ownership outside reconciliation and timeout the exercise future inside its joined task. Terminal report waits and dependent assertions belong to teardown; only the blocked-body abort case exposes a deliberate intermediate observer checkpoint. Retain event/wire wait diagnostics on cancellation without competing private timers; retain capture in the driver and recheck handler-entry counts after terminal shutdown.tests/support/http_graceful.rsobserves the resolved native connection event for both lifetime targets. Keep its current-thread requirement and fail on missing native evidence; the signal producer is not connection acknowledgement.
Edit here for X
Change HTTP policy and rendering here. Change operation/lifecycle semantics in
crates/batter-core. Keep RequestPolicy's combined readiness and deadline contract unless
a separately approved API change calls for decoupling. Update the root HTTP
contract, source map and implemented status, then record executed checks in the owning Bead.
Keep raw durations outside RequestPolicy::new; validation belongs to the opaque
adapter-owned ResponseConstructionBudget witness.
Invariants
The adapter depends on the foundation, never the reverse. Use
batter_core::telemetry::with_current_dispatch inside the async request entrypoint to
retain first-poll capture and protect full future destruction. Keep observation
guards and nested spans inside the wrapped future. Do not duplicate its private
pin/drop implementation. Bound response construction without claiming body
streaming or detached connection-task shutdown. Keep probe routes separate from
guarded business routes. Apply observe_http after assembling routes/fallback;
use request_admission inside it. Nesting observation around request_scope
intentionally emits twice; no request-extension deduplication is provided.
Do not log cause contents or untrusted request fields. operational_http must
replace inbound header, Tower and adapter identities before observation and must
replace inner response IDs. Keep typed server correlation on completion events
when INFO spans are disabled. Construct test dispatches through private
test-support/dispatch.rs; its OFF-filtered inert registration must not enable
macros before the first real dispatcher rebuild or change thread/global selection.
This includes raw subscriber arguments converted implicitly by with_subscriber.
Readiness defaults: Starting/Draining INFO; dependency failures while Ready and
Stopped WARN. Existing status-only probes and Problem JSON remain compatible.
Carry the foundation ReadinessDecision in response extensions; do not recreate
lifecycle/health classification or accept a broad HealthStatus as a failure.
Keep readiness_status and default_readiness_level as the canonical reusable
adapter mappings. The unready payload is named ReadinessUnreadyReason; do not
introduce a ReadinessReason alias or re-export. Pre-cutover extension lookups
must fail loudly rather than compile and miss the new decision extension.
Observation severity overrides are explicit response extensions, independent of
admission. Preserve actual status/outcome and the default WARN for dropped
futures. No application callback belongs in the observation guard's destructor.
Keep sanitized HTTP completion fields on the event independently of span filtering.
The all-targets test gate includes the example's live readiness tests and requires
loopback socket permission. Preserve both enabled-event and filtered-event assertions.
Select execution/event context once at first poll, retaining the HTTP span or its
available application parent. Never discover a fallback parent at Drop or record
HTTP fields into the application span. Handler panics propagate; an unwind before
a response is observed as dropped, with no invented status or logged panic payload.
Browser origins come only from validated operator configuration, never Host or
forwarding headers. Scan every Cookie field and reject target ambiguity. Keep
cookie/url dependency types private. Mutation checks are browser signals, not
authentication or complete CSRF protection. Apply private_response outside
application rejection middleware and inside observe_http; an outer
short-circuit cannot be retroactively decorated.
Common commands
Run from the workspace root:
cargo test -p batter-axum --locked
cargo build -p batter --features axum --example http_service --locked
python3 scripts/smoke_http.py --binary target/debug/examples/http_service
scripts/jig check test