RatMinimal web framework

Publishing a Rat app

Rat ships a hardened substrate, but a few things have to be wired up per deploy: secrets, TLS, prod mode, and the populators that turn on rate limiting and audit logging. Walk this list top to bottom before any URL goes public.

1. Provision secrets

Two env vars, neither committed

RAT_SESSION_SECRET signs the session cookie and the per-page call manifest. RAT_ENCRYPTION_KEY encrypts model fields marked [encrypted]. Both must be 32+ random bytes and live outside source control; a .env file is fine if .gitignore covers it.

# .env (or your deploy-time env injector)
RAT_SESSION_SECRET=<32 random bytes, base64 or hex>
RAT_ENCRYPTION_KEY=<32 random bytes, base64 or hex>

If either secret ever lands in git history, rotate immediately: a leaked session secret invalidates every existing session on rotation, and a leaked encryption key locks every [encrypted] field until rolled back. Check that git check-ignore site/.env returns the file before the first deploy.

2. Terminate TLS at a reverse proxy

Rat speaks plain HTTP on purpose

The Rat binary listens on plain HTTP. Put Caddy, nginx, or IIS+ARR in front to terminate TLS and forward X-Forwarded-Proto: https; the HSTS header only fires when the request is recognised as secure. See Sample configs for a working Caddyfile.

3. Flip prod mode on

One env var, one settings flag

Prod mode strips error pages, disables hot reload, enables the SSRF guard on outbound http.* calls, and tightens cookie and CSP defaults. The simplest switch is RAT_MODE=prod in the service env; rat start prod also works.

RAT_MODE=prod
RAT_SESSION_SECRET=...
RAT_ENCRYPTION_KEY=...

4. Wire the security populators

Copy from pups/ in this repo

Three pups ship in the canonical site under site/pups/ and copy verbatim into any project. None of them are core: they're middleware on top of the substrate's before[req] / after[req] hooks. Every public deploy wants them.

pups/rate_limit.rat   # token bucket per IP, 429 on exhaust
pups/audit_log.rat    # structured JSON line per request
pups/auth.rat         # register / login / logout flow

Edit each pup's match: field to scope it. The defaults are conservative (rate limit covers every route, audit log covers every API call), and a narrower match: (say, only the auth endpoints) cuts log volume without weakening the gate. For an authenticated surface, add auth.is_authenticated() gates in the page or in a custom middleware service.

If you ship pups/auth.rat, also provision the SMTP env vars consumed by lang/python/mailer.py (SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASS, SMTP_FROM) and set RAT_BASE_URL so the confirmation link inside the email is absolute. Without these the register flow still parses, but the confirmation email never leaves the box and users can't complete signup.

5. Harden the database story

SQLite is fine, with caveats

SQLite handles a read-mostly single-host site easily. Two things to confirm before launch: the database file lives on durable storage (not ephemeral container scratch), and auto_migrate: true is acceptable for your deploy cadence. Auto-migrate is fine on a single host; turn it off and run migrations manually once you have rolling deploys so two instances don't race.

# settings.rat
> database
provider: 'sqlite'
url: 'file:./prod.db'
auto_migrate: true

Backups are out of scope for the framework. Wire a cron job that runs sqlite3 prod.db .backup on a schedule that matches your tolerance for data loss.

6. Strip server fingerprint headers at the edge

Edge layer, not the app

The Rat HTTP layer already suppresses the Server header. A reverse proxy in front of it may re-inject its own fingerprint (IIS+ARR adds X-Powered-By: ARR/3.0, nginx adds Server: nginx/...). Strip these at the proxy with an outbound URL Rewrite rule on IIS, more_clear_headers with nginx-extras, or header_up -Server in Caddy.

7. Allowlist HTTP verbs at the edge

Reject TRACE / PUT / DELETE upstream

Rat dispatches on GET, HEAD, and POST; every other verb returns 404 from the app. That's fine, but pushing the deny up to the proxy is cheaper and cleaner, and TRACE in particular should never reach an application layer. Configure IIS Request Filtering, an nginx limit_except, or a Caddy matcher to allowlist the three verbs Rat actually consumes.

8. Publish security.txt

Two-line disclosure contract

Drop a /.well-known/security.txt under public/ with a contact address and an expiry. It costs nothing and makes responsible disclosure possible for researchers who find something later.

# public/.well-known/security.txt
Contact: mailto:security@your-domain.com
Expires: 2027-01-01T00:00:00Z
Preferred-Languages: en

See also Sample configs · Settings · Databases