Web Control UI
The Control UI is a browser-based interface for managing the OpenClaw Gateway. It’s a Vite + Lit single-page app served directly from the Gateway.Quick access
Local (same machine as Gateway): Remote (LAN/Tailnet):http://<gateway-host>:18789/https://<magicdns>/(Tailscale Serve, recommended)
Features
The Control UI provides full Gateway management through the browser:Chat
- Send messages to the model via Gateway WebSocket
- View session history with
chat.history - Stream tool calls and live tool output cards
- Abort in-flight runs with Stop button or
/stopcommand - Inject assistant notes directly to transcript (
chat.inject) - Partial abort retention: See partial assistant output when runs are stopped
Channels
- View status of all channels (WhatsApp, Telegram, Discord, Slack, plugins)
- QR code login for WhatsApp
- Per-channel configuration via forms
- Enable/disable channels
- Channel authentication and pairing
Configuration
- View and edit
~/.openclaw/openclaw.json - Schema-driven form rendering (including plugin and channel schemas)
- Raw JSON editor for advanced use
- Apply and restart Gateway with validation
- Base-hash guard prevents concurrent edit clobbers
Sessions
- List all Gateway sessions
- Per-session thinking/verbose overrides
- Switch active session context
- View token counts and history metadata
Nodes
- List connected nodes (macOS, iOS, Android)
- View node capabilities and permissions
- Invoke node commands
- Node status and connection health
Exec Approvals
- Edit Gateway or node allowlists for
execcommands - Configure ask policy:
never,on-miss,always - Security modes:
deny,allowlist,ask,allow - Add/remove allowlist patterns
- View last-used metadata for cleanup
Cron Jobs
- List scheduled jobs with run history
- Add/edit/enable/disable jobs
- Run jobs manually
- Configure delivery modes:
- Announce summary (with channel/target)
- Webhook (with optional bearer token)
- None (internal-only)
- Isolated vs main-session jobs
Skills
- View installed skills and status
- Enable/disable skills
- Install new skills
- Update skill API keys
Logs
- Live tail of Gateway log files
- Filter by level, category, subsystem
- Export logs to file
- Search log content
Debug
- Status/health/models snapshots
- Event log viewer
- Manual RPC calls to Gateway
- Presence list and refresh
- Instance coordination
Updates
- Run package/git updates
- Restart Gateway with validation
- View restart report
Authentication
The Control UI authenticates during WebSocket handshake: Token-based:Device pairing
When connecting from a new browser or device, Gateway requires one-time pairing approval: What you’ll see: “disconnected (1008): pairing required” To approve:- Local connections (
127.0.0.1) are auto-approved - Remote connections (LAN, Tailnet) require explicit approval
- Each browser profile generates unique device ID
- Clearing browser data requires re-pairing
Remote access
Tailscale Serve (recommended)
Keep Gateway on loopback and let Tailscale proxy with HTTPS:https://<magicdns>/
By default, Serve requests authenticate via Tailscale identity headers (tailscale-user-login) when gateway.auth.allowTailscale: true. OpenClaw verifies identity by resolving x-forwarded-for with tailscale whois.
Disable Tailscale auth:
Bind to tailnet
http://<tailscale-ip>:18789/
Paste token in UI Settings.
SSH tunnel
http://localhost:18789/
See Gateway remote access for details.
WebChat channel
The Control UI Chat tab uses the WebChat channel:- Native Gateway WebSocket integration (no separate server)
- Deterministic routing: replies always return to WebChat
- Uses same sessions and routing rules as other channels
- Shared history with macOS/iOS apps via Gateway sessions
- No dedicated
webchat.*config block - Uses
gateway.port,gateway.bind,gateway.auth.*settings
Chat behavior
Non-blocking sends:chat.sendacks immediately with{runId, status: "started"}- Response streams via
chatevents - Re-sending same
idempotencyKeyreturns:{status: "in_flight"}while running{status: "ok"}after completion
chat.historyresponses are size-bounded for UI safety- Gateway may truncate long text fields
- Oversized messages replaced with
[chat.history omitted: message too large]
- Click Stop button (calls
chat.abort) - Type
/stop(orstop|esc|abort|wait|exit|interrupt) chat.abortsupports{sessionKey}to abort all runs for session- Partial assistant text persisted with abort metadata
chat.injectappends assistant note to transcript- Broadcasts
chatevent for UI-only update - No agent run, no channel delivery
Insecure HTTP
Over plain HTTP (http://<lan-ip> or http://<tailscale-ip>), browsers run in non-secure context and block WebCrypto. By default, OpenClaw blocks Control UI connections without device identity.
Recommended fix: Use HTTPS or local access:
https://<magicdns>/(Tailscale Serve)http://127.0.0.1:18789/(on gateway host)
Building the UI
The Gateway serves static files fromdist/control-ui:
gatewayUrlstored in localStorage after loadtokenstored in localStorage;passwordkept in memory only- When
gatewayUrlset, explicit credentials required (no config fallback) - Use
wss://for TLS (Tailscale Serve, HTTPS proxy) gatewayUrlonly accepted in top-level window (not embedded)
gateway.controlUi.allowedOrigins:
Base path configuration
Optional URL prefix for reverse proxies:Cron jobs panel
Delivery modes:- Announce summary: Post to channel/target (channel and target fields appear)
- Webhook: POST to URL with optional bearer token (set
cron.webhookTokenin config) - None: Internal-only runs (no delivery)
- Isolated jobs: Separate context, delivery defaults to announce
- Main-session jobs: Use main session, webhook and none modes available
notify: true can still use cron.webhook until migrated.
Troubleshooting
Connection fails with “pairing required”
- List pending devices:
- Approve:
- Or use local access (auto-approved):
WebSocket connection fails
- Verify Gateway is running:
- Check port is listening:
- Test WebSocket:
“device identity required” over HTTP
Browser blocked WebCrypto in non-secure context. Fix:- Use HTTPS:
https://<magicdns>/(Tailscale Serve) - Use local:
http://127.0.0.1:18789/ - Or downgrade: Set
gateway.controlUi.allowInsecureAuth: true
Config changes not saving
-
Check for concurrent edits:
- UI includes base-hash guard
- Reload UI and try again
- Verify config file writable:
- Check Gateway logs for validation errors:

