Skip to content

Commit 43c9534

Browse files
jsell-rhclaudemergify[bot]
authored
feat(ambient-ui): add deploy skill, remove from production overlay (#1622)
## Summary - **Deploy skill** (`skills/ambient-ui/deploy/SKILL.md`): Comprehensive deployment instructions covering Docker build gotchas, manifest file map, environment config, auth architecture, prerequisites (manual steps), adding to new overlays, troubleshooting, and verification checklist. - **Remove from production overlay**: The `origin-oauth-proxy` cannot produce JWTs required by the `ambient-api-server` (`--enable-jwt=true` against Red Hat SSO). Added `$patch: delete` to exclude ambient-ui Deployment, ServiceAccount, and Service from the production kustomize output. Kind overlays (local dev) are unaffected. ## Why The ambient-ui deployment to staging surfaced many issues: SDK dist not built in Docker, standalone output path mismatch, probe 401s behind oauth-proxy, cookie secret size, RBAC for token reviews, HTTP vs HTTPS to API server, and ultimately an incompatible auth token format. The deploy skill captures all of this so future agents don't repeat these mistakes. The production removal prevents broken deploys until an OIDC client on `sso.redhat.com` is provisioned and the sidecar is swapped to `oauth2-proxy`. ## Test plan - [x] `kustomize build overlays/production` — 0 ambient-ui references - [x] `kustomize build overlays/kind` — 10 ambient-ui references (still included) - [x] Skill file exists at `skills/ambient-ui/deploy/SKILL.md` 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Updated production deployment configuration to remove and exclude the ambient-ui component from the deployment. * **Documentation** * Added comprehensive deployment documentation for the ambient-ui component, including architecture, configuration, and troubleshooting guidance. * Updated deployment discovery guide with new step for checking deployment-related documentation. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
1 parent f6efa98 commit 43c9534

4 files changed

Lines changed: 236 additions & 21 deletions

File tree

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
$patch: delete
2+
apiVersion: apps/v1
3+
kind: Deployment
4+
metadata:
5+
name: ambient-ui
6+
---
7+
$patch: delete
8+
apiVersion: v1
9+
kind: ServiceAccount
10+
metadata:
11+
name: ambient-ui
12+
---
13+
$patch: delete
14+
apiVersion: v1
15+
kind: Service
16+
metadata:
17+
name: ambient-ui-service

components/manifests/overlays/production/kustomization.yaml

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ resources:
2020
- operator-config-openshift.yaml
2121
- ldap-config.yaml
2222
- frontend-hpa.yaml
23-
- ambient-ui-route.yaml
24-
- ambient-ui-rbac.yaml
2523

2624
components:
2725
- ../../components/oauth-proxy
@@ -58,17 +56,8 @@ patches:
5856
kind: PersistentVolumeClaim
5957
name: minio-data
6058
version: v1
61-
- path: ambient-ui-oauth-patch.yaml
62-
target:
63-
group: apps
64-
kind: Deployment
65-
name: ambient-ui
66-
version: v1
67-
- path: ambient-ui-service-patch.yaml
68-
target:
69-
kind: Service
70-
name: ambient-ui-service
71-
version: v1
59+
- path: ambient-ui-delete-patch.yaml
60+
7261
# Production images
7362
images:
7463
- name: quay.io/ambient_code/vteam_api_server:latest
@@ -116,9 +105,3 @@ images:
116105
- name: ghcr.io/ambient-code/observability:latest
117106
newName: ghcr.io/ambient-code/observability
118107
newTag: latest
119-
- name: quay.io/ambient_code/vteam_ambient_ui
120-
newName: quay.io/ambient_code/vteam_ambient_ui
121-
newTag: latest
122-
- name: quay.io/ambient_code/vteam_ambient_ui:latest
123-
newName: quay.io/ambient_code/vteam_ambient_ui
124-
newTag: latest
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
---
2+
name: deploy
3+
description: >
4+
Deploy, update manifests, and troubleshoot the ambient-ui component.
5+
Use when creating or modifying deployment manifests, adding ambient-ui
6+
to a new overlay, building images, or debugging cluster issues.
7+
---
8+
9+
# Ambient UI Deployment
10+
11+
Instructions for deploying the ambient-ui component across environments.
12+
13+
## User Input
14+
15+
```text
16+
$ARGUMENTS
17+
```
18+
19+
## Architecture
20+
21+
The ambient-ui is a Next.js BFF (Backend-for-Frontend) that handles OIDC
22+
authentication as a confidential client, manages server-side sessions,
23+
and proxies API requests to the ambient-api-server with JWT relay.
24+
25+
```
26+
Route (port 443, TLS edge termination)
27+
→ Service (port 3000)
28+
→ Next.js BFF (port 3000)
29+
├── OIDC auth (Authorization Code Flow + PKCE against Red Hat SSO)
30+
├── Server-side session (iron-session, httpOnly cookie)
31+
├── JWT relay to ambient-api-server (Authorization: Bearer <jwt>)
32+
└── ambient-api-server (port 8000, HTTPS, validates JWT against same issuer)
33+
```
34+
35+
No sidecar proxy. The BFF IS the confidential OIDC client. The browser
36+
never receives a raw JWT — only an httpOnly session cookie.
37+
38+
In local/kind environments, `AUTH_MODE=none` disables auth entirely.
39+
40+
## Manifest File Map
41+
42+
| File | Purpose |
43+
|------|---------|
44+
| `components/manifests/base/core/ambient-ui-deployment.yaml` | Base Deployment, ServiceAccount, Service |
45+
| `components/manifests/overlays/kind/kustomization.yaml` | Kind overlay (Quay images, no auth) |
46+
| `components/manifests/overlays/kind-local/kustomization.yaml` | Kind-local overlay (localhost images) |
47+
| `components/ambient-ui/Dockerfile` | Multi-stage Docker build |
48+
| `.github/workflows/components-build-deploy.yml` | CI build matrix entry |
49+
50+
Production overlay files exist (`ambient-ui-oauth-patch.yaml`, etc.) but are
51+
disabled — they used origin-oauth-proxy which can't produce JWTs. See Auth section.
52+
53+
## Docker Build
54+
55+
**Build context** is `./components` (not `./components/ambient-ui`), because
56+
the Dockerfile references `ambient-sdk/ts-sdk` as a sibling.
57+
58+
### Critical details
59+
60+
1. **SDK dist/ is gitignored.** The Dockerfile must build it:
61+
```dockerfile
62+
COPY ambient-sdk/ts-sdk /ambient-sdk/ts-sdk
63+
RUN cd /ambient-sdk/ts-sdk && npm install --ignore-scripts && npm run build
64+
```
65+
66+
2. **Standalone output path.** `outputFileTracingRoot: ../..` in next.config.js
67+
resolves to `/` in Docker (WORKDIR `/app`), so standalone nests under `app/`:
68+
```dockerfile
69+
cp -r .next/standalone/app/. /app-output/
70+
```
71+
Locally it nests under `components/ambient-ui/`. Always verify with:
72+
```bash
73+
find .next/standalone -name server.js -not -path '*/node_modules/*'
74+
```
75+
76+
3. **Webpack, not Turbopack.** The build script is `next build --webpack`.
77+
Turbopack cannot resolve `file:` linked dependencies.
78+
79+
4. **OpenShift permissions.** The builder stage must run:
80+
```dockerfile
81+
chmod -R g=u /app-output && chgrp -R 0 /app-output
82+
```
83+
The runner image (Red Hat Hardened Image) is distroless — no shell at runtime.
84+
85+
### Local build & test
86+
87+
```bash
88+
podman build -t ambient-ui-test -f components/ambient-ui/Dockerfile components/
89+
podman run --rm ambient-ui-test node -e 'require("fs").statSync("/app/server.js"); console.log("server.js found")'
90+
podman run -d --name ambient-ui-test -e HOSTNAME=0.0.0.0 ambient-ui-test
91+
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)); })"
92+
podman stop ambient-ui-test && podman rm ambient-ui-test
93+
```
94+
95+
## Environment Configuration
96+
97+
| Env Var | kind / local | Production |
98+
|---------|-------------|------------|
99+
| `AUTH_MODE` | `none` | `native-sso` |
100+
| `API_SERVER_URL` | `http://ambient-api-server:8000` | `https://ambient-api-server:8000` |
101+
| `NODE_EXTRA_CA_CERTS` | (unset) | `/etc/ssl/service-ca/service-ca.crt` (from service-ca ConfigMap) |
102+
| `SSO_ISSUER_URL` | (unset) | `https://sso.redhat.com/auth/realms/redhat-external` |
103+
| `SSO_CLIENT_ID` | (unset) | OIDC client ID |
104+
| `SSO_CLIENT_SECRET` | (unset) | OIDC client secret (from Secret) |
105+
| `SESSION_SECRET` | (unset) | Random 32+ byte string (from Secret) |
106+
| `NEXT_PUBLIC_PREVIEW_ALLOWED_HOSTS` | (unset, defaults `localhost:*`) | target domains |
107+
108+
## Authentication
109+
110+
### Native SSO (target architecture)
111+
112+
The BFF handles OIDC directly using Authorization Code Flow with PKCE:
113+
114+
1. User visits ambient-ui → BFF redirects to SSO issuer authorize endpoint
115+
2. User authenticates at Red Hat SSO → redirected back with auth code
116+
3. BFF exchanges code for tokens (access token = JWT, refresh token, ID token)
117+
4. BFF stores tokens in an iron-session encrypted httpOnly cookie
118+
5. On API requests, BFF extracts JWT from session and forwards as `Authorization: Bearer`
119+
6. ambient-api-server validates JWT against `sso.redhat.com` JWKS endpoint
120+
121+
This is already implemented in:
122+
- `src/lib/oidc.ts` — OIDC discovery, auth URL, code exchange, token refresh
123+
- `src/lib/session.ts` — iron-session storage, token expiry, auto-refresh
124+
- `src/lib/auth.ts``resolveAccessToken()` extracts JWT from session
125+
- `src/app/api/auth/sso/login/route.ts` — initiates OIDC flow
126+
- `src/app/api/auth/sso/callback/route.ts` — handles callback, stores tokens
127+
- `src/app/api/auth/sso/logout/route.ts` — destroys session
128+
129+
### What's needed to enable native SSO
130+
131+
An OIDC confidential client on `sso.redhat.com/auth/realms/redhat-external`:
132+
- Client protocol: `openid-connect`, access type: `confidential`
133+
- Valid redirect URI: `https://<ambient-ui-route>/api/auth/sso/callback`
134+
- Scopes: `openid`, `email`, `profile`
135+
136+
Then set the env vars: `SSO_ISSUER_URL`, `SSO_CLIENT_ID`, `SSO_CLIENT_SECRET`,
137+
`SESSION_SECRET`, and `AUTH_MODE=native-sso`.
138+
139+
### Why origin-oauth-proxy doesn't work
140+
141+
The OpenShift `origin-oauth-proxy` issues opaque `sha256~` tokens, not JWTs.
142+
The ambient-api-server requires Red Hat SSO JWTs (`--enable-jwt=true`).
143+
Confirmed via source code audit: origin-oauth-proxy has zero JWT support.
144+
The upstream OIDC id_token is decoded for claims and permanently discarded
145+
by the OpenShift OAuth server. No configuration can change this.
146+
147+
Production overlay files for oauth-proxy exist as reference but are disabled.
148+
149+
## Prerequisites (Manual Steps)
150+
151+
### 1. Quay.io repository
152+
153+
`quay.io/ambient_code/vteam_ambient_ui` with push access for `vteamrobbit`.
154+
155+
### 2. OIDC client (REQUIRED for production)
156+
157+
Create on `sso.redhat.com/auth/realms/redhat-external`:
158+
- Client protocol: `openid-connect`, access type: `confidential`
159+
- Redirect URI: `https://<route>/api/auth/sso/callback`
160+
- Scopes: `openid`, `email`, `profile`
161+
162+
### 3. Secrets
163+
164+
```bash
165+
oc create secret generic ambient-ui-config \
166+
--from-literal=sso-client-id=<client-id> \
167+
--from-literal=sso-client-secret=<client-secret> \
168+
--from-literal=session-secret="$(openssl rand -base64 32)"
169+
```
170+
171+
### 4. Route + TLS
172+
173+
OpenShift Route with TLS edge termination, targeting the ambient-ui Service on port 3000.
174+
175+
## Adding ambient-ui to a New Overlay
176+
177+
1. Include base: `resources: [../../base]` (ambient-ui-deployment.yaml is in base/core)
178+
2. Add image entries for `quay.io/ambient_code/vteam_ambient_ui`
179+
3. Apply environment patches (auth mode, API URL, SSO config)
180+
4. If auth: create OIDC client and secrets per Prerequisites
181+
5. If OpenShift with HTTPS to ambient-api-server: trust the service-ca bundle.
182+
The API server TLS cert is signed by OpenShift service-ca, not the default
183+
SA CA. Create a ConfigMap with inject-cabundle annotation, mount it into the
184+
ambient-ui container, and set NODE_EXTRA_CA_CERTS=/etc/ssl/service-ca/service-ca.crt
185+
6. Add a Route if external access is needed
186+
7. Verify: `kustomize build overlays/<target> | grep -A5 "name: ambient-ui"`
187+
188+
## Troubleshooting
189+
190+
| Symptom | Fix |
191+
|---------|-----|
192+
| `Cannot find module '/app/server.js'` | Copy from `.next/standalone/app/` |
193+
| `Can't resolve 'ambient-sdk'` | Build SDK dist in Dockerfile deps stage |
194+
| Liveness probe 401 | Use `/api/healthz`, not `/` |
195+
| `Client sent HTTP request to HTTPS server` | Use `https://` in API_SERVER_URL |
196+
| 401 `text/plain` from API server | Token format mismatch — ensure JWT, not opaque token |
197+
| Turbopack build fails | Use `next build --webpack` |
198+
199+
## Verification
200+
201+
- [ ] `kustomize build overlays/<target>` valid YAML
202+
- [ ] Image references consistent across kustomization, CI, Makefile
203+
- [ ] Pod starts with 0 restarts
204+
- [ ] `/api/healthz` returns 200
205+
- [ ] API proxy returns data

skills/discover/SKILL.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,17 @@ ls *.spec.md 2>/dev/null
7070

7171
Read the relevant spec before implementing — code reconciles against specs, not the other way around.
7272

73-
### 4. Check Standards
73+
### 4. Check Deployment Skills
74+
75+
If the work involves deployment, manifests, Docker builds, or cluster operations, check for deployment skills:
76+
77+
```bash
78+
ls skills/deployments/ 2>/dev/null
79+
```
80+
81+
Read the relevant SKILL.md — these contain environment config, prerequisites, and troubleshooting for deploying each component.
82+
83+
### 5. Check Standards
7484

7585
Load the standards for the component you're modifying:
7686

@@ -80,7 +90,7 @@ ls specs/standards/*/
8090

8191
Pick the relevant domain (backend, frontend, control-plane, security, platform).
8292

83-
### 5. Report and Proceed
93+
### 6. Report and Proceed
8494

8595
Summarize what you found:
8696
- Which domains are involved

0 commit comments

Comments
 (0)