Configure security
Tune Resuma's HTTP hardening from Rust or environment variables.
SecurityConfig snapshot
Live snapshot from this server process (no secrets) — compare with resuma doctor.
RESUMA_ENV— RESUMA_TRUST_PROXY— - rate backend
— - CSRF
— - origin check
—
ServeOptions + SecurityConfig
use resuma::prelude::*;
pub fn serve_options() -> ServeOptions {
ServeOptions {
addr: "127.0.0.1:3000".parse().unwrap(),
security: SecurityConfig {
csrf: true,
origin_check: true,
trust_proxy: true, // Fly.io / nginx
body_limit_bytes: 256 * 1024,
actions_per_minute: 90,
submits_per_minute: 45,
hide_benchmark: true,
production: true,
},
}
}
#[tokio::main]
async fn main() -> std::io::Result<()> {
ResumaApp::new()
.page("/", || page())
.serve(serve_options())
.await
}Environment variables
Local dev needs no env vars — cargo run and resuma dev use secure defaults. For production, SecurityConfig::from_env() reads RESUMA_ENV, RESUMA_TRUST_PROXY, rate limits, and CSP flags.
Full guide: Environment variables — what you need by app type (SSR vs workers), automatic vs manual setup, Fly scaffold, and resuma doctor.
RESUMA_CSRF=1 # default on
RESUMA_ORIGIN_CHECK=1 # default on
RESUMA_BODY_LIMIT=1048576
RESUMA_RATE_ACTIONS=120
RESUMA_RATE_SUBMITS=60
RESUMA_RATE_BACKEND=memory|disk # disk auto when RESUMA_ENV=production
RESUMA_DATA_DIR=/data/resuma # persistent volume for disk rate limits + execBuilt-in rate limiting (no Redis)
Resuma includes per-IP rate limiting for actions, submits, and exec routes. Dev uses memory; production uses {RESUMA_DATA_DIR}/rate-limit/ with file locks — shared across processes on the same volume. For multi-region deploys without a shared disk, add edge rate limiting (nginx, Fly proxy, Cloudflare) instead of an external datastore.
Resuma OS (exec layer)
Worker, queue, graph, scheduler, and webhook routes need RESUMA_EXEC_API_KEY in production (fail-closed without it). See Environment variables · Exec security · Ops & production.
CSP (Qwik-style, stronger defaults)
Like Qwik City's plugin@csp.ts, Resuma sets CSP on every SSR response — but uses a crypto nonce per request (not Date.now()), adds 'strict-dynamic' on scripts, and skips CSP in dev when RESUMA_DEV=1 unless RESUMA_CSP_DEV=1.
Inline <style> / <script> in with_head() get the nonce automatically. Host images on 'self' via public/images/… (see PWA & public/), static_asset, or extend CspConfig::img_src.
FlowApp::new()
.with_head("<style>.hero { color: var(--accent); }</style>")
.serve(FlowServeOptions {
security: SecurityConfig {
csp: CspConfig::production(["https://images.pexels.com"]),
..SecurityConfig::from_env()
},
..FlowServeOptions::default()
})Environment: RESUMA_CSP=0, RESUMA_CSP_IMG_SRC=https://cdn.example.com, RESUMA_CSP_REPORT_ONLY=1. Validate at csp-evaluator.withgoogle.com.
Flow apps
use pages::PagesRegistry; // generated by resuma routes --generate
FlowApp::new()
.auto_pages("src/pages", PagesRegistry)
.serve(FlowServeOptions {
addr: ([0, 0, 0, 0], 3000).into(),
security: SecurityConfig::from_env(),
})
.awaitFly.io
Production deploy guide (Dockerfile, fly.toml, health checks): Docker deploy cookbook. This repo ships a working config at the workspace root — live at resuma-docs.fly.dev.
# fly.toml [env] — minimum for production
RESUMA_ENV = "production"
RESUMA_TRUST_PROXY = "1"
SITE_URL = "https://your-app.fly.dev"
HOST = "0.0.0.0"
PORT = "3000"
[http_service]
force_https = trueSecurity checklist before deploy
RESUMA_ENV=production— sanitized errorsRESUMA_TRUST_PROXY=1— real client IP + HTTPS detectionforce_https = trueon Fly / reverse proxy- Run container as non-root (see Dockerfile)
- Auth + validation on sensitive
#[server]actions — todo example