Skip to content

Commit 59de1c6

Browse files
committed
spec(projects): define Project preview URL allowlist
1 parent 16bf3e5 commit 59de1c6

3 files changed

Lines changed: 220 additions & 29 deletions

File tree

specs/ambient-ui/ambient-ui.spec.md

Lines changed: 89 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -830,25 +830,63 @@ Sessions with an `ambient-code.io/ui/preview-url` annotation SHALL offer a live
830830

831831
The preview iframe SHALL be hardened:
832832
- The `sandbox` attribute SHALL be set with minimal permissions (`allow-scripts allow-same-origin allow-forms`). Top-level navigation (`allow-top-navigation`) and popups (`allow-popups`) SHALL NOT be granted.
833-
- The UI SHALL validate the preview URL against a configurable allowlist of trusted host patterns (e.g., `*.apps.rosa.example.com`, `*.apps.cluster.local`). URLs not matching the allowlist SHALL be rejected with an error message instead of rendered.
834-
- A Content-Security-Policy `frame-src` directive SHALL restrict the iframe to the allowlisted hosts.
833+
- The UI and BFF preview proxy SHALL validate the preview URL against the active Project's `spec.preview.allowedHosts` policy. URLs not matching the Project allowlist SHALL be rejected with an error message instead of rendered.
834+
- The iframe SHALL load a session-scoped BFF route, for example `/api/projects/{projectId}/sessions/{sessionId}/preview?url=...`. The BFF SHALL fetch the Session server-side, read `ambient-code.io/ui/preview-url`, and accept the requested URL only when it equals that annotation value or carries a server-issued preview continuation token for the same Project, Session, and target URL. Arbitrary `url` parameters outside the Session preview context SHALL be rejected.
835+
- The preview proxy SHALL issue continuation tokens only for URLs discovered from a previously authorized preview response or redirect chain. Continuation tokens SHALL be signed or stored server-side, short-lived, bound to Project and Session, and revalidated against the effective preview policy before use.
836+
- For every proxied request, including redirects and injected-link navigations, the preview proxy SHALL revalidate the target URL against the effective preview policy. Token relay SHALL be recomputed per target host.
837+
- The preview proxy SHALL forward the BFF's server-side SSO access token to the preview target only when the matching Project preview host entry has `forwardAmbientToken: true` and the platform token-relay policy permits that host. Hosts with `forwardAmbientToken: false` SHALL render without Ambient token relay. Custom connection tokens entered through the status-bar connection controls SHALL NOT be forwarded to preview targets.
838+
- A Content-Security-Policy `frame-src` directive SHALL restrict iframes to the same-origin preview proxy and any platform-configured direct-frame hosts. Project-specific preview host trust is enforced server-side by the proxy.
839+
840+
The session-scoped BFF route is required for preview rendering. `GET /api/projects/{projectId}/sessions/{sessionId}/preview?url=...` SHALL be the iframe `src`, fetch Session and Project state server-side, evaluate the requested URL against the Session annotation and the Project preview policy, proxy the authorized response, and return a 4xx response for malformed, untrusted, or out-of-session targets. Direct iframe navigation to the annotation URL SHALL NOT bypass this route. Links and redirects discovered inside a proxied preview SHALL be rewritten through the same route with a continuation token, so every subsequent navigation repeats Session binding, Project allowlist validation, and token-relay evaluation.
841+
842+
Preview policy resolution during rollout SHALL be tri-state:
843+
- If the API/SDK cannot expose Project `spec.preview`, preview trust MAY be read from deployment configuration as a read-only, deployment-scoped fallback.
844+
- If the API/SDK exposes Project `spec.preview` but a Project has no explicit `spec.preview` field, the deployment fallback MAY be used for that Project.
845+
- If a Project has explicit `spec.preview.allowedHosts`, including `[]`, Project policy is authoritative and overrides deployment fallback.
846+
847+
Fallback entries imply `forwardAmbientToken: false`.
835848

836849
#### Scenario: Preview mode activation
837850

