RatMinimal web framework

Threat model

Rat draws a deliberate line between the framework substrate and per-app code. The substrate handles the protocol-level mechanics (CSRF, session signing, header policy, redirect targets), because those would be unreasonable to redo per app. Everything that depends on the deploy environment (TLS, rate limiting, auth flow, logging) lives in author code or in copyable populators. This page lists both sides, so you know where to look when adding a feature.

What the substrate guarantees

Holds without per-app code

Every Rat app inherits the following at the framework level. None of these require populators or settings flags; they fire on the default request path.

CSRF gate on /__rat__/call
  HMAC-signed token bound to session id, constant-time compare,
  cross-origin rejection on missing or mismatched Origin.

Per-page call manifest
  Each rendered page hashes its allowed call surface (functions + __set targets) under the session secret. Cross-page replay
  and out-of-manifest calls return 403 before the handler runs.

Session cookie hygiene
  __rat_session is HttpOnly, Secure, SameSite=Lax. Server-side
  store backs it; only the signed sid travels.

Session id regeneration
  session.regenerate() rotates the sid in place. pups/auth.rat
  calls it on register, login, and logout to close session
  fixation.

Security headers (prod mode)
  HSTS 2yr + includeSubDomains, X-Frame-Options SAMEORIGIN,
  X-Content-Type-Options nosniff, Referrer-Policy strict-
  origin-when-cross-origin, Permissions-Policy denying camera/
  mic/geolocation, Server header suppressed, baseline CSP.

Outbound HTTP SSRF guard (prod mode)
  http.get/post/put/delete/request refuse loopback, link-local,
  RFC 1918, carrier-grade NAT, and non-http(s) schemes. Each
  redirect hop is re-validated. Final IP is re-resolved at
  dial time to close the DNS-rebinding window.

URL scheme filter on redirect targets
  Page-level redirect << "..." rejects javascript:, data:,
  vbscript:, and other entity-scheme bypasses.

Path normalisation
  /public/../ and percent-encoded traversal variants normalise
  before any handler runs.

XFF trust model
  req.ip honours X-Forwarded-For only when the immediate peer
  is in a trusted-proxy list; direct connections see the raw
  remote address.

HEAD / mirrors GET /
  The default-route redirect honours HEAD without a body, so
  the verb policy stays consistent across canonical paths.

What the author wires up

Substrate cannot do it for you

These belong in the deploy story or in copyable populators. The framework intentionally doesn't ship a hosting template: VISION.md keeps the substrate small, and these decisions are too deploy-specific to bake in.

TLS termination
  Front the binary with Caddy, nginx, or IIS+ARR. Forward
  X-Forwarded-Proto so HSTS engages.

Rate limiting
  pups/rate_limit.rat. Token bucket per IP. Tune cap and
  refill_per_sec; narrow match: for endpoint-specific gates.

Audit logging
  pups/audit_log.rat. JSON line per request, picked up by any
  downstream log collector. Broaden or narrow match: as fits.

Authentication
  pups/auth.rat. Register, login, logout, current_user. Ships
  with timing-equalised user-not-found path and per-account
  exponential lockout. Wire your own routes that delegate to
  auth.* services.

Brute-force defence beyond the populator
  Per-IP token bucket plus per-account lockout cover walk-the-
  list and grind-one-account. If you expect botnet-scale
  credential stuffing, add a CAPTCHA or proof-of-work gate in
  front of login.

Email confirmation
  pups/auth.rat ships an email-confirmation flow on register:
  new and existing emails both return the identical "if this
  email is new, a confirmation link has been sent" payload
  after paying a matching bcrypt cycle, closing the register
  oracle. Wire SMTP_HOST / SMTP_FROM and set RAT_BASE_URL so
  the confirmation link the email carries is absolute.

Secret rotation policy
  Rotating RAT_SESSION_SECRET invalidates every existing
  session; rotating RAT_ENCRYPTION_KEY locks every [encrypted]
  field. Build a runbook before you need it.

Backups
  SQLite .backup on a cron, or whatever your durable store
  recommends. Framework has no opinion.

Verb allowlist at the edge
  Rat dispatches GET / HEAD / POST. The substrate 404s the
  rest; the proxy should 405 them so TRACE never reaches the
  app at all.

CSP nonces
  Prod CSP pins script-src and style-src to a per-request
  nonce; 'unsafe-inline' is gone. The security middleware
  rolls 128 bits per response, the renderer stamps the
  matching nonce on every inline tag it emits, and any
  XSS-injected script that isn't carrying the secret is
  refused. Static builds ('rat build --prod') emit
  inline tags without a nonce - the operator's reverse
  proxy owns CSP in that path.

Eval-free runtime
  'unsafe-eval' is gone too. Reactive expressions and event
  handlers compile to a JSON AST the client runtime walks
  with a small interpreter, not to JS strings run through
  new Function. So even if an injection reaches a reactive
  attribute, there is no eval gate to escalate through - the
  handler/expression interpreter only dispatches the fixed
  opcode set, never arbitrary code.

Out-of-band threats

Not covered by either side

A few classes of risk are outside both the substrate's and the populator's reach. Worth a sentence each so they don't surprise you.

Supply chain
  rat install ships a per-file SHA-256 manifest in rat.lock.
  Re-pin and re-verify when upgrading; don't run the install
  with --ignore-lock in production.

Renderer escaping audit
  Bracket interpolation escapes for the four canonical sinks
  (text, attr, href, inline-script). User-supplied content
  rendered in unusual contexts (SVG, MathML, web components)
  has not been audited end-to-end.

Side-channel timing
  Substrate paths are not constant-time end-to-end. Crypto
  primitives (HMAC compare, bcrypt) are constant-time; routing
  and template evaluation are not. Assume an attacker can
  observe request timing.

Tail subsystem trust
  Python and JS modules under lang/ run with the same trust
  as Rat code. Treat them as part of your code review surface.

See also Publishing a Rat app · Middleware · Settings