# Edge reverse proxy for the Scrabble contour. A single Basic-Auth gate covers # every operator surface under /_gm (the backend-rendered admin console and the # Grafana subpath); the game SPA (/app/, /telegram/) and the Connect edge go to # the gateway; the catch-all — notably the public landing at / — goes to the # static landing container (R3), so stray traffic never reaches the Go edge. # Mirrors ../galaxy-game's /_gm model. # # CADDY_SITE_ADDRESS is ":80" in the test contour (the host caddy terminates TLS # and forwards); set it to a domain in prod (Stage 18) so this caddy does its own # ACME and the contour is self-contained. { admin off # Trust X-Forwarded-For from private-range upstreams so the real client IP survives # (chat moderation + per-IP rate limiting in the gateway). Test contour: the host caddy # (a private IP) is trusted, so its forwarded client IP is preserved. Prod (no host caddy): # clients connect from public IPs, which are NOT trusted, so Caddy uses the real peer — # the same config is correct (and spoof-safe) in both contours (Stage 17). servers { trusted_proxies static private_ranges } } {$CADDY_SITE_ADDRESS::80} { # Operator surfaces under /_gm: a single shared Basic-Auth, then route. @gm path /_gm /_gm/* handle @gm { basic_auth { {$GM_BASICAUTH_USER:gm} {$GM_BASICAUTH_HASH} } # Grafana serves from this sub-path (GF_SERVER_SERVE_FROM_SUB_PATH=true), so # the prefix is forwarded intact, not stripped. handle /_gm/grafana* { reverse_proxy grafana:3000 } # Everything else under /_gm is the backend-rendered admin console. handle { reverse_proxy backend:8080 } } # The game SPA and the Connect edge are served by the gateway. @gateway path /app /app/* /telegram /telegram/* /scrabble.edge.v1.Gateway/* handle @gateway { reverse_proxy gateway:8081 } # Everything else — the public landing at / and any stray path — is static. handle { reverse_proxy landing:80 } }