838851
- GIVEN a session with `ambient-code.io/ui/preview-url: "https://app.example.com"` and `ambient-code.io/ui/preview-title: "SSO Login v2"`
839-
- AND the URL matches the configured preview host allowlist
852+
- AND the URL matches the active Project's preview host allowlist
840853
- WHEN the user clicks "Open Preview" in the session detail
841854
- THEN a near-fullscreen overlay opens with the URL loaded in a sandboxed iframe
842855
- AND the overlay header shows the preview title, device size toggles (Desktop/Tablet/Mobile), and a Comment button
843856

844857
#### Scenario: Preview URL rejected (untrusted host)
845858

846859
- GIVEN a session with `ambient-code.io/ui/preview-url: "https://evil.example.com"`
847-
- AND the URL does not match the configured preview host allowlist
860+
- AND the URL does not match the active Project's preview host allowlist
848861
- WHEN the user clicks "Open Preview"
849862
- THEN the preview does not render
850863
- AND an error message is displayed: "Preview URL is not on the trusted hosts allowlist"
851864

865+
#### Scenario: Preview renders without Ambient token relay
866+
867+
- GIVEN a session with `ambient-code.io/ui/preview-url: "https://deploy-preview-123.netlify.app"`
868+
- AND the active Project allowlist contains `pattern: "deploy-preview-*.netlify.app"` with `forwardAmbientToken: false`
869+
- WHEN the user opens the preview
870+
- THEN the preview proxy fetches the target without an Ambient `Authorization` header
871+
- AND the preview renders if the target does not require Ambient authentication
872+
873+
#### Scenario: Preview token relay requires explicit trust
874+
875+
- GIVEN a session with `ambient-code.io/ui/preview-url: "https://pr-123.checkout.apps.rosa.example.com"`
876+
- AND the active Project allowlist contains `pattern: "pr-*.checkout.apps.rosa.example.com"` with `forwardAmbientToken: true`
877+
- AND platform token-relay policy permits that host pattern
878+
- WHEN the user opens the preview
879+
- THEN the preview proxy forwards the BFF's server-side SSO access token to the preview target
880+
- AND no custom status-bar connection token is forwarded
881+
882+
#### Scenario: Preview token relay denied by platform policy
883+
884+
- GIVEN a Project preview allowlist entry with `forwardAmbientToken: true`
885+
- AND the host pattern is outside the platform token-relay policy
886+
- WHEN the UI or API attempts to save the Project preview entry
887+
- THEN the save is rejected
888+
- AND the preview target is not treated as trusted for Ambient token relay
889+
852890
#### Scenario: Device size emulation
853891

854892
- GIVEN the preview overlay is open
@@ -966,7 +1004,50 @@ No list-watch endpoint exists for sessions today. Polling is the interim mechani
9661004

9671005
The Settings view SHALL provide project-scoped configuration management with tabbed sections.
9681006

969-
**Tabs:** General (project metadata), Permissions (user/role management), API Keys (key lifecycle), Feature Flags (toggles with confirmation).
1007+
**Tabs:** General (project metadata), Preview (trusted preview hosts), Permissions (user/role management), API Keys (key lifecycle), Feature Flags (toggles with confirmation).
1008+
1009+
### Requirement: Preview Trust Configuration
1010+
1011+
The Settings view SHALL expose the active Project's `spec.preview.allowedHosts` policy. The Preview tab SHALL list trusted host patterns, show whether each pattern forwards the BFF's server-side SSO access token, and allow authorized users to add, edit, or remove entries.
1012+
1013+
The UI SHALL persist Preview tab changes through the Project API by updating Project `spec.preview`. The UI SHALL NOT write preview trust policy to Project labels or annotations.
1014+
1015+
If Project `spec.preview` API/SDK support is not available in a deployed version, the Preview tab SHALL show the effective deployment-scoped fallback policy as read-only or hide editing controls entirely. Editing becomes available only when Project `spec.preview` writes are supported.
1016+
1017+
The UI SHALL render the same logical policy that can be applied by YAML:
1018+
1019+
```yaml
1020+
apiVersion: ambient-code.io/v1alpha1
1021+
kind: Project
1022+
metadata:
1023+
name: checkout
1024+
spec:
1025+
preview:
1026+
allowedHosts:
1027+
- pattern: "pr-*.checkout.apps.rosa.example.com"
1028+
forwardAmbientToken: true
1029+
```
1030+
1031+
#### Scenario: Project editor updates preview allowlist
1032+
1033+
- GIVEN user A has `project:editor` on Project `checkout`
1034+
- WHEN user A adds `pr-*.checkout.apps.rosa.example.com` in Settings > Preview
1035+
- THEN the UI saves the entry to Project `spec.preview.allowedHosts`
1036+
- AND subsequent previews in Project `checkout` use the updated allowlist
1037+
1038+
#### Scenario: Project viewer sees read-only preview settings
1039+
1040+
- GIVEN user B has `project:viewer` on Project `checkout`
1041+
- WHEN user B opens Settings > Preview
1042+
- THEN the current preview host policy is visible
1043+
- AND controls that would create, update, or remove entries are disabled or hidden
1044+
1045+
#### Scenario: Empty allowlist disables project previews
1046+
1047+
- GIVEN Project `checkout` has `spec.preview.allowedHosts: []`
1048+
- WHEN a Session in `checkout` has an `ambient-code.io/ui/preview-url` annotation
1049+
- THEN opening the preview is rejected as untrusted
1050+
- AND the UI explains that no trusted preview hosts are configured for the Project
9701051

9711052
#### Scenario: Feature flag toggle confirmation
9721053

@@ -1117,6 +1198,7 @@ This section documents API endpoints and capabilities that this spec depends on
11171198

11181199
| Dependency | Required By | Status | Interim |
11191200
|------------|-------------|--------|---------|
1201+
| Project `spec.preview.allowedHosts` API and SDK support | Live Preview, Settings > Preview | Specified in Ambient Data Model; implementation planned | Use deployment-level preview env allowlist as read-only fallback until Project preview policy is available; fallback never enables Ambient token relay |
11201202
| Annotation enrichment endpoint (resolve `ambient-code.io/jira/issue` etc. against bound credentials) | Annotation enrichment, Issues view status filtering | Not yet specified | Render raw annotation values as clickable chips |
11211203
| `GET /credentials/{cred_id}/role_bindings` (scoped query) | Credential binding display | Planned, not implemented | Use generic `GET /role_bindings` filtered by `credential_id` |
11221204
| Cross-resource search endpoint | Global search | Not planned | Client-side aggregation across multiple list endpoints |
@@ -1133,6 +1215,8 @@ This section documents API endpoints and capabilities that this spec depends on
11331215
| Annotation registry is a code enum (not dynamic) | Simplicity. Adding a new annotation type is a PR, not a config change. The set of annotations the UI understands should be deliberate and reviewed. |
11341216
| Enrichment as graceful degradation | UI ships without enrichment API. Raw annotation values are useful on their own (clickable links). Enriched tooltips are additive. |
11351217
| Cost as annotation, not API field | Cost is agent-computed and written as `ambient-code.io/cost/estimate`. No API-level cost computation. |
1218+
| Preview trust as Project spec, not annotation metadata or a singleton settings kind | Preview host trust is security-sensitive Project desired state. It belongs in typed Project `spec.preview` so it can be validated, authorized, edited via UI/API, and reconciled by `acpctl apply`. Session annotations only point to a preview URL; they do not grant trust. |
1219+
| Preview rendering trust separated from Ambient token relay trust | Many preview hosts should be frameable without receiving Ambient bearer tokens. `forwardAmbientToken` is explicit per host entry and may be constrained by platform token-relay policy. |
11361220
| Tool metrics computed client-side | The API stores raw SessionMessages. Aggregating tool call stats is a UI concern, not an API concern. |
11371221
| SSE for sessions, polling for rest | Sessions have real-time SSE streams. Credentials, schedules, and agents change infrequently — polling is sufficient and simpler. |
11381222
| Single interaction pattern per entity | Agent rows: navigate to detail page. Session rows: navigate to detail page. Reduces cognitive load per Krug's "Don't Make Me Think." |

0 commit comments

Comments
 (0)