# Caddy 2 — single stable RootNode Academy origin, automatic HTTPS (Let's Encrypt) unless # Cloudflare origin certs are used instead (see docs/PRODUCTION_NETWORK_SECURITY.md §8 for the # Cloudflare-vs-direct-LE decision). Rendered by the `platform` Ansible role from # infra/ansible/group_vars/production.yml's `academy_domain`. # # The student/instructor path proxies to the Gateway (platform/gateway) — the sole # student-facing entrypoint, unchanged architectural rule from Phase 2A onward. Nothing here # reaches the Provisioner, Postgres, or Docker TLS directly; those have no public route at all. # Grafana is reachable at /grafana/ but restricted to the management CIDR even though it has # its own login — internal observability tooling shouldn't be internet-facing at all, same # posture as everything else that isn't the student/instructor product surface. {{ academy_domain }} { encode gzip zstd handle_path /grafana/* { @notmanagement not remote_ip {{ management_cidr }} respond @notmanagement 403 reverse_proxy grafana:3000 } # WebSocket terminal traffic (shell-ws) needs the connection upgraded, not buffered — # Caddy's reverse_proxy handles this automatically for Upgrade: websocket requests, no # special directive needed, but flush_interval -1 avoids buffering SSE/long-poll-shaped # responses too. `gateway` is the Docker Compose service name (rn-platform-internal # network), not localhost — Caddy no longer shares host networking with the Gateway in # production, unlike the dev compose file. reverse_proxy gateway:4200 { flush_interval -1 } log { output file /var/log/caddy/access.log { roll_size 100mb roll_keep 7 } } }