UI Access

Tiphys'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.

Whichever path you use, somebody has to sign in; see Sign-in for the options.

Sign-in

auth.mode decides who can use Tiphys. The default, builtin, works the moment the pod is up, over a port-forward or an ingress.

ModeWho signs people inAudit showsUse when
builtin (default)Tiphys itself: one admin accountadminSmall teams, trials, any install without SSO
proxyYour authenticating ingress (oauth2-proxy, ALB + Cognito)Each person's SSO identityProduction with SSO and per-person accountability
devNobody; a synthetic userdev@localhostLocal kind clusters only
disabledNobodyanonymousAir-gapped networks that enforce identity elsewhere

Built-in sign-in

On first start Tiphys creates the admin account with a one-time password and writes it next to its database on the state volume (/var/lib/clu/initial-admin-password, readable only inside the pod). Print it with:

kubectl -n tiphys exec tiphys-0 -c backend -- cat /var/lib/clu/initial-admin-password

Reading it takes exec rights in the tiphys namespace, so your cluster's own access control is what protects it. The first sign-in has to set a new password (at least 12 characters), which deletes the file. After that, Account (click your name in the header) changes the password or signs you out.

  • Forgot the password: kubectl -n tiphys exec tiphys-0 -c backend -- python -m ops_agent.reset_admin prints a new one-time password and signs everyone out.

  • Choose the password yourself (GitOps, or persistence.mode=configmap, which has no state volume): put it in a Secret and point the chart at it. The password then always comes from the Secret and can't be changed in the UI; rotate the Secret and restart the pod instead.

    kubectl -n tiphys create secret generic tiphys-admin --from-literal=password='<at least 12 characters>'
    helm upgrade tiphys oci://ghcr.io/intelligant-ai/charts/tiphys -n tiphys \
      --reuse-values --set auth.builtin.existingSecret=tiphys-admin
    
  • How long a sign-in lasts: auth.builtin.sessionTtlHours (default 168, one week). The session cookie is HttpOnly and SameSite=Strict, and Secure whenever the request arrived over HTTPS (including TLS ended at your ingress).

  • Five wrong passwords in a row lock the account for 30 seconds, doubling with each further miss up to 15 minutes.

SSO through your ingress

Set auth.mode=proxy and put Tiphys behind an ingress that signs people in and forwards X-Forwarded-User / X-Forwarded-Email (examples below). Every audit entry and approval then names the person. A request that arrives without those headers, including a plain port-forward, is refused, and the UI explains why instead of loading.

Port-forward (default)

kubectl port-forward -n tiphys svc/tiphys 8080:8080

Open http://localhost:8080 and sign in (see Sign-in). 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 Tiphys via ingress is overkill.

Ingress (production)

Tiphys 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: tiphys
  namespace: tiphys
  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: tiphys
                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: tiphys
  namespace: tiphys
  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: tiphys
                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 Tiphys 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: tiphys
  namespace: tiphys
  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: tiphys
                port:
                  number: 8080

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

Either way, set auth.mode=proxy so Tiphys records each person's SSO identity. If you leave the default builtin, the ingress still works, but people sign in twice (SSO, then Tiphys's admin) and the audit log shows admin for everyone.

Multi-cluster considerations

Tiphys is namespace-scoped to tiphys in each cluster where it runs. Multi-cluster environments install one Tiphys 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 Tiphys 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 Tiphys 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.