feat(display): proxy native desktop streams after takeover #1752

Merged
hongming merged 1 commits from feat/1686-display-session-proxy into main 2026-05-24 01:33:25 +00:00
Owner

Implements the remaining native Display takeover path for issue #1686.

Changes:

  • Canvas uses trusted @novnc/novnc client instead of iframeing workspace-served HTML.
  • Take Control now returns a short-lived signed display session URL with the token in the URL fragment; Canvas sends it as a WebSocket subprotocol so it is not logged as a request query string.
  • workspace-server proxies only /websockify, only after validating the active display-control lock token, and strips credentials plus the custom token subprotocol before forwarding upstream.
  • Display status reports ready only when display is enabled and the EC2 instance_id exists.
  • Tenant startup now applies DISPLAY_SESSION_SIGNING_SECRET from the existing /cp/tenants/config self-refresh path.
  • Adds regression coverage for token signing, empty-secret fail-closed behavior, auth gating, stream proxy credential stripping, no query-token URLs, and tenant startup env refresh.

Verification:

  • workspace-server: go test ./cmd/server ./internal/handlers ./internal/router
  • canvas: npm test -- --run src/components/tabs/tests/DisplayTab.test.tsx src/components/tests/CreateWorkspaceDialog.test.tsx
  • canvas: npm run build -- --no-lint (passes; Next emits an @novnc/novnc top-level-await compatibility warning)

Review notes:

  • Follow-up to PR #1732. This avoids same-origin proxying of workspace-controlled HTML by serving trusted viewer code from Canvas and proxying only the VNC websocket.
  • Requires matching controlplane PR #263, which injects DISPLAY_SESSION_SIGNING_SECRET for new tenants and lazily backfills it for existing tenants through /cp/tenants/config on restart/redeploy.
Implements the remaining native Display takeover path for issue #1686. Changes: - Canvas uses trusted @novnc/novnc client instead of iframeing workspace-served HTML. - Take Control now returns a short-lived signed display session URL with the token in the URL fragment; Canvas sends it as a WebSocket subprotocol so it is not logged as a request query string. - workspace-server proxies only /websockify, only after validating the active display-control lock token, and strips credentials plus the custom token subprotocol before forwarding upstream. - Display status reports ready only when display is enabled and the EC2 instance_id exists. - Tenant startup now applies DISPLAY_SESSION_SIGNING_SECRET from the existing /cp/tenants/config self-refresh path. - Adds regression coverage for token signing, empty-secret fail-closed behavior, auth gating, stream proxy credential stripping, no query-token URLs, and tenant startup env refresh. Verification: - workspace-server: go test ./cmd/server ./internal/handlers ./internal/router - canvas: npm test -- --run src/components/tabs/__tests__/DisplayTab.test.tsx src/components/__tests__/CreateWorkspaceDialog.test.tsx - canvas: npm run build -- --no-lint (passes; Next emits an @novnc/novnc top-level-await compatibility warning) Review notes: - Follow-up to PR #1732. This avoids same-origin proxying of workspace-controlled HTML by serving trusted viewer code from Canvas and proxying only the VNC websocket. - Requires matching controlplane PR #263, which injects DISPLAY_SESSION_SIGNING_SECRET for new tenants and lazily backfills it for existing tenants through /cp/tenants/config on restart/redeploy.
hongming added 1 commit 2026-05-23 23:28:41 +00:00
feat(display): proxy native desktop streams after takeover
ci-arm64-advisory / fast-checks (pull_request) Waiting to run
CI / Canvas Deploy Reminder (pull_request) Blocked by required conditions
Lint shellcheck (arm64 pilot) / shellcheck-arm64 (pilot) (pull_request) Successful in 9s
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 16s
CI / Python Lint & Test (pull_request) Successful in 19s
CI / Detect changes (pull_request) Successful in 23s
E2E API Smoke Test / detect-changes (pull_request) Successful in 9s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 5s
Harness Replays / detect-changes (pull_request) Successful in 7s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 10s
E2E Chat / detect-changes (pull_request) Successful in 11s
Lint forbidden tenant-env keys / Scan workspace_secrets writers for forbidden env keys (pull_request) Successful in 9s
Lint no tenant GITEA or GITHUB token write / Scan for repo-host token write into tenant workspace surface (pull_request) Successful in 8s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 5s
gate-check-v3 / gate-check (pull_request) Successful in 5s
sop-checklist / review-refire (pull_request) Has been skipped
sop-checklist / na-declarations (pull_request) N/A: (none)
security-review / approved (pull_request) Failing after 10s
sop-checklist / all-items-acked (pull_request) Successful in 9s
qa-review / approved (pull_request) Failing after 10s
sop-tier-check / tier-check (pull_request) Successful in 8s
lint-required-no-paths / lint-required-no-paths (pull_request) Successful in 1m4s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 2s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 1m26s
E2E Chat / E2E Chat (pull_request) Successful in 5s
CI / Platform (Go) (pull_request) Successful in 5m22s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 11s
Harness Replays / Harness Replays (pull_request) Successful in 5s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 1m52s
CI / Canvas (Next.js) (pull_request) Successful in 7m16s
CI / all-required (pull_request) Successful in 20m31s
759e1133df
app-fe force-pushed feat/1686-display-session-proxy from 759e1133df to ce9dcfd75c 2026-05-23 23:33:36 +00:00 Compare
app-fe force-pushed feat/1686-display-session-proxy from ce9dcfd75c to 3fa5a90bc1 2026-05-23 23:40:02 +00:00 Compare
core-qa approved these changes 2026-05-23 23:43:50 +00:00
core-qa left a comment
Member

Approved from QA: focused local verification covers Display tab takeover, workspace-server display/session handlers, tenant startup env refresh, and Canvas build.

Approved from QA: focused local verification covers Display tab takeover, workspace-server display/session handlers, tenant startup env refresh, and Canvas build.
core-security approved these changes 2026-05-23 23:43:51 +00:00
core-security left a comment
Member

Approved from security: token moved out of request query logging path, session proxy validates active control lock, strips browser credentials, and forwards only /websockify.

Approved from security: token moved out of request query logging path, session proxy validates active control lock, strips browser credentials, and forwards only /websockify.
app-fe force-pushed feat/1686-display-session-proxy from 3fa5a90bc1 to 3c82b39f3d 2026-05-24 00:41:49 +00:00 Compare
hongming merged commit 43422e0ba9 into main 2026-05-24 01:33:25 +00:00
Sign in to join this conversation.
4 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: molecule-ai/molecule-core#1752