openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
121 lines (94 loc) • 4.84 kB
Markdown
# Control UI hosting paths
Classify the connection before troubleshooting. “Native” can mean a browser on
the Gateway host, a direct Gateway connection, or a native client; those paths
do not have identical routing.
## Local or direct Gateway
Prefer `openclaw dashboard` on the Gateway host. It creates a short-lived local
bootstrap URL and binds the browser to durable device identity. Do not paste or
log bootstrap credentials.
For a remote browser, prefer an SSH tunnel or HTTPS ingress. Direct browser
access needs both:
- the Gateway Control UI/WebSocket listener; and
- the separate widget sandbox listener, normally the Gateway port plus one.
When the browser reaches the Gateway host directly, OpenClaw can derive the
sandbox origin by substituting the sandbox port. A custom `mcp.apps.sandboxPort`
changes that listener. The sandbox listener is shared by dashboard HTML frames
and enabled MCP Apps, but it never serves the authenticated Control UI.
## Tailscale-hosted Control UI
Use managed Tailscale Serve for browser access:
```bash
openclaw gateway --tailscale serve
```
Serve keeps the Gateway on loopback and publishes the Control UI and WebSocket
over HTTPS. With `gateway.auth.allowTailscale: true`, OpenClaw can accept a
verified Serve identity for Control UI authentication. The browser still needs
device identity. Externally managed Serve routes are generic trusted-proxy
ingress and do not inherit managed Serve authentication semantics.
The managed Control UI route does **not** publish the widget sandbox listener.
For dashboards with HTML or MCP App frames:
1. publish the sandbox listener at a second HTTPS origin reachable by the same
browser;
2. route that origin only to the configured sandbox port;
3. set `mcp.apps.sandboxOrigin` to that public origin;
4. restart the Gateway;
5. update or republish the widget to issue a fresh ticketed frame URL;
6. verify the frame in the Tailscale-hosted Control UI.
Write down both sides of every proxy hop before changing it. The public side is
normally HTTPS. The Gateway sandbox listener uses HTTPS when Gateway TLS is
enabled, otherwise HTTP. Configure the proxy upstream with the listener's real
protocol. Do not infer it from the public URL
or from the proxy's automatic TLS behavior. A response such as `Client sent an
HTTP request to an HTTPS server` is a protocol mismatch, not proof that the
sandbox is healthy.
Example shape:
```json5
{
mcp: {
apps: {
sandboxOrigin: "https://widgets.example.ts.net",
sandboxPort: 18790,
},
},
}
```
The sandbox origin must differ from the Control UI origin. Never host
authenticated or sensitive content on it. Preserve existing Tailscale routes;
inspect them before adding or changing a route.
Do not use `gateway.bind: "tailnet"` with plain HTTP for a browser Control UI.
That direct bind is intended for native or CLI clients. Prefer Tailscale Serve
for a browser. Use Funnel only when public exposure is intentional and protect
it with the required password.
## Other reverse proxies
The same two-origin rule applies to nginx, Caddy, Cloudflare Tunnel, and other
TLS terminators:
- one authenticated origin for the Control UI and WebSocket;
- one unprivileged origin, routed only to the sandbox listener, for widget
frames;
- `mcp.apps.sandboxOrigin` set to the second origin.
Do not forward arbitrary authentication headers. Follow the trusted-proxy
documentation when a proxy owns identity.
## Diagnose by layer
1. **Gateway:** Control UI and WebSocket reachable; authentication and device
identity succeed.
2. **Board state:** `dashboard read` returns the expected widget and revision.
3. **Sandbox route:** configured sandbox origin reaches only the sandbox
listener. Verify the public scheme and the proxy-to-listener scheme
separately, and inspect the response body as well as its status.
4. **Ticket:** inspect the browser's frame requests and confirm the current
ticketed widget document loads. The sandbox shell and widget document are
separate requests; a 200 from one does not prove the other works.
5. **Widget network:** for live data, confirm the exact HTTPS origin appears in
the widget's declared and granted `capabilities.netOrigins`. Then inspect the
browser request for CSP, CORS, certificate, mixed-content, and final-URL
failures. A successful request from the Gateway host does not prove a
sandboxed browser fetch works.
6. **Rendering:** the browser loads the frame, its data requests succeed, and
its controls work.
A working Control UI shell with a blank widget usually points to layers 3–6,
not board storage.
Source documentation:
- `docs/web/control-ui.md`
- `docs/web/dashboards.md`
- `docs/web/dashboard-architecture.md`
- `docs/gateway/tailscale.md`
- `docs/cli/mcp.md` (MCP Apps sandbox listener)