Skip to main content

Gateway TLS

Put the gateway behind TLS; plaintext http:// breaks long streams.

The gateway never terminates TLS itself: it speaks plain HTTP on :8787 and sits behind a TLS edge (a reverse proxy, an Ingress, a load balancer, or your platform's router). The rule is the same everywhere:

The one invariant

Set ANYRAY_GATEWAY_PUBLIC_URL to the https:// URL your users reach the gateway at. The gateway reports it to your console, so enrollment links resolve to the secure URL automatically. An http:// value (to anything but a loopback or .local host) is flagged as insecure on the developer enrollment page at app.anyray.ai.

Pick the recipe for where you run it: Docker Compose below, Kubernetes, a managed platform, or a cloud load balancer. Whichever it is, finish by re-pointing users.

Docker Compose

The stack ships a Caddy TLS edge behind the public profile; it provisions and renews a Let's Encrypt certificate automatically. You need a domain with a DNS A record pointing at the host, and ports 80/443 open.

1
Point a domain at the host

Create a DNS A record, e.g. gateway.example.com → your host's public IP.

2
Configure the edge

In .env:

.env
ANYRAY_PUBLIC_DOMAIN=gateway.example.com # Caddy provisions a cert for this
ANYRAY_GATEWAY_PUBLIC_URL=https://gateway.example.com
ANYRAY_GATEWAY_BIND=127.0.0.1 # so :8787 isn't reachable, bypassing TLS
ANYRAY_TRUST_PROXY=true # trust X-Forwarded-* from Caddy
ANYRAY_HSTS=true # emit Strict-Transport-Security, safe once TLS fronts it
3
Start with the public profile
docker compose --profile public up -d

Caddy comes up on :80/:443, gets the certificate, and proxies to the gateway. The console and /admin stay in-network only.

Exposing the console over HTTPS (optional)

By default the console (compose port 3000) speaks plain HTTP and stays in-network; reach it over an SSH tunnel. To let the app.anyray.ai Billing app link your admins straight to it, give the console its own public hostname so the same Caddy edge terminates TLS for it too: create another DNS A record (e.g. console.example.com → the same host IP), then

.env
ANYRAY_CONSOLE_DOMAIN=console.example.com # Caddy provisions a cert for this
ANYRAY_CONSOLE_PUBLIC_URL=https://console.example.com # reported home, so the Billing app links here

Re-run docker compose --profile public up -d and restart the gateway so it re-reports the URL. Leave both unset to keep the console in-network only.

This exposes the admin surface to the internet

The console hostname serves the admin console and the /admin, /public, and /log endpoints, gated by admin auth (ANYRAY_ADMIN_TOKEN, or console SSO if enabled; the gateway hostname keeps 403-ing them). Use a strong admin token, and prefer an SSH tunnel if you don't need the Billing app hand-off.

Kubernetes

Terminate TLS at an Ingress with cert-manager (or your cluster's managed certificate). The gateway runs as a normal Service on :8787; the Ingress is the only thing exposed.

gateway-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: anyray-gateway
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
ingressClassName: nginx
tls:
- hosts: [gateway.example.com]
secretName: anyray-gateway-tls
rules:
- host: gateway.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: anyray-gateway
port: { number: 8787 }

Then set on the gateway Deployment:

ANYRAY_GATEWAY_PUBLIC_URL=https://gateway.example.com
ANYRAY_TRUST_PROXY=true
Keep /admin private

Don't expose /admin/*, /public/*, or /log/* through the public Ingress; keep the console and admin API in-network (a separate internal Ingress or kubectl port-forward). The bundled Caddy edge blocks them by default; on an Ingress you enforce it with path rules.

Managed platforms

Railway, Render, and Fly terminate TLS for you and give the service a public https:// hostname. There's nothing to provision; just don't expose :8787 directly, and set:

ANYRAY_GATEWAY_PUBLIC_URL=https://your-app.up.railway.app
ANYRAY_TRUST_PROXY=true

Cloud load balancers

Terminate TLS at the load balancer with a managed certificate, targeting the gateway on :8787:

  • AWS (ALB). An HTTPS:443 listener with an ACM certificate, forwarding to a target group on the gateway's :8787. Lock the gateway's security group so :8787 only accepts traffic from the ALB.
  • Google Cloud (HTTPS LB). An external HTTPS load balancer with a Google-managed certificate and a backend service pointing at the gateway. Restrict the gateway so only the LB's ranges reach :8787.
ANYRAY_GATEWAY_PUBLIC_URL=https://gateway.example.com
ANYRAY_TRUST_PROXY=true

Re-point users

Re-run anyray-connect --gateway https://gateway.example.com, or share fresh enrollment links. Once the gateway reports its https ANYRAY_GATEWAY_PUBLIC_URL, new links resolve to the secure URL automatically. Once every tool is on https, close off any direct plaintext :8787 exposure.