arcature 2026.1.0

Arcature application framework: a high-level Application facade over the certified Arcature subsystems, with the low-level Axum/Tower escape hatch preserved.
Documentation
//! Arcature — one full-stack web framework over the certified Rust ecosystem.
//!
//! Arcature is a high-level application framework that composes the certified
//! Arcature subsystem crates (Inertia, database, auth, cache, storage, mail,
//! jobs, pages, observability, API) behind a single facade, while preserving a
//! guaranteed escape hatch down to the raw Axum/Tower/Tokio ecosystem
//! underneath.
//!
//! # Getting started
//!
//! A normal application depends on the `arcature` crate with the capabilities
//! it needs, and drives the framework through [`Application`]. The simplest
//! stateless app — compiles with no features beyond the kernel:
//!
//! ```no_run
//! use arcature::prelude::*;
//!
//! async fn hello() -> &'static str {
//!     "hello"
//! }
//!
//! # // `serve` is generic over `axum::serve::Listener`; an expert user brings
//! # // their own runtime. The `macros` feature's `run()` helper binds a
//! # // TcpListener for the common case (shown in the ignore block below).
//! ```
//!
//! With the `macros` feature, `#[arcature::main]` and `Application::run()`
//! remove the need for a direct `tokio` dependency:
//!
//! ```ignore
//! use arcature::prelude::*;
//!
//! async fn hello() -> &'static str {
//!     "hello"
//! }
//!
//! #[arcature::main]
//! async fn main() -> Result<()> {
//!     Application::new()
//!         .routes(Routes::new().route("/", get(hello)))
//!         .run()
//!         .await
//! }
//! ```
//!
//! `Application` is the high-level composition root: it owns framework
//! lifecycle, request-pipeline assembly, and subsystem coordination. It sits
//! *above* the low-level [`App`] kernel, which remains the raw Axum-compatible
//! seam for expert use.
//!
//! # The two layers
//!
//! ```text
//! Application   — high-level framework facade (lifecycle, composition)
//!     |
//! App<S>        — low-level HTTP kernel (raw Axum/Tower interop)
//!     |
//! Axum / Tower  — the upstream ecosystem
//! ```
//!
//! - **[`Application`]** — the normal user entry point. Owns routing
//!   assembly, the request pipeline (pre-routing proxy, post-routing
//!   middleware, fallback pages), and — when subsystem features are enabled —
//!   deterministic startup and graceful shutdown of database, cache, storage,
//!   mail, jobs, and observability.
//! - **[`App`]** — the low-level HTTP kernel. A thin wrapper over
//!   [`axum::Router`] that forwards to `axum::serve`. Use it directly when you
//!   want raw Axum/Tower with no framework opinions.
//!
//! # Features
//!
//! The facade re-exports each certified subsystem behind a Cargo feature, so a
//! minimal application pulls no heavyweight runtime:
//!
//! ```toml
//! [dependencies]
//! arcature = { version = "2026.1.0", features = ["inertia", "db", "pages", "observe"] }
//! ```
//!
//! `default = []` keeps the bare HTTP kernel. `fullstack` enables every runtime
//! subsystem (but never auto-connects to a service — features are compile-time
//! capabilities, not runtime permission). See the crate `Cargo.toml` for the
//! full feature graph.
//!
//! # Advanced: the raw escape hatch
//!
//! Arcature hides complexity without hiding capability. Expert code can still
//! reach Axum, Tower, and every subsystem crate directly:
//!
//! ```no_run
//! use arcature::App;
//! use arcature::axum::routing::get;
//! use arcature::axum::Router;
//!
//! async fn hello() -> &'static str { "hello" }
//!
//! // Build a raw Axum router, wrap it in the kernel, serve it.
//! # #[tokio::main] async fn main() -> std::io::Result<()> {
//! let router: Router = Router::new().route("/", get(hello));
//! # let _ = router;
//! # Ok(()) }
//! ```
//!
//! [`axum::Router`] is re-exported as [`axum`] so downstream code targets the
//! certified version through Arcature. Use [`App::into_router`] /
//! [`App::from_router`] to move between the kernel and a raw router.
//!
//! [Axum]: https://docs.rs/axum

#![forbid(unsafe_code)]
#![doc(html_root_url = "https://docs.rs/arcature")]

// Re-export the certified `axum` crate so downstream code uses the escape
// hatch through Arcature's pinned dependency (e.g. `arcature::axum::routing`).
pub use axum;

// Low-level HTTP kernel (Phase 1) — the raw Axum/Tower seam.
mod app;
mod server;

// High-level Application engine (engine phase) — the composition root.
mod application;
mod facade;
mod pipeline;
pub mod proxy;
mod routing;

// A1: the DX layer runtime contracts (DxComponent trait) and the
// `arcature-dx` proc-macro re-exports. Gated behind the `dx` feature so
// applications opt in to the application programming model. The module is
// private; its public items are re-exported at the crate root below.
#[cfg(feature = "dx")]
mod dx;

// The curated prelude is a public module so applications write
// `use arcature::prelude::*;` (engine spec §19).
pub mod prelude;

pub use app::App;
pub use application::Application;
pub use application::ApplicationBuilder;
pub use application::EngineError;
pub use application::ProxyFn;
pub use application::Result;
// `Resources` is the typed handle bundle passed to the `state_fn` closure of
// `run_with_lifecycle` / `serve_with_lifecycle`. It is re-exported at the
// crate root so application code can name the type (e.g. in helper function
// signatures). Only available when the `macros` feature + at least one
// lifecycle subsystem is enabled.
#[cfg(all(
    feature = "macros",
    any(
        feature = "db",
        feature = "cache",
        feature = "storage",
        feature = "mail",
        feature = "jobs"
    )
))]
#[allow(unused_imports)]
pub use application::Resources;
pub use routing::Routes;
// Re-export the Axum method-routing constructors at the crate root so the
// `routes!` macro expansion can reference them via `::arcature::get`, etc.
// This is the same certified upstream function, forwarded verbatim.
pub use routing::{any, delete, get, head, options, patch, post, put};
// Re-export `from_fn` at the crate root so the `routes!` macro's middleware
// expansion (`::arcature::from_fn(mw)`) resolves. Also useful for
// applications that wire `from_fn` middleware directly.
pub use routing::from_fn;
// Re-export axum's `Redirect` response type at the crate root so the
// `redirect!` macro expansion can reference it via `::arcature::Redirect`.
pub use axum::response::Redirect;

// Facade modules — namespaced re-exports of the certified subsystem crates.
// `facade` is private; its `pub mod` children are re-exported at the crate
// root so a normal application reaches them as `arcature::inertia`,
// `arcature::db`, etc. Each re-export is feature-gated so only enabled
// subsystems appear on the public surface, and no `pub use` fires under
// `--no-default-features` (engine spec §18/§34/§55). Explicit per-feature
// re-exports (rather than a glob) keep the public surface auditable.
#[cfg(feature = "api")]
pub use facade::api;
#[cfg(feature = "auth")]
pub use facade::auth;
#[cfg(feature = "cache")]
pub use facade::cache;
#[cfg(feature = "db")]
pub use facade::db;
#[cfg(feature = "inertia")]
pub use facade::inertia;
#[cfg(feature = "jobs")]
pub use facade::jobs;
#[cfg(feature = "mail")]
pub use facade::mail;
#[cfg(feature = "observe")]
pub use facade::observe;
#[cfg(feature = "pages")]
pub use facade::pages;
#[cfg(feature = "storage")]
pub use facade::storage;

// A1: the Arcature DX layer (ADR-0003). Behind the `dx` feature:
//   - `DxComponent` (type namespace) — the runtime trait that
//     `#[derive(DxComponent)]` generates an impl for.
//   - `DxComponent` (macro namespace) — the derive macro re-exported from
//     `arcature-dx` so applications write `#[derive(arcature::DxComponent)]`
//     without importing `arcature_dx` directly.
// The trait and derive macro share the name `DxComponent` because they
// live in different namespaces (the same pattern as `serde::Serialize` the
// trait and `serde::Serialize` the derive). The `arcature_dx` crate itself
// is also re-exported so attribute macros (when added in later A-phases)
// are reachable as `arcature::arcature_dx::<MacroName>`.
#[cfg(feature = "dx")]
pub use arcature_dx;
#[cfg(feature = "dx")]
pub use arcature_dx::DxComponent;
#[cfg(feature = "dx")]
pub use dx::ApplicationGraph;
#[cfg(feature = "dx")]
pub use dx::ControllerMethod;
#[cfg(feature = "dx")]
pub use dx::DxComponent;
#[cfg(feature = "dx")]
pub use dx::Empty;
#[cfg(feature = "dx")]
pub use dx::GraphError;
#[cfg(all(feature = "dx", feature = "serde"))]
pub use dx::Json;
#[cfg(feature = "dx")]
pub use dx::ModuleDescriptor;
#[cfg(feature = "dx")]
pub use dx::ModuleNode;
#[cfg(all(feature = "dx", feature = "serde"))]
pub use dx::Page;
#[cfg(feature = "dx")]
pub use dx::RouteDescriptor;
#[cfg(feature = "dx")]
pub use dx::RouteMethod;
#[cfg(all(feature = "dx", feature = "api"))]
pub use dx::Validated;

// A7: the route model binding contract and Bound<T> extractor. Behind
// `dx` + `db` (for the RouteModel trait, which references Db) and `api`
// (for Bound<T>, which produces Problem responses on 404/400/500).
#[cfg(all(feature = "dx", feature = "db", feature = "api"))]
pub use dx::Bound;
#[cfg(all(feature = "dx", feature = "db"))]
pub use dx::DbFromState;
#[cfg(all(feature = "dx", feature = "db"))]
pub use dx::RouteModel;

// A9: the auth/policies/session/flash DX layer (ADR-0004). Behind `dx` +
// `auth` (auth extractors need `tower_sessions::Session` from
// `arcature-auth`). The `#[policy]` macro generates `impl DxComponent`
// only — the developer writes `impl Policy<M>` by hand (the authorization
// logic is business behavior the macro must not guess or hide).
//
// `AuthUser` / `UserLoader` are the application identity contracts — the
// application implements them for its user type. `Auth<U>`,
// `OptionalAuth<U>`, and `AuthManager<U>` are genuine Axum
// `FromRequestParts` extractors. `Session` and `Flash` are ergonomic
// wrappers over `tower_sessions::Session`. `Policy<M>` is the explicit
// authorization trait. `AuthzError` is the typed 403 error.
#[cfg(all(feature = "dx", feature = "auth"))]
pub use arcature_dx::policy;
#[cfg(all(feature = "dx", feature = "auth"))]
pub use dx::{Auth, AuthError, AuthManager, AuthUser, AuthzError, LoginBuilder, OptionalAuth};
#[cfg(all(feature = "dx", feature = "auth"))]
pub use dx::{Flash, FlashError, FlashLevel, FlashMessage};
#[cfg(all(feature = "dx", feature = "auth"))]
pub use dx::{Policy, Session, SessionError, UserLoader};

// A10: the middleware attribute macro and error-mapping layer. `#[middleware]`
// validates a function signature (`pub`, `async`, return type present) and
// passes it through unchanged so it remains a genuine Axum `from_fn`
// middleware. `ErrorMapFn` is the type-erased response-mapping function
// installed via `Application::error_mapping(...)`. Behind `dx`.
#[cfg(feature = "dx")]
pub use arcature_dx::middleware;
#[cfg(feature = "dx")]
pub use pipeline::error_mapping::ErrorMapFn;

// A11: the event/listener DX layer. `#[derive(Event)]` generates
// `impl DxComponent` + `impl Event` for typed application events. `#[listener(
// Event)]` validates a function signature and generates a `ListenerBinding`
// const for `arc check` inspection. `Dispatcher` is the type-erased in-process
// event dispatcher — listeners run sequentially in registration order. Behind
// `dx` + `serde` (events are type-erased via `serde_json::Value`).
#[cfg(all(feature = "dx", feature = "serde"))]
pub use arcature_dx::Event;
#[cfg(all(feature = "dx", feature = "serde"))]
pub use arcature_dx::listener;
#[cfg(all(feature = "dx", feature = "serde"))]
pub use dx::{DispatchError, Dispatcher, Event, ListenerBinding};

// A12: the jobs/scheduler/commands DX layer. `#[derive(Job)]` generates
// `impl DxComponent` + `impl Job` + a `JobModel` const for typed enqueue.
// `#[job_handler]` validates a function signature and generates a
// `JobBinding` const for `arc check` inspection. `#[command("name")]`
// generates a `CommandBinding` const. `Scheduler` is the managed
// recurring-job enqueuer with `CancellationToken` lifecycle. `CommandRegistry`
// is the type-erased command dispatcher for `arc run`. Behind `dx` + `jobs`
// (and `serde` for the `Job` derive — payloads are type-erased via
// `serde_json::Value`, matching the A11 `Dispatcher` and the `arcature-jobs`
// `Registry` patterns).
#[cfg(all(feature = "dx", feature = "jobs", feature = "serde"))]
pub use arcature_dx::Job;
#[cfg(all(feature = "dx", feature = "jobs"))]
pub use arcature_dx::command;
#[cfg(all(feature = "dx", feature = "jobs"))]
pub use arcature_dx::job_handler;
#[cfg(all(feature = "dx", feature = "jobs", feature = "serde"))]
pub use dx::Job;
#[cfg(all(feature = "dx", feature = "jobs"))]
pub use dx::{
    Command, CommandBinding, CommandError, CommandRegistry, JobBinding, ScheduleBinding,
    ScheduleCadence, Scheduler, SchedulerError,
};

// A14: the testing DX layer. `#[test(app = <expr>)]` wraps an
// `async fn(app: TestApp)` into a `#[tokio::test]` that builds a `TestApp`
// from the caller's router expression — removing the `TestApp::new(...)`
// boilerplate while keeping the test honest (a real socket, a real HTTP
// client). Behind `dx` (the proc-macro re-export). The macro expansion
// references `::arcature_test::` paths that resolve in the downstream crate,
// so the caller must depend on `arcature-test`; `arcature` itself does not
// depend on `arcature-test` (the dependency direction is
// arcature-test → axum, not arcature-test → arcature → axum; §16).
#[cfg(feature = "dx")]
pub use arcature_dx::test;

// return `Json<T>` / `Page<T>` can derive `Serialize` without a direct
// `serde` dependency. This mirrors the prelude re-export (engine spec §44).
#[cfg(feature = "serde")]
pub use serde::{Deserialize, Serialize};

// A5: the certified validator surface at the crate root so `#[request]`
// can generate `#[derive(::validator::Validate)]` and controllers can
// bound on `T: Validate`. The `derive` feature is on, so both the trait
// and the derive macro are accessible through `::arcature::Validate`.
#[cfg(feature = "validation")]
pub use validator::Validate;

// A6: the page and resource DX macros. `#[page("name")]` generates
// `impl ClientData`, `Serialize` derive, and a `PageContract` const.
// `#[resource]` generates `impl ClientData` and `Serialize` derive. Both
// require the `inertia` feature (for `ClientData` / `PropsSchema` /
// `ContractType` / `PageContract`) and the `dx` feature (for the proc-macro
// re-export). `page!` constructs a `Page<T>` with a compile-time
// `ClientData` assertion.
#[cfg(all(feature = "dx", feature = "inertia"))]
pub use arcature_dx::page;
#[cfg(all(feature = "dx", feature = "inertia"))]
pub use arcature_dx::page_macro;
#[cfg(all(feature = "dx", feature = "inertia"))]
pub use arcature_dx::resource;

// A7: the #[route_model] attribute macro. Behind `dx` + `db` (the macro
// generates an `impl RouteModel` that references `::arcature::db::Db` and
// `::arcature::db::sea_orm`).
#[cfg(all(feature = "dx", feature = "db"))]
pub use arcature_dx::route_model;

// A8: the typed services/providers DX layer (ADR-0004). Behind the `dx`
// feature — services and providers are pure composition over application
// state, not tied to any specific subsystem. The `#[service]` macro
// generates `impl DxComponent`, `impl Service`, and `impl Resolve<S>`. The
// `#[provider]` macro generates `impl DxComponent` (the developer writes
// `impl Provider` by hand — the init logic is business behavior).
//
// `Resolve<S>` is the typed resolution trait (no runtime container). `Db`
// gets a `Resolve<S>` impl behind `dx` + `db` (via the existing
// `DbFromState<S>`). Other resources get manual one-line impls from the
// application. `Inject<T>` is the Axum extractor that calls `T::resolve`.
#[cfg(feature = "dx")]
pub use arcature_dx::provider;
#[cfg(feature = "dx")]
pub use arcature_dx::service;
#[cfg(feature = "dx")]
pub use dx::Inject;
#[cfg(feature = "dx")]
pub use dx::Provider;
#[cfg(feature = "dx")]
pub use dx::Resolve;
#[cfg(feature = "dx")]
pub use dx::Service;

// `#[arcature::main]` re-exports the certified Tokio multi-thread runtime macro
// so a normal application needs no direct `tokio` dependency merely for
// `#[tokio::main]` (engine spec §23). Expert users may still bring their own
// runtime and call the async `Application::serve` / `App::serve` APIs without
// this feature.
#[cfg(feature = "macros")]
pub use tokio::main;