TLS, ingress & networking
The backend serves plain HTTP and assumes you terminate TLS at the edge. What to expose, the WebSocket paths that must survive your proxy, and the one direction traffic flows between clusters.
Exo's backend is a single HTTP server with the UI embedded — there's no separate web tier and no built-in TLS. You front it with whatever ingress your platform already trusts. The only non-obvious requirement is that two paths carry long-lived WebSockets, so your proxy must allow upgrades.
Ports
1Platform (exo-app)2 9092/tcp HTTP — UI, dashboard API, Exo Operator WebSocket, shell channel3 4agentkube-manager (agentkube-system)5 443 → 9443 webhook (admission reviews; TLS, cert self-bootstrapped)6 8080 metrics (Prometheus /metrics)7 8081 probes (/healthz, /readyz)8 8082 invoke (agent invocation API; in-cluster or via apiserver proxy)The platform Service is a ClusterIP on 9092; the manager Service exposes the four ports above inside agentkube-system.
TLS & ingress
Terminate TLS at your ingress controller and forward to the exo-app ClusterIP on 9092. The backend reads EXO_BASE_URL for the public origin it puts in email links and OAuth callbacks, so set it to your HTTPS hostname.
1apiVersion: networking.k8s.io/v12kind: Ingress3metadata:4 name: exo5 namespace: exo6 annotations:7 cert-manager.io/cluster-issuer: letsencrypt8 nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"9spec:10 ingressClassName: nginx11 tls:12 - hosts: [exo.example.com]13 secretName: exo-tls14 rules:15 - host: exo.example.com16 http:17 paths:18 - path: /19 pathType: Prefix20 backend:21 service:22 name: exo-app23 port: { number: 9092 }WebSockets
Exo Operator egress
When the operator runs in managed mode it opens one outbound WebSocket from the operator's cluster to the platform at wss://<EXO_BASE_URL>/api/agentkube/connect, authenticated with the deployment token. No inbound ports are opened on the operator's cluster — only egress to your platform host on 443 is required. This is what makes air-gapped and tightly-firewalled clusters straightforward. See Connecting to the control plane.
CORS
The API responds with permissive CORS (Allow-Origin: *; methods GET/POST/PUT/PATCH/DELETE/OPTIONS; headers including Authorization and X-Tenant-ID). If you put the API behind a gateway that injects its own CORS, make sure you don't double up the headers.