{
    # Administrative API stays inside the website jail; no remote admin listener.
    admin unix//var/run/caddy/caddy.sock
    log default {
        # Proxy error records can contain client-controlled URI/header values.
        exclude http.log.error http.log.access
    }
    servers {
        protocols h1 h2
        max_header_size 16KB
        timeouts {
            read_header 10s
            read_body 20s
            idle 60s
        }
    }
}

veiledchat.io {
    bind 10.77.0.20
    header {
        -Server
        X-Content-Type-Options nosniff
        Referrer-Policy no-referrer
        Content-Security-Policy "default-src 'none'; style-src 'self'; img-src 'self'; base-uri 'none'; frame-ancestors 'none'; form-action 'none'"
        Permissions-Policy "camera=(), microphone=(), geolocation=()"
    }
    @encoded header Content-Encoding *
    @service path /healthz /v1/config /v1/enrollment/begin /v1/enrollment/complete /v1/enrollment/withdraw
    # Preserve rejection order explicitly; Caddy otherwise sorts directives.
    route {
        respond @encoded 415
        handle @service {
            request_body {
                max_size 8KB
            }
            reverse_proxy 10.77.0.10:8080 {
                header_up Veiled-Client-IP {remote_host}
                header_up -Forwarded
                header_up -X-Forwarded-For
                transport http {
                    dial_timeout 3s
                    response_header_timeout 25s
                }
            }
        }
        handle {
            root * /usr/local/www/veiled
            file_server
        }
    }
    # No access log: enrollment numbers/codes/request bodies must not be logged.
}
