Skip to content

Commit 2a0efcb

Browse files
committed
spec(projects): define preview URL allowlist settings
1 parent 16bf3e5 commit 2a0efcb

3 files changed

Lines changed: 272 additions & 32 deletions

File tree

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

Lines changed: 83 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -830,25 +830,55 @@ 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 `ProjectSettings.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 is a validated navigation/redirect derived from it. Arbitrary `url` parameters outside the Session preview context SHALL be rejected.
835+
- 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.
836+
- The preview proxy SHALL forward the BFF's server-side SSO access token to the preview target only when the matching ProjectSettings 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.
837+
- 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.
838+
839+
When ProjectSettings preview policy is available from the API/SDK, it is authoritative. During rollout before that API is available, preview trust MAY be read from deployment configuration as a read-only, deployment-scoped fallback. Fallback entries imply `forwardAmbientToken: false`. Once a Project has explicit ProjectSettings preview policy, even `allowedHosts: []`, the ProjectSettings policy overrides the deployment fallback for that Project.
835840

836841
#### Scenario: Preview mode activation
837842

838843
- 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
844+
- AND the URL matches the active Project's preview host allowlist
840845
- WHEN the user clicks "Open Preview" in the session detail
841846
- THEN a near-fullscreen overlay opens with the URL loaded in a sandboxed iframe
842847
- AND the overlay header shows the preview title, device size toggles (Desktop/Tablet/Mobile), and a Comment button
843848

844849
#### Scenario: Preview URL rejected (untrusted host)
845850

846851
- 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
852+
- AND the URL does not match the active Project's preview host allowlist
848853
- WHEN the user clicks "Open Preview"
849854
- THEN the preview does not render
850855
- AND an error message is displayed: "Preview URL is not on the trusted hosts allowlist"
851856

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

854884
- GIVEN the preview overlay is open
@@ -966,7 +996,52 @@ No list-watch endpoint exists for sessions today. Polling is the interim mechani
966996

967997
The Settings view SHALL provide project-scoped configuration management with tabbed sections.
968998

969-
**Tabs:** General (project metadata), Permissions (user/role management), API Keys (key lifecycle), Feature Flags (toggles with confirmation).
999+
**Tabs:** General (project metadata), Preview (trusted preview hosts), Permissions (user/role management), API Keys (key lifecycle), Feature Flags (toggles with confirmation).
1000+
1001+
### Requirement: Preview Trust Configuration
1002+
1003+
The Settings view SHALL expose the active Project's `ProjectSettings.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.
1004+
1005+
The UI SHALL persist Preview tab changes through the ProjectSettings API. The UI SHALL NOT write preview trust policy to Project labels or annotations.
1006+
1007+
If the ProjectSettings 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 ProjectSettings writes are supported.
1008+
1009+
The UI SHALL render the same logical policy that can be applied by YAML:
1010+
1011+
```yaml
1012+
apiVersion: ambient-code.io/v1alpha1
1013+
kind: ProjectSettings
1014+
metadata:
1015+
name: checkout-settings
1016+
spec:
1017+
projectRef:
1018+
name: checkout
1019+
preview:
1020+
allowedHosts:
1021+
- pattern: "pr-*.checkout.apps.rosa.example.com"
1022+
forwardAmbientToken: true
1023+
```
1024+
1025+
#### Scenario: Project editor updates preview allowlist
1026+
1027+
- GIVEN user A has `project:editor` on Project `checkout`
1028+
- WHEN user A adds `pr-*.checkout.apps.rosa.example.com` in Settings > Preview
1029+
- THEN the UI saves the entry to `ProjectSettings.spec.preview.allowedHosts`
1030+
- AND subsequent previews in Project `checkout` use the updated allowlist
1031+
1032+
#### Scenario: Project viewer sees read-only preview settings
1033+
1034+
- GIVEN user B has `project:viewer` on Project `checkout`
1035+
- WHEN user B opens Settings > Preview
1036+
- THEN the current preview host policy is visible
1037+
- AND controls that would create, update, or remove entries are disabled or hidden
1038+
1039+
#### Scenario: Empty allowlist disables project previews
1040+
1041+
- GIVEN Project `checkout` has `ProjectSettings.spec.preview.allowedHosts: []`
1042+
- WHEN a Session in `checkout` has an `ambient-code.io/ui/preview-url` annotation
1043+
- THEN opening the preview is rejected as untrusted
1044+
- AND the UI explains that no trusted preview hosts are configured for the Project
9701045

9711046
#### Scenario: Feature flag toggle confirmation
9721047

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

11181193
| Dependency | Required By | Status | Interim |
11191194
|------------|-------------|--------|---------|
1195+
| `ProjectSettings.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 ProjectSettings preview policy is available; fallback never enables Ambient token relay |
11201196
| 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 |
11211197
| `GET /credentials/{cred_id}/role_bindings` (scoped query) | Credential binding display | Planned, not implemented | Use generic `GET /role_bindings` filtered by `credential_id` |
11221198
| Cross-resource search endpoint | Global search | Not planned | Client-side aggregation across multiple list endpoints |
@@ -1133,6 +1209,8 @@ This section documents API endpoints and capabilities that this spec depends on
11331209
| 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. |
11341210
| Enrichment as graceful degradation | UI ships without enrichment API. Raw annotation values are useful on their own (clickable links). Enriched tooltips are additive. |
11351211
| Cost as annotation, not API field | Cost is agent-computed and written as `ambient-code.io/cost/estimate`. No API-level cost computation. |
1212+
| Preview trust as ProjectSettings, not annotation metadata | Preview host trust is security-sensitive desired state. It belongs in the typed ProjectSettings Kind 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. |
1213+
| 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. |
11361214
| Tool metrics computed client-side | The API stores raw SessionMessages. Aggregating tool call stats is a UI concern, not an API concern. |
11371215
| SSE for sessions, polling for rest | Sessions have real-time SSE streams. Credentials, schedules, and agents change infrequently — polling is sufficient and simpler. |
11381216
| 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)