name: deploy description: > Deploy, update manifests, and troubleshoot the ambient-ui component. Use when creating or modifying deployment manifests, adding ambient-ui to a new overlay, building images, or debugging cluster issues.
Ambient UI Deployment
Instructions for deploying the ambient-ui component across environments.
User Input
$ARGUMENTS
Architecture
The ambient-ui is a Next.js BFF (Backend-for-Frontend) that handles OIDC authentication as a confidential client, manages server-side sessions, and proxies API requests to the ambient-api-server with JWT relay.
Route (port 443, TLS edge termination)
→ Service (port 3000)
→ Next.js BFF (port 3000)
├── OIDC auth (Authorization Code Flow + PKCE against Red Hat SSO)
├── Server-side session (iron-session, httpOnly cookie)
├── JWT relay to ambient-api-server (Authorization: Bearer <jwt>)
└── ambient-api-server (port 8000, HTTPS, validates JWT against same issuer)
No sidecar proxy. The BFF IS the confidential OIDC client. The browser never receives a raw JWT — only an httpOnly session cookie.
Manifest File Map
| File | Purpose |
|---|---|
components/manifests/base/core/ambient-ui-deployment.yaml |
Base Deployment, ServiceAccount, Service |
components/manifests/overlays/kind/kustomization.yaml |
Kind overlay (Quay images, no auth) |
components/manifests/overlays/kind-local/kustomization.yaml |
Kind-local overlay (localhost images) |
components/ambient-ui/Dockerfile |
Multi-stage Docker build |
.github/workflows/components-build-deploy.yml |
CI build matrix entry |
components/manifests/overlays/hcmais/kustomization.yaml |
HCMAIS overlay (ambient-api namespace, Keycloak SSO) |
Production overlay files exist (ambient-ui-oauth-patch.yaml, etc.) but are
disabled — they used origin-oauth-proxy which can't produce JWTs. See Auth section.
Docker Build
Build context is ./components (not ./components/ambient-ui), because
the Dockerfile references ambient-sdk/ts-sdk as a sibling.
Critical details
SDK dist/ is gitignored. The Dockerfile must build it:
COPY ambient-sdk/ts-sdk /ambient-sdk/ts-sdk RUN cd /ambient-sdk/ts-sdk && npm install --ignore-scripts && npm run buildStandalone output path.
outputFileTracingRoot: ../..in next.config.js resolves to/in Docker (WORKDIR/app), so standalone nests underapp/:cp -r .next/standalone/app/. /app-output/Locally it nests under
components/ambient-ui/. Always verify with:find .next/standalone -name server.js -not -path '*/node_modules/*'Webpack, not Turbopack. The build script is
next build --webpack. Turbopack cannot resolvefile:linked dependencies.OpenShift permissions. The builder stage must run:
chmod -R g=u /app-output && chgrp -R 0 /app-outputThe runner image (Red Hat Hardened Image) is distroless — no shell at runtime.
Local build & test
podman build -t ambient-ui-test -f components/ambient-ui/Dockerfile components/
podman run --rm ambient-ui-test node -e 'require("fs").statSync("/app/server.js"); console.log("server.js found")'
podman run -d --name ambient-ui-test -e HOSTNAME=0.0.0.0 ambient-ui-test
podman exec ambient-ui-test node -e "require('http').get('http://localhost:3000/', r => { console.log(r.statusCode); r.on('data',()=>{}); r.on('end',()=>process.exit(0)); })"
podman stop ambient-ui-test && podman rm ambient-ui-test
Environment Configuration
| Env Var | kind / local | Production |
|---|---|---|
API_SERVER_URL |
http://ambient-api-server:8000 |
https://ambient-api-server:8000 |
NODE_EXTRA_CA_CERTS |
(unset) | /etc/ssl/service-ca/service-ca.crt (from service-ca ConfigMap) |
SSO_ISSUER_URL |
Keycloak realm URL (auto-generated by make dev COMPONENT=ambient-ui) |
https://sso.redhat.com/auth/realms/redhat-external |
SSO_CLIENT_ID |
ambient-frontend (Kind Keycloak) |
OIDC client ID |
SSO_CLIENT_SECRET |
dev-secret-do-not-use-in-prod (Kind Keycloak) |
OIDC client secret (from Secret) |
SESSION_SECRET |
auto-generated from cluster name | Random 32+ byte string (from Secret) |
NEXT_PUBLIC_PREVIEW_ALLOWED_HOSTS |
(unset, defaults localhost:*) |
target domains |
Authentication
Native SSO (target architecture)
The BFF handles OIDC directly using Authorization Code Flow with PKCE:
- User visits ambient-ui → BFF redirects to SSO issuer authorize endpoint
- User authenticates at Red Hat SSO → redirected back with auth code
- BFF exchanges code for tokens (access token = JWT, refresh token, ID token)
- BFF stores tokens in an iron-session encrypted httpOnly cookie
- On API requests, BFF extracts JWT from session and forwards as
Authorization: Bearer - ambient-api-server validates JWT against
sso.redhat.comJWKS endpoint
This is already implemented in:
src/lib/oidc.ts— OIDC discovery, auth URL, code exchange, token refreshsrc/lib/session.ts— iron-session storage, token expiry, auto-refreshsrc/lib/auth.ts—resolveAccessToken()extracts JWT from sessionsrc/app/api/auth/sso/login/route.ts— initiates OIDC flowsrc/app/api/auth/sso/callback/route.ts— handles callback, stores tokenssrc/app/api/auth/sso/logout/route.ts— destroys session
What's needed to enable native SSO
An OIDC confidential client on sso.redhat.com/auth/realms/redhat-external:
- Client protocol:
openid-connect, access type:confidential - Valid redirect URI:
https://<ambient-ui-route>/api/auth/sso/callback - Scopes:
openid,email,profile
Then set the env vars: SSO_ISSUER_URL, SSO_CLIENT_ID, SSO_CLIENT_SECRET,
SESSION_SECRET, and AUTH_MODE=native-sso.
Why origin-oauth-proxy doesn't work
The OpenShift origin-oauth-proxy issues opaque sha256~ tokens, not JWTs.
The ambient-api-server requires Red Hat SSO JWTs (--enable-jwt=true).
Confirmed via source code audit: origin-oauth-proxy has zero JWT support.
The upstream OIDC id_token is decoded for claims and permanently discarded
by the OpenShift OAuth server. No configuration can change this.
Production overlay files for oauth-proxy exist as reference but are disabled.
Prerequisites (Manual Steps)
1. Quay.io repository
quay.io/ambient_code/vteam_ambient_ui with push access for vteamrobbit.
2. OIDC client (REQUIRED for production)
Create on sso.redhat.com/auth/realms/redhat-external:
- Client protocol:
openid-connect, access type:confidential - Redirect URI:
https://<route>/api/auth/sso/callback - Scopes:
openid,email,profile
3. Secrets
oc create secret generic ambient-ui-config \
--from-literal=sso-client-id=<client-id> \
--from-literal=sso-client-secret=<client-secret> \
--from-literal=session-secret="$(openssl rand -base64 32)"
4. Route + TLS
OpenShift Route with TLS edge termination, targeting the ambient-ui Service on port 3000.
Adding ambient-ui to a New Overlay
- Include base:
resources: [../../base](ambient-ui-deployment.yaml is in base/core) - Add image entries for
quay.io/ambient_code/vteam_ambient_ui - Apply environment patches (auth mode, API URL, SSO config)
- If auth: create OIDC client and secrets per Prerequisites
- If OpenShift with HTTPS to ambient-api-server: trust the service-ca bundle. The API server TLS cert is signed by OpenShift service-ca, not the default SA CA. Create a ConfigMap with inject-cabundle annotation, mount it into the ambient-ui container, and set NODE_EXTRA_CA_CERTS=/etc/ssl/service-ca/service-ca.crt
- Add a Route if external access is needed
- Verify:
kustomize build overlays/<target> | grep -A5 "name: ambient-ui"
HCMAIS Environment (ambient-api namespace)
The hcmais overlay deploys to ambient-api namespace on the HCMAIS ROSA cluster.
It includes only: ambient-api-server, ambient-api-server-db, ambient-control-plane,
ambient-ui, and postgresql.
Prerequisites
Keycloak client — create
ambient-uiclient in theambient-coderealm athttps://keycloak-ambient-keycloak.apps.rosa.hcmais01ue1.s9m2.p3.openshiftapps.com/admin:- Client authentication: ON (confidential)
- Root URL:
https://ambient-ui-ambient-api.apps.rosa.hcmais01ue1.s9m2.p3.openshiftapps.com - Valid redirect URIs:
https://ambient-ui-ambient-api.apps.rosa.hcmais01ue1.s9m2.p3.openshiftapps.com/api/auth/sso/callback - Valid post logout redirect URIs:
https://ambient-ui-ambient-api.apps.rosa.hcmais01ue1.s9m2.p3.openshiftapps.com/*
Secrets (create in
ambient-apinamespace):# SSO credentials for ambient-ui oc create secret generic sso-credentials -n ambient-api \ --from-literal=SSO_ISSUER_URL=https://keycloak-ambient-keycloak.apps.rosa.hcmais01ue1.s9m2.p3.openshiftapps.com/realms/ambient-code \ --from-literal=SSO_CLIENT_ID=ambient-ui \ --from-literal=SSO_CLIENT_SECRET=<from-keycloak> \ --from-literal=SESSION_SECRET=$(head -c 32 /dev/urandom | base64 | head -c 32)Deploy:
kustomize build components/manifests/overlays/hcmais | oc apply -n ambient-api -f -Verify:
oc get pods -n ambient-api curl -s https://ambient-ui-ambient-api.apps.rosa.hcmais01ue1.s9m2.p3.openshiftapps.com/api/healthz
Troubleshooting
| Symptom | Fix |
|---|---|
Cannot find module '/app/server.js' |
Copy from .next/standalone/app/ |
Can't resolve 'ambient-sdk' |
Build SDK dist in Dockerfile deps stage |
| Liveness probe 401 | Use /api/healthz, not / |
Client sent HTTP request to HTTPS server |
Use https:// in API_SERVER_URL |
401 text/plain from API server |
Token format mismatch — ensure JWT, not opaque token |
| Turbopack build fails | Use next build --webpack |
Verification
-
kustomize build overlays/<target>valid YAML - Image references consistent across kustomization, CI, Makefile
- Pod starts with 0 restarts
-
/api/healthzreturns 200 - API proxy returns data