# Caddy v2 reverse proxy for llm-multiverse-ui # # Handles: # 1. gRPC-Web → gRPC translation for browser clients # 2. CORS headers for cross-origin gRPC-Web requests # 3. SvelteKit dev server proxy (local development) # # Usage: # caddy run --config Caddyfile # # Environment variables: # ORCHESTRATOR_HOST — gRPC backend (default: localhost:50058) # UI_HOST — SvelteKit dev server (default: localhost:5173) # DOMAIN — domain for TLS (default: localhost) # TLS_MODE — "internal" for self-signed, omit for Let's Encrypt { admin off servers { protocols h1 h2 h2c } } {$DOMAIN:localhost} { tls {$TLS_MODE:internal} # Health check handle /healthz { respond "OK" 200 } # gRPC-Web and gRPC traffic to Orchestrator # Match any request with gRPC or gRPC-Web content types @grpc_web { header Content-Type application/grpc-web* } @grpc { protocol grpc } # CORS preflight for gRPC-Web @cors_preflight { method OPTIONS header Access-Control-Request-Method * } handle @cors_preflight { header Access-Control-Allow-Origin "{$CORS_ORIGIN:*}" header Access-Control-Allow-Methods "POST, OPTIONS" header Access-Control-Allow-Headers "Content-Type, X-Grpc-Web, X-User-Agent, Grpc-Timeout" header Access-Control-Max-Age "86400" respond "" 204 } # gRPC-Web requests → Orchestrator with CORS headers handle @grpc_web { header Access-Control-Allow-Origin "{$CORS_ORIGIN:*}" header Access-Control-Expose-Headers "Grpc-Status, Grpc-Message, Grpc-Status-Details-Bin" reverse_proxy {$ORCHESTRATOR_HOST:localhost:50058} { transport http { versions h2c } } } # Native gRPC requests → Orchestrator handle @grpc { reverse_proxy {$ORCHESTRATOR_HOST:localhost:50058} { transport http { versions h2c } } } # All other traffic → SvelteKit dev server handle { reverse_proxy {$UI_HOST:localhost:5173} } log { output stdout format json } }