{ email office@linumiq.com on_demand_tls { # SECURITY (R2/F3): closed allow-list authorizer. The edge function returns # 200 only for reserved hosts (apex/app/api) and subdomains registered in # the tunnels table; 403 otherwise. This prevents unbounded on-demand # certificate issuance for arbitrary hostnames. ask http://supabase-edge-functions:9000/check-subdomain } } # SECURITY (R4/F10/W5): baseline response-hardening headers applied to the # LinumIQ-controlled surfaces (apex/app/api). HSTS forces HTTPS for a year and # is safe for first-party hostnames we fully control. (security_headers) { header { Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" X-Content-Type-Options "nosniff" X-Frame-Options "SAMEORIGIN" Referrer-Policy "no-referrer" Permissions-Policy "geolocation=(), microphone=(), camera=()" Cross-Origin-Opener-Policy "same-origin" -Server -X-Powered-By } } # Apex -> dashboard redirect linumiq.net { tls { on_demand } import security_headers redir https://app.linumiq.net{uri} permanent } # Reserved hostname: Next.js dashboard (upstream not yet running in Wave A) app.linumiq.net { tls { on_demand } import security_headers reverse_proxy web:3000 } # Reserved hostname: Supabase API (Kong) api.linumiq.net { tls { on_demand } import security_headers # SECURITY (R2): block machine-only webhook functions from the public edge. # auth-webhook and stripe-webhook are invoked internally over the docker # network (supabase-edge-functions:9000) and must never be callable from the # internet. get-node stays public for the Home Assistant add-on. @blocked_webhooks path /functions/v1/auth-webhook* /functions/v1/stripe-webhook* respond @blocked_webhooks 403 reverse_proxy supabase-kong:8000 } # Wildcard tunnel subdomains -> frps vhost HTTP. Per-name HTTP-01 issued on first hit. # NOTE: only HSTS is injected here; Home Assistant sets its own security headers # (X-Frame-Options, etc.) and we must not override its CSP / framing behaviour. *.linumiq.net { tls { on_demand } header Strict-Transport-Security "max-age=31536000; includeSubDomains" reverse_proxy frps:7080 }