Skip to content

Code Sandbox / Remote Browser

Code Sandbox (sandbox-service) and Remote Browser (browser-service) are two optional advanced services, off by default. Both log in via the platform’s OAuth flow. This page covers enabling them, OAuth client auto-registration, and public-URL overrides for external-ingress scenarios.

ServiceCapabilityDependency
Code SandboxLets agents run code and serve temporary web previewsThe third-party OpenSandbox (you install it yourself; the platform points at it via OPENSANDBOX_URL)
Remote BrowserLets agents drive a real browser while users watch live over WebRTCThe bundled TURN relay (coturn), enabled together with BROWSER_ENABLED

In the configuration generator, toggle the corresponding module to produce a values.env with these sections, or set SANDBOX_ENABLED / BROWSER_ENABLED to true by hand.

Set the relevant values.env toggle to true, fill in the required fields, and the next install.sh applies it:

Terminal window
# Code Sandbox
SANDBOX_ENABLED=true
SANDBOX_NODE_PORT=30086
SANDBOX_JWT_SECRET= # openssl rand -hex 32
SANDBOX_SERVICE_KEY= # openssl rand -hex 32, shared between browser and sandbox
# Remote Browser (incl. TURN)
BROWSER_ENABLED=true
BROWSER_NODE_PORT=30085
BROWSER_JWT_SECRET= # openssl rand -hex 32
TURN_HOST= # public / LAN IP browsers can reach, required
TURN_AUTH_SECRET= # openssl rand -hex 32

SANDBOX_JWT_SECRET / BROWSER_JWT_SECRET are each service’s session signing key; SANDBOX_SERVICE_KEY is the shared key the browser uses to call the sandbox (reused by the browser when both modules are on). For TURN_HOST and its troubleshooting, see Ingress Integration and “Debugging” below.

Code Sandbox is powered by OpenSandbox (github.com/alibaba/OpenSandbox), a third-party component the platform installer does not install for you. When SANDBOX_ENABLED=true, install.sh deploys nap-sandbox and assumes OpenSandbox is already installed and reachable at OPENSANDBOX_URL.

Install OpenSandbox (controller + server) from its upstream Helm charts, then point the platform at it:

Terminal window
git clone https://github.com/alibaba/OpenSandbox && cd OpenSandbox/kubernetes/charts
helm dependency build opensandbox
helm install opensandbox ./opensandbox \
--namespace nap --create-namespace \
-f <path-to>/optional/sandbox/opensandbox-values.yaml

Running OpenSandbox in the nap namespace lets the platform’s default OPENSANDBOX_URL resolve. If you install OpenSandbox into its own namespace, set OPENSANDBOX_URL explicitly (e.g. http://opensandbox-server.opensandbox-system.svc:80). See the self-host README “Enabling Code Sandbox” for the full reference, including mounting the platform’s sandbox pod template.

Both services log in via the platform OAuth flow with fixed client_ids (sandbox-service / browser-service) — they are PKCE public clients with no client_secret. These clients must exist in the control plane’s oauth_clients table, or GET /api/oauth/authorize returns 400 invalid_client at login.

After seeding the admin, install.sh runs a nap-seed-oauth-clients Job that auto-registers these two rows based on the enabled modules — no manual creation on the “applications” page. The registered redirect_uri is the same origin the service computes its own callback from, so they don’t drift:

client_idDefault redirect_uri
sandbox-servicehttp://<TOS_HOST>:<SANDBOX_NODE_PORT>/api/auth/callback
browser-servicehttp://<TOS_HOST>:<BROWSER_NODE_PORT>/api/auth/callback

The Job is idempotent: after changing a NodePort or public URL, re-running install.sh (or install.sh --seed-only) syncs the redirect_uris automatically.

Service addresses are derived from the NodePort by default. When you expose the services through your own ingress / a custom domain (typically INGRESS_MODE=external, see Ingress Integration), the NodePort-form address is neither externally correct nor matches the OAuth callback. Override it with *_PUBLIC_URL:

Terminal window
SANDBOX_PUBLIC_URL=https://sandbox.example.com
BROWSER_PUBLIC_URL=https://browsers.example.com

The override feeds both the service’s own callback address and the registered redirect_uri, keeping them in sync. Leave it blank to keep the original NodePort behavior, which doesn’t affect existing deployments. The address has no trailing slash.

  • Login redirect reports 400 invalid_client — the matching row is missing in oauth_clients. Confirm the seed Job ran and succeeded: kubectl -n <ns> logs job/nap-seed-oauth-clients; the log prints registered "sandbox-service" → .... Re-run manually: install.sh --seed-only.
  • Login callback reports 400 invalid_redirect_uri — the registered redirect_uri doesn’t match the service’s actual callback address. Common when external ingress is configured but *_PUBLIC_URL isn’t set, or a NodePort changed without re-seeding. Set *_PUBLIC_URL and re-run install.sh --seed-only.
  • Browser shows no video / black screen — usually TURN can’t be reached. Verify TURN_HOST is an IP the browser side can actually reach (kubelet auto-detect is unreliable on multi-NIC nodes, so set it explicitly), and that coturn is ready: kubectl -n <ns> get pod -l app=coturn.
  • Sandbox won’t start — first check whether OpenSandbox is installed: kubectl -n <ns> get pod -l app.kubernetes.io/name=opensandbox.