Skip to content

[Fix] Sandbox tasks cannot connect or stop on LAN-hosted deployments - #1096

Merged
daniel-lxs merged 7 commits into
developfrom
fix/task-cancel-api-fallback-2zwfuyrabss0w
Aug 4, 2026
Merged

[Fix] Sandbox tasks cannot connect or stop on LAN-hosted deployments#1096
daniel-lxs merged 7 commits into
developfrom
fix/task-cancel-api-fallback-2zwfuyrabss0w

Conversation

@roomote-community

@roomote-community roomote-community Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Opened on behalf of sky. Follow up by mentioning @roomote, in the web UI, or in Discord.

What changed

Local Docker sandbox-server traffic now uses the app's same-origin /_roomote-sandbox/<taskId> route when the configured preview domain is local. Caddy forwards that route to the preview proxy for both HTTP and WebSocket traffic instead of allowing it to fall through to the Roomote web app.

The task Stop action also falls back to the authenticated task-run cancellation API when the live sandbox client is unavailable or its cancellation request fails. Successful sandbox cancellation continues to use the immediate live path without making a duplicate API request.

Regression coverage verifies local-domain URL selection and disconnected, failed-sandbox, and successful-sandbox cancellation paths. Deployment validation also protects the Caddy route and sandbox-server host rewrite.

Why this change was made

On LAN-hosted deployments, .localhost sandbox URLs could reach the main web listener instead of the sandbox server. The resulting sign-in redirect failed browser CORS checks, broke the WebSocket connection, and left no working live cancellation path.

Impact

Sandbox HTTP and WebSocket requests stay on the browser-reachable app origin for local preview domains, while production wildcard preview domains keep their existing routing. If the live transport still fails, users can stop the active task through the API fallback.

Self-hosting guidance now requires roomote upgrade rather than image-only updates so the controller and Caddy routing configuration roll out together.

The deployment configuration shape and route assertions passed locally; Caddy's final adapt check could not run because the sandbox does not expose a Docker socket.

@roomote-community

roomote-community Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

1 issue outstanding. See task

  • apps/web/src/app/(sandbox)/task/[taskId]/prompt-input/PromptInput.tsx:146 - Stop is hidden when the sandbox never establishes a connection because taskPhase remains null even while the persisted task run is active. Use the persisted run status (or expose cancellation on the connection-error banner) so the new API fallback is reachable for this failure mode.

Reviewed 41ed554

@roomote-community roomote-community Bot changed the title [Fix] Task cannot stop when sandbox connection fails [Fix] Sandbox tasks cannot connect or stop on LAN-hosted deployments Aug 4, 2026
@daniel-lxs

Copy link
Copy Markdown
Member

The approach makes sense to me. Local preview domains fundamentally can't resolve from a second device, so routing sandbox traffic through the app origin via the existing /_roomote-sandbox/<taskId> pattern is the right call, and the Stop button API fallback is a solid fix on its own.

A few things I'd like resolved before this merges:

  • Rollout skew: the controller now emits app-origin URLs for networked Docker sandboxes on local preview domains, but only when the new Caddy route exists does that URL go anywhere. Anyone who updates the app images without picking up the new Caddyfile gets sandbox URLs that hit the web app and fail, which is the same symptom this PR is fixing. Worth calling out in upgrade notes at minimum.
  • Dev stack parity: only deploy/caddy/Caddyfile got the route. Should .docker/caddy/Caddyfile get the same handler so the dev stack matches the deploy topology?
  • Auth on the rewritten-Host path: has the preview proxy been checked to behave the same when requests arrive with the rewritten <taskId>-sandbox-server.<domain> Host header instead of a real subdomain request, for both HTTP and the WebSocket upgrade?
  • Unvalidated Caddy config: the PR notes caddy adapt couldn't run in the sandbox, so the new stanza is syntactically unverified. This needs a real end-to-end pass on a LAN topology (app on one machine, browser on another) before merge.
  • Scope: the cancel fallback is independent of the routing change and could land on its own if the routing half needs more iteration.

Happy to run the LAN end-to-end validation locally.

@roomote-community

roomote-community Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the rollout-skew concern in 74d65df: the self-hosting guide now explicitly requires roomote upgrade so application images and Caddy configuration move together.

The other checks resolved as follows:

  • .docker/caddy/Caddyfile already has the equivalent /_roomote-sandbox/<taskId> handler and rewritten sandbox-server Host, so the dev stack already matches this topology.
  • The rewritten Host uses the preview proxy's normal {taskId}-{portName}.{domain} format. Both HTTP and WebSocket handlers pass that Host through the same parseHostForConfig path before their existing auth checks.
  • The automated controller, cancellation, type, lint, and deployment-shape checks remain green. caddy adapt and the two-machine LAN end-to-end pass are still outstanding because this sandbox has no Docker socket.

@daniel-lxs, taking you up on the offered LAN validation would close the remaining pre-merge evidence gap. If that exposes routing trouble, the API cancellation fallback remains independently shippable and the routing commit can be split out.

roomote and others added 4 commits August 4, 2026 19:10
crypto.randomUUID is only available in secure contexts (https or
localhost), so prompt submission, task tools, terminal tabs, and
attachment handling all threw on plain-http LAN deployments. Route
them through a shared helper with a getRandomValues-based v4
fallback.
The dev Caddyfile hardcoded the preview proxy at
host.docker.internal:18081 with the roomotepreview.localhost host
suffix, so dev environments that override
ROOMOTE_PREVIEW_PROXY_PORT or the preview domain lost the
same-origin sandbox route once the controller prefers app-origin
URLs for local preview domains. Defaults are unchanged.
A dead transport does not always reject quickly: an unresponsive
upstream can hold the live cancelTask request open for minutes, and
the API fallback only ran on a thrown error. Race the live
cancellation against a 10s timeout so Stop degrades to the
authenticated cancellation API instead of hanging.
@daniel-lxs

Copy link
Copy Markdown
Member

Ran the LAN end-to-end validation on a local deployment: stack on one machine, browser reaching it via the machine's LAN IP origin (http://192.168.x.x:18080), networked Local Docker sandbox provider, dev Caddy edge.

Routing: works as designed.

  • The controller stored the app-origin sandbox URL (http://192.168.x.x:18080/_roomote-sandbox/<taskId>) for a networked Docker run on a local preview domain.
  • Sandbox tRPC over HTTP, the WebSocket, live transcript streaming, live tool-call chips, and follow-up steering all worked from the LAN origin. Zero CORS errors in the console.
  • Stop on a running task went through the live commands.cancelTask path (200) with no duplicate API cancellation, matching the intended behavior.
  • Freezing the worker container reproduced the exact console signature from the report ([SandboxProvider] Connection timeout — retrying initial connection → retries exhausted), confirming the failure mode this PR addresses.

Three fixes pushed to this branch based on what the validation surfaced:

  1. crypto.randomUUID is only available in secure contexts, so on a plain-http LAN origin every follow-up submission threw before sending (sendPrompt error: TypeError: globalThis.crypto.randomUUID is not a function). Task tools, terminal tabs, and attachments had the same issue. Added a generateClientUuid helper with a getRandomValues fallback and routed the client call sites through it. Verified working over the LAN origin after the fix.
  2. The dev Caddyfile hardcoded host.docker.internal:18081 and roomotepreview.localhost in the sandbox route, so dev setups that override the preview proxy port or domain would lose live sandbox connectivity once the controller prefers app-origin URLs. Parameterized with env defaults that keep current behavior; caddy adapt passes with defaults and overrides.
  3. The API cancellation fallback only ran on a thrown error, but a half-dead transport (accepts connections, never responds) held the live cancelTask open for minutes with no user feedback. Bounded the live cancellation with a 10s timeout before falling back. Regression test added.

Also ran caddy adapt on deploy/caddy/Caddyfile (passes), the controller and PromptInput suites, the deployment artifact validation, and lint/typecheck.

Known follow-up (not blocking): when the sandbox transport never connects at all, the task page shows "Could not reach the live task" and disables the prompt area, so no Stop control is rendered and the disconnected-client fallback branch has no UI entry point in that state. Surfacing a DB-backed Stop (or a cancel action on the error banner) would close that last gap; the fast-failing-transport case from the original report is covered.

@daniel-lxs daniel-lxs left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LAN E2E validated end to end (details in comments); CI green.

@daniel-lxs
daniel-lxs marked this pull request as ready for review August 4, 2026 22:14
@daniel-lxs
daniel-lxs merged commit 73f0207 into develop Aug 4, 2026
18 checks passed
@daniel-lxs
daniel-lxs deleted the fix/task-cancel-api-fallback-2zwfuyrabss0w branch August 4, 2026 22:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants