Docs
FrameworkBuilder configuration
Configure FrameworkBuilder, security defaults, static mounts, and response behavior.
FrameworkBuilder turns routes, static services, and hardening layers into the Axum router you run in production.
Use this page when you need the complete setup surface in one place. Use Routing, Responses, Static assets, and Deployment for deeper workflow examples.
Minimal FrameworkBuilder Setup
use axum::{routing::get, Router};
use proa::{HeadConfig, HeadUrlPolicy};
use proa::axum::{FrameworkBuilder, RouteResponse};
use crate::{layouts::RootDocument, pages::HomePage};
async fn home() -> RouteResponse {
RouteResponse::ssr(RootDocument { children: HomePage })
}
let routes = Router::new().route("/", get(home));
let head = HeadConfig::default()
.with_urls(HeadUrlPolicy::new("https://example.com", "/")?);
let app = FrameworkBuilder::new()
.with_head(head)
.with_dynamic_routes(routes)
.build();
RootDocument is the LAYOUT component containing the document markup and head outlet; HomePage declares ROLE = PAGE. See Layouts and Pages for complete implementations. Replace the public origin with trusted application configuration.
Use with_dynamic_routes for an ordinary Axum router. Route specifications are optional: use with_dynamic_routes_and_metadata when you also need their route-mode or other specification metadata. Legacy route titles do not populate the managed head; use render_head or app-wide defaults.
The builder returns an Axum router; bind a TCP listener and pass both to proa::axum::serve() to run it. See the complete application example for route setup, discovery endpoints, and server startup.
FrameworkBuilder Methods
| Method | Use it for |
|---|---|
FrameworkBuilder::new() | Create the builder with secure defaults. No manifest or configuration is required. |
.with_head(head) | Install app-wide managed head configuration and defaults for rendered routes. |
.with_base_path(base_path) | Set the public URL prefix for a mounted application. |
.with_public_origin(origin)? | Set the public HTTP(S) origin used by discovery endpoints. |
.with_sitemap(entries) | Serve one sitemap using the final public origin and base path. |
.with_sitemaps(files) | Serve named sitemap documents plus a sitemap index; accepts results from an awaited async generator. |
.with_robots(robots) | Serve /robots.txt from a document or awaited generator result, with bot-specific groups and automatic sitemap/index links. Behind a prefix-stripping proxy, forward origin-root /robots.txt to this handler. |
.with_rsjs_assets() | Serve the linked RSJS runtimes and generated modules. |
.with_static_service(router) | Mount a router under /static. |
.with_dynamic_routes(router) | Merge an Axum router without a metadata map. |
.with_dynamic_routes_and_metadata(router, metadata) | Merge Proa route specs plus computed route metadata. |
.with_body_limit(bytes) | Override the global request body cap. |
.with_cross_origin_protection(bool) | Toggle browser cross-origin unsafe request protection. |
.with_trusted_origin(origin) | Add an exact trusted origin such as https://admin.example.com. |
.with_security_headers(config) | Configure frame policy, HSTS, and CSP. |
.with_request_timeout(duration) | Override the default request timeout. |
.without_request_timeout() | Disable the timeout layer. Prefer raising the timeout first. |
.with_concurrency_limit(max) | Add a global in-flight request cap. |
.with_catch_panic(bool) | Convert panics into plain 500 responses instead of resetting the connection. |
.build() | Produce the final Axum Router. |
.try_build()? | Build the router while returning invalid discovery configuration as an error. |
FrameworkBuilder Base Paths and RSJS Assets
These methods configure separate parts of the application:
with_base_path(BasePath)sets the public mount used bycx.url(...)and framework-generated URLs. Island query/mutation requests follow it only when declared withcx.url(...)orFetchRequest.apply_base_path: true. Managed static CSS/JS URLs follow it too. Its default is the origin root.with_rsjs_assets()installs routes for the RSJS runtimes and linked modules. Enable it when this app serves its own island assets, including at the origin root. It is opt-in so applications can supply their own asset handlers.
For an app exposed at /apps/demo/ by a prefix-stripping proxy:
use proa::axum::{BasePath, FrameworkBuilder};
let app = FrameworkBuilder::new()
.with_base_path(BasePath::new("/apps/demo")?)
.with_rsjs_assets()
.with_dynamic_routes_and_metadata(routes, metadata)
.build();
Keep routes such as /docs/start and /static/... unprefixed. Compose
application links with cx.url("/docs/start") and handler redirects
with app_url("/"). Pass the original, unprefixed manifest to the builder.
See linked island asset serving for the installed routes and custom-router alternative. The URL prefix deployment guide covers startup environment configuration, proxy behavior, and coverage limits.
FrameworkBuilder Production Defaults
build() applies conservative defaults:
| Default | Value |
|---|---|
| Body limit | 1 MiB through Axum DefaultBodyLimit. |
| Request timeout | 30s for response production. Streaming bodies keep flowing after the response head is produced. |
| Cross-origin protection | On for unsafe browser requests. |
| Security headers | X-Content-Type-Options: nosniff, X-Frame-Options: DENY, and Referrer-Policy: strict-origin-when-cross-origin. |
| Concurrency limit | Off until configured. |
| Panic catching | Off until configured. |
Keep the global body limit small. For uploads, prefer a dedicated nested router with a larger Axum body limit instead of raising the whole app.
FrameworkBuilder Security
Security defaults live on their own pages, because they are policy decisions rather than configuration plumbing:
| Builder call | Covered in |
|---|---|
.with_security_headers(config), .with_strict_csp(), .with_csp_policy(policy) | Headers and CSP |
.with_trusted_origin(origin) | Cross-origin and CSRF |
Cross-origin protection, the 1 MiB body limit, and the 30-second request timeout are applied by build() without configuration. See Security for what Proa hardens and what stays yours.
FrameworkBuilder Static Services
Mount generated assets under /static:
use tower_http::services::ServeDir;
let static_router = axum::Router::new().nest_service("/", ServeDir::new("public"));
let app = FrameworkBuilder::new()
.with_static_service(static_router)
.build();
For public directories, keep untrusted writers out of the served tree. ServeDir rejects .. traversal, but deployments should still treat symlinks and generated files deliberately.
RouteResponse Quick Reference
| Constructor | Use it for |
|---|---|
RouteResponse::ssr(component) | Sync WebRenderSync pages. |
RouteResponse::ssr_async(component) | Async pages that do not use a loader. |
RouteResponse::ssr_async_with_loader(component, loader) | Async pages with a typed loader. |
RouteResponse::ssr_stream(component) | Streaming pages without a loader. |
RouteResponse::ssr_stream_with_loader(component, loader) | Streaming pages with a typed loader. |
Response modifiers:
| Modifier | Applies to |
|---|---|
.with_head(config) / .without_head() | Enable managed metadata for one response, or keep its document in manual mode. |
.with_loader(loader) | Async and streaming responses with typed loaders. |
.with_render_mode(mode) | Async render plans; chunked modes stream, while static and agent modes buffer. |
Use Responses for examples and Data loading and streaming for loader and streaming details.