UI Access

Clu's frontend is served from a ClusterIP Service on port 8080. Three paths cover every deployment:

  1. Port-forward — local dev, first-install smoke testing.
  2. Ingress — standard production path with your existing ingress controller (ALB / nginx / Traefik).
  3. Ingress + Cognito auth — production with SSO enforcement.

Port-forward (default)

kubectl port-forward -n clu-ops svc/clu-ops-agent 8080:8080

Open http://localhost:8080. Works on every K8s distribution, no additional dependencies. Forward breaks on pod restart — the ./scripts/clu-open.sh helper wraps kubectl port-forward in a reconnecting loop for demos + dev.

Use for first-install smoke, individual-operator debugging, or any environment where exposing Clu via ingress is overkill.

Ingress (production)

Clu works with any ingress controller that supports standard networking.k8s.io/v1 Ingress. Example with ingress-nginx:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: clu-ops-agent
  namespace: clu-ops
  annotations:
    nginx.ingress.kubernetes.io/proxy-body-size: "1m"
    # SSE endpoints need long timeouts for streaming chat.
    nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
spec:
  ingressClassName: nginx
  rules:
    - host: clu.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: clu-ops-agent
                port:
                  number: 8080
  tls:
    - hosts: [clu.example.com]
      secretName: clu-example-tls

For AWS ALB via the AWS Load Balancer Controller:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: clu-ops-agent
  namespace: clu-ops
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internal    # or internet-facing
    alb.ingress.kubernetes.io/target-type: ip
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}]'
    alb.ingress.kubernetes.io/ssl-redirect: "443"
    alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:us-east-1:...
spec:
  rules:
    - host: clu.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: clu-ops-agent
                port:
                  number: 8080

Important — SSE streaming: the /api/query endpoint streams chat responses via Server-Sent Events. Long-lived connections can hit default idle timeouts. Set:

  • nginx: proxy-read-timeout: 3600 + proxy-send-timeout: 3600
  • ALB: alb.ingress.kubernetes.io/load-balancer-attributes: idle_timeout.timeout_seconds=3600
  • Cloudflare / other CDNs: disable caching on /api/*, set origin timeout ≥ 3600s.

Chats feel fine at 60s timeout for short answers; complex diagnostic flows with many tool calls can exceed that.

Putting Clu behind auth is the standard deployment — production installs shouldn't be reachable without SSO. ALB + Cognito example:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: clu-ops-agent
  namespace: clu-ops
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internal
    alb.ingress.kubernetes.io/target-type: ip
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}]'
    alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:us-east-1:...
    alb.ingress.kubernetes.io/auth-type: cognito
    alb.ingress.kubernetes.io/auth-scope: openid
    alb.ingress.kubernetes.io/auth-session-timeout: "28800"
    alb.ingress.kubernetes.io/auth-idp-cognito: |
      {
        "UserPoolArn": "arn:aws:cognito-idp:us-east-1:...:userpool/...",
        "UserPoolClientId": "...",
        "UserPoolDomain": "clu-auth"
      }
    alb.ingress.kubernetes.io/auth-on-unauthenticated-request: authenticate
spec:
  rules:
    - host: clu.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: clu-ops-agent
                port:
                  number: 8080

For nginx + oauth2-proxy, the standard oauth2-proxy sidecar pattern works — Clu doesn't need auth-aware chart templating.

Multi-cluster considerations

Clu is namespace-scoped to clu-ops in each cluster where it runs. Multi-cluster environments install one Clu pod per cluster; there's no cross-cluster federation in v0.0.1. Operators who want one pane of glass across N clusters use N separate ingress hostnames (clu-prod.example.com, clu-staging.example.com) — each Clu instance has full visibility into its own cluster + whatever AWS resources its IRSA role grants access to.

Local-development ingress

For local kind development, the bundled setup script configures extraPortMappings so port 8080 / 8443 on the host reach the ingress-nginx controller inside kind. Combined with the test-cluster setup, the Clu UI is reachable at http://clu.localtest.me:8080 without a port-forward (localtest.me always resolves to 127.0.0.1).

What the frontend needs from the backend

The React SPA makes all API calls relative — there's no hardcoded backend URL. The nginx.conf inside the frontend container reverse-proxies /api to http://localhost:8081 (the backend container in the same pod). Any ingress that terminates at the Service port forwards both / (SPA) and /api/* (backend) through the same path.

No CORS config needed.