| 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 build
-
Standalone output path. outputFileTracingRoot: ../.. in next.config.js
resolves to / in Docker (WORKDIR /app), so standalone nests under app/:
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 resolve file: linked dependencies.
-
OpenShift permissions. The builder stage must run:
chmod -R g=u /app-output && chgrp -R 0 /app-output
The 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.com JWKS endpoint
This is already implemented in:
src/lib/oidc.ts — OIDC discovery, auth URL, code exchange, token refresh
src/lib/session.ts — iron-session storage, token expiry, auto-refresh
src/lib/auth.ts — resolveAccessToken() extracts JWT from session
src/app/api/auth/sso/login/route.ts — initiates OIDC flow
src/app/api/auth/sso/callback/route.ts — handles callback, stores tokens
src/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-ui client in the ambient-code realm at
https://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-api namespace):
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