{
  "title": "Approve by request ID",
  "content": "openclaw devices approve <requestId>\nbash theme={null}\nopenclaw gateway --tailscale serve\nbash theme={null}\nopenclaw gateway --bind tailnet --token \"$(openssl rand -hex 32)\"\njson5 theme={null}\n{\n  gateway: {\n    controlUi: { allowInsecureAuth: true },\n    bind: \"tailnet\",\n    auth: { mode: \"token\", token: \"replace-me\" },\n  },\n}\nbash theme={null}\npnpm ui:build # auto-installs UI deps on first run\nbash theme={null}\nOPENCLAW_CONTROL_UI_BASE_PATH=/openclaw/ pnpm ui:build\nbash theme={null}\npnpm ui:dev # auto-installs UI deps on first run\ntext theme={null}\nhttp://localhost:5173/?gatewayUrl=ws://<gateway-host>:18789\ntext theme={null}\nhttp://localhost:5173/?gatewayUrl=wss://<gateway-host>:18789&token=<gateway-token>\njson5 theme={null}\n{\n  gateway: {\n    controlUi: {\n      allowedOrigins: [\"http://localhost:5173\"],\n    },\n  },\n}\n```\n\nRemote access setup details: [Remote access](/gateway/remote).",
  "code_samples": [
    {
      "code": "Once approved, the device is remembered and won't require re-approval unless\nyou revoke it with `openclaw devices revoke --device <id> --role <role>`. See\n[Devices CLI](/cli/devices) for token rotation and revocation.\n\n**Notes:**\n\n* Local connections (`127.0.0.1`) are auto-approved.\n* Remote connections (LAN, Tailnet, etc.) require explicit approval.\n* Each browser profile generates a unique device ID, so switching browsers or\n  clearing browser data will require re-pairing.\n\n## What it can do (today)\n\n* Chat with the model via Gateway WS (`chat.history`, `chat.send`, `chat.abort`, `chat.inject`)\n* Stream tool calls + live tool output cards in Chat (agent events)\n* Channels: WhatsApp/Telegram/Discord/Slack + plugin channels (Mattermost, etc.) status + QR login + per-channel config (`channels.status`, `web.login.*`, `config.patch`)\n* Instances: presence list + refresh (`system-presence`)\n* Sessions: list + per-session thinking/verbose overrides (`sessions.list`, `sessions.patch`)\n* Cron jobs: list/add/run/enable/disable + run history (`cron.*`)\n* Skills: status, enable/disable, install, API key updates (`skills.*`)\n* Nodes: list + caps (`node.list`)\n* Exec approvals: edit gateway or node allowlists + ask policy for `exec host=gateway/node` (`exec.approvals.*`)\n* Config: view/edit `~/.openclaw/openclaw.json` (`config.get`, `config.set`)\n* Config: apply + restart with validation (`config.apply`) and wake the last active session\n* Config writes include a base-hash guard to prevent clobbering concurrent edits\n* Config schema + form rendering (`config.schema`, including plugin + channel schemas); Raw JSON editor remains available\n* Debug: status/health/models snapshots + event log + manual RPC calls (`status`, `health`, `models.list`)\n* Logs: live tail of gateway file logs with filter/export (`logs.tail`)\n* Update: run a package/git update + restart (`update.run`) with a restart report\n\nCron jobs panel notes:\n\n* For isolated jobs, delivery defaults to announce summary. You can switch to none if you want internal-only runs.\n* Channel/target fields appear when announce is selected.\n\n## Chat behavior\n\n* `chat.send` is **non-blocking**: it acks immediately with `{ runId, status: \"started\" }` and the response streams via `chat` events.\n* Re-sending with the same `idempotencyKey` returns `{ status: \"in_flight\" }` while running, and `{ status: \"ok\" }` after completion.\n* `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).\n* Stop:\n  * Click **Stop** (calls `chat.abort`)\n  * Type `/stop` (or `stop|esc|abort|wait|exit|interrupt`) to abort out-of-band\n  * `chat.abort` supports `{ sessionKey }` (no `runId`) to abort all active runs for that session\n\n## Tailnet access (recommended)\n\n### Integrated Tailscale Serve (preferred)\n\nKeep the Gateway on loopback and let Tailscale Serve proxy it with HTTPS:",
      "language": "unknown"
    },
    {
      "code": "Open:\n\n* `https://<magicdns>/` (or your configured `gateway.controlUi.basePath`)\n\nBy default, Serve requests can authenticate via Tailscale identity headers\n(`tailscale-user-login`) when `gateway.auth.allowTailscale` is `true`. OpenClaw\nverifies the identity by resolving the `x-forwarded-for` address with\n`tailscale whois` and matching it to the header, and only accepts these when the\nrequest hits loopback with Tailscale’s `x-forwarded-*` headers. Set\n`gateway.auth.allowTailscale: false` (or force `gateway.auth.mode: \"password\"`)\nif you want to require a token/password even for Serve traffic.\n\n### Bind to tailnet + token",
      "language": "unknown"
    },
    {
      "code": "Then open:\n\n* `http://<tailscale-ip>:18789/` (or your configured `gateway.controlUi.basePath`)\n\nPaste the token into the UI settings (sent as `connect.params.auth.token`).\n\n## Insecure HTTP\n\nIf you open the dashboard over plain HTTP (`http://<lan-ip>` or `http://<tailscale-ip>`),\nthe browser runs in a **non-secure context** and blocks WebCrypto. By default,\nOpenClaw **blocks** Control UI connections without device identity.\n\n**Recommended fix:** use HTTPS (Tailscale Serve) or open the UI locally:\n\n* `https://<magicdns>/` (Serve)\n* `http://127.0.0.1:18789/` (on the gateway host)\n\n**Downgrade example (token-only over HTTP):**",
      "language": "unknown"
    },
    {
      "code": "This disables device identity + pairing for the Control UI (even on HTTPS). Use\nonly if you trust the network.\n\nSee [Tailscale](/gateway/tailscale) for HTTPS setup guidance.\n\n## Building the UI\n\nThe Gateway serves static files from `dist/control-ui`. Build them with:",
      "language": "unknown"
    },
    {
      "code": "Optional absolute base (when you want fixed asset URLs):",
      "language": "unknown"
    },
    {
      "code": "For local development (separate dev server):",
      "language": "unknown"
    },
    {
      "code": "Then point the UI at your Gateway WS URL (e.g. `ws://127.0.0.1:18789`).\n\n## Debugging/testing: dev server + remote Gateway\n\nThe Control UI is static files; the WebSocket target is configurable and can be\ndifferent from the HTTP origin. This is handy when you want the Vite dev server\nlocally but the Gateway runs elsewhere.\n\n1. Start the UI dev server: `pnpm ui:dev`\n2. Open a URL like:",
      "language": "unknown"
    },
    {
      "code": "Optional one-time auth (if needed):",
      "language": "unknown"
    },
    {
      "code": "Notes:\n\n* `gatewayUrl` is stored in localStorage after load and removed from the URL.\n* `token` is stored in localStorage; `password` is kept in memory only.\n* When `gatewayUrl` is set, the UI does not fall back to config or environment credentials.\n  Provide `token` (or `password`) explicitly. Missing explicit credentials is an error.\n* Use `wss://` when the Gateway is behind TLS (Tailscale Serve, HTTPS proxy, etc.).\n* `gatewayUrl` is only accepted in a top-level window (not embedded) to prevent clickjacking.\n* For cross-origin dev setups (e.g. `pnpm ui:dev` to a remote Gateway), add the UI\n  origin to `gateway.controlUi.allowedOrigins`.\n\nExample:",
      "language": "unknown"
    }
  ],
  "headings": [
    {
      "level": "h2",
      "text": "What it can do (today)",
      "id": "what-it-can-do-(today)"
    },
    {
      "level": "h2",
      "text": "Chat behavior",
      "id": "chat-behavior"
    },
    {
      "level": "h2",
      "text": "Tailnet access (recommended)",
      "id": "tailnet-access-(recommended)"
    },
    {
      "level": "h3",
      "text": "Integrated Tailscale Serve (preferred)",
      "id": "integrated-tailscale-serve-(preferred)"
    },
    {
      "level": "h3",
      "text": "Bind to tailnet + token",
      "id": "bind-to-tailnet-+-token"
    },
    {
      "level": "h2",
      "text": "Insecure HTTP",
      "id": "insecure-http"
    },
    {
      "level": "h2",
      "text": "Building the UI",
      "id": "building-the-ui"
    },
    {
      "level": "h2",
      "text": "Debugging/testing: dev server + remote Gateway",
      "id": "debugging/testing:-dev-server-+-remote-gateway"
    }
  ],
  "url": "llms-txt#approve-by-request-id",
  "links": []
}