{$DOMAIN:localhost} { # Cert provisioning. Driven by env var CADDY_TLS: # - unset (default) → cert-file mode for corporate PKI (rotated by # scripts/ops/agnes-tls-rotate.sh into /data/state/certs/). # - "tls " → Let's Encrypt auto-issue, e.g. "tls ops@example.com" # (used by public-internet deployments). # - "tls internal" → Caddy-managed self-signed cert (lab/dev only, # browser warning on every visit). # # The {$VAR:default} substitution lets one Caddyfile serve all three # regimes without per-deployment forks. Caddyfile parses the substituted # string as a directive, so the value MUST start with `tls `. {$CADDY_TLS:tls /certs/fullchain.pem /certs/privkey.pem} { # Modern TLS only. Caddy default already excludes 1.0/1.1 in # most builds, but pin explicitly so a future Caddy default # change can't silently weaken our posture. protocols tls1.2 tls1.3 } # HSTS: tell compliant browsers to refuse plain-HTTP for this host # for a year. Skipping `preload` so we keep an escape hatch (preload # submission is hard-bound and blocks rollback). Skipping # `includeSubDomains` because we don't control subdomains. header Strict-Transport-Security "max-age=31536000" reverse_proxy app:8000 { # App's uvicorn runs with --proxy-headers, so stamping these # ourselves makes OAuth callback URLs and Set-Cookie Secure # flags resolve to https consistently. X-Forwarded-Host is # also Caddy's default, but pinning it explicitly insures # against future default changes. header_up X-Forwarded-Proto https header_up X-Forwarded-Host {host} } }