openclaw/docs/web/control-ui.md
Miles Smith 0ea9547012 docs: document IDENTITY.md PWA customization feature
Add documentation for the undocumented IDENTITY.md → PWA manifest
customization feature:

- Updated agent-workspace.md to explain that the Name field in IDENTITY.md
  automatically customizes the Control UI PWA manifest
- Added dedicated PWA customization section to control-ui.md with examples
- Documented the current limitation: name changes after initial setup
  require manual manifest editing
- Included workaround steps for runtime name changes

This feature allows users to personalize their Clawdbot PWA appearance
(e.g., 'Nova - Clawdbot Control' instead of generic 'Clawdbot Control').
2026-01-26 09:02:37 -07:00

7.4 KiB
Raw Blame History

summary read_when
Browser-based control UI for the Gateway (chat, nodes, config)
You want to operate the Gateway from a browser
You want Tailnet access without SSH tunnels

Control UI (browser)

The Control UI is a small Vite + Lit single-page app served by the Gateway:

  • default: http://<host>:18789/
  • optional prefix: set gateway.controlUi.basePath (e.g. /clawdbot)

It speaks directly to the Gateway WebSocket on the same port.

Quick open (local)

If the Gateway is running on the same computer, open:

If the page fails to load, start the Gateway first: clawdbot gateway.

Auth is supplied during the WebSocket handshake via:

  • connect.params.auth.token
  • connect.params.auth.password The dashboard settings panel lets you store a token; passwords are not persisted. The onboarding wizard generates a gateway token by default, so paste it here on first connect.

What it can do (today)

  • Chat with the model via Gateway WS (chat.history, chat.send, chat.abort, chat.inject)
  • Stream tool calls + live tool output cards in Chat (agent events)
  • Channels: WhatsApp/Telegram/Discord/Slack + plugin channels (Mattermost, etc.) status + QR login + per-channel config (channels.status, web.login.*, config.patch)
  • Instances: presence list + refresh (system-presence)
  • Sessions: list + per-session thinking/verbose overrides (sessions.list, sessions.patch)
  • Cron jobs: list/add/run/enable/disable + run history (cron.*)
  • Skills: status, enable/disable, install, API key updates (skills.*)
  • Nodes: list + caps (node.list)
  • Exec approvals: edit gateway or node allowlists + ask policy for exec host=gateway/node (exec.approvals.*)
  • Config: view/edit ~/.clawdbot/clawdbot.json (config.get, config.set)
  • Config: apply + restart with validation (config.apply) and wake the last active session
  • Config writes include a base-hash guard to prevent clobbering concurrent edits
  • Config schema + form rendering (config.schema, including plugin + channel schemas); Raw JSON editor remains available
  • Debug: status/health/models snapshots + event log + manual RPC calls (status, health, models.list)
  • Logs: live tail of gateway file logs with filter/export (logs.tail)
  • Update: run a package/git update + restart (update.run) with a restart report

Chat behavior

  • chat.send is non-blocking: it acks immediately with { runId, status: "started" } and the response streams via chat events.
  • Re-sending with the same idempotencyKey returns { status: "in_flight" } while running, and { status: "ok" } after completion.
  • chat.inject appends an assistant note to the session transcript and broadcasts a chat event for UI-only updates (no agent run, no channel delivery).
  • Stop:
    • Click Stop (calls chat.abort)
    • Type /stop (or stop|esc|abort|wait|exit|interrupt) to abort out-of-band
    • chat.abort supports { sessionKey } (no runId) to abort all active runs for that session

Integrated Tailscale Serve (preferred)

Keep the Gateway on loopback and let Tailscale Serve proxy it with HTTPS:

clawdbot gateway --tailscale serve

Open:

  • https://<magicdns>/ (or your configured gateway.controlUi.basePath)

By default, Serve requests can authenticate via Tailscale identity headers (tailscale-user-login) when gateway.auth.allowTailscale is true. Clawdbot verifies the identity by resolving the x-forwarded-for address with tailscale whois and matching it to the header, and only accepts these when the request hits loopback with Tailscales x-forwarded-* headers. Set gateway.auth.allowTailscale: false (or force gateway.auth.mode: "password") if you want to require a token/password even for Serve traffic.

Bind to tailnet + token

clawdbot gateway --bind tailnet --token "$(openssl rand -hex 32)"

Then open:

  • http://<tailscale-ip>:18789/ (or your configured gateway.controlUi.basePath)

Paste the token into the UI settings (sent as connect.params.auth.token).

Insecure HTTP

If you open the dashboard over plain HTTP (http://<lan-ip> or http://<tailscale-ip>), the browser runs in a non-secure context and blocks WebCrypto. By default, Clawdbot blocks Control UI connections without device identity.

Recommended fix: use HTTPS (Tailscale Serve) or open the UI locally:

  • https://<magicdns>/ (Serve)
  • http://127.0.0.1:18789/ (on the gateway host)

Downgrade example (token-only over HTTP):

{
  gateway: {
    controlUi: { allowInsecureAuth: true },
    bind: "tailnet",
    auth: { mode: "token", token: "replace-me" }
  }
}

This disables device identity + pairing for the Control UI (even on HTTPS). Use only if you trust the network.

See Tailscale for HTTPS setup guidance.

Building the UI

The Gateway serves static files from dist/control-ui. Build them with:

pnpm ui:build # auto-installs UI deps on first run

Optional absolute base (when you want fixed asset URLs):

CLAWDBOT_CONTROL_UI_BASE_PATH=/clawdbot/ pnpm ui:build

For local development (separate dev server):

pnpm ui:dev # auto-installs UI deps on first run

Then point the UI at your Gateway WS URL (e.g. ws://127.0.0.1:18789).

Debugging/testing: dev server + remote Gateway

The Control UI is static files; the WebSocket target is configurable and can be different from the HTTP origin. This is handy when you want the Vite dev server locally but the Gateway runs elsewhere.

  1. Start the UI dev server: pnpm ui:dev
  2. Open a URL like:
http://localhost:5173/?gatewayUrl=ws://<gateway-host>:18789

Optional one-time auth (if needed):

http://localhost:5173/?gatewayUrl=wss://<gateway-host>:18789&token=<gateway-token>

Notes:

  • gatewayUrl is stored in localStorage after load and removed from the URL.
  • token is stored in localStorage; password is kept in memory only.
  • Use wss:// when the Gateway is behind TLS (Tailscale Serve, HTTPS proxy, etc.).

Remote access setup details: Remote access.

PWA customization

The Control UI supports installation as a Progressive Web App (PWA) for a native-like experience. The PWA name and display settings are defined in dist/control-ui/manifest.json.

Automatic name customization

If your workspace contains an IDENTITY.md file with a Name field, Clawdbot automatically customizes the PWA manifest at build/install time:

# IDENTITY.md
- **Name:** Nova

This updates the manifest to:

{
  "name": "Nova - Clawdbot Control",
  "short_name": "Nova",
  ...
}

The customized PWA name appears in app launchers, browser tabs, and the OS app switcher.

Updating the name after initial setup

Important: Name changes after initial setup are not automatically applied to the manifest. This is a known limitation.

Manual workaround:

  1. Edit IDENTITY.md with the new name
  2. Manually edit <install-path>/dist/control-ui/manifest.json:
    • Update "name" to "YourNewName - Clawdbot Control"
    • Update "short_name" to "YourNewName"
  3. Restart the gateway: clawdbot gateway restart
  4. Force a hard browser refresh (Ctrl+Shift+R or Cmd+Shift+R)
  5. Reinstall the PWA if the name doesn't update

See Agent workspace for the full IDENTITY.md structure and usage.