{
  "title": "Tailscale (Gateway dashboard)",
  "content": "OpenClaw can auto-configure Tailscale **Serve** (tailnet) or **Funnel** (public) for the\nGateway dashboard and WebSocket port. This keeps the Gateway bound to loopback while\nTailscale provides HTTPS, routing, and (for Serve) identity headers.\n\n* `serve`: Tailnet-only Serve via `tailscale serve`. The gateway stays on `127.0.0.1`.\n* `funnel`: Public HTTPS via `tailscale funnel`. OpenClaw requires a shared password.\n* `off`: Default (no Tailscale automation).\n\nSet `gateway.auth.mode` to control the handshake:\n\n* `token` (default when `OPENCLAW_GATEWAY_TOKEN` is set)\n* `password` (shared secret via `OPENCLAW_GATEWAY_PASSWORD` or config)\n\nWhen `tailscale.mode = \"serve\"` and `gateway.auth.allowTailscale` is `true`,\nvalid Serve proxy requests can authenticate via Tailscale identity headers\n(`tailscale-user-login`) without supplying a token/password. OpenClaw verifies\nthe identity by resolving the `x-forwarded-for` address via the local Tailscale\ndaemon (`tailscale whois`) and matching it to the header before accepting it.\nOpenClaw only treats a request as Serve when it arrives from loopback with\nTailscale’s `x-forwarded-for`, `x-forwarded-proto`, and `x-forwarded-host`\nheaders.\nTo require explicit credentials, set `gateway.auth.allowTailscale: false` or\nforce `gateway.auth.mode: \"password\"`.\n\n### Tailnet-only (Serve)\n\nOpen: `https://<magicdns>/` (or your configured `gateway.controlUi.basePath`)\n\n### Tailnet-only (bind to Tailnet IP)\n\nUse this when you want the Gateway to listen directly on the Tailnet IP (no Serve/Funnel).\n\nConnect from another Tailnet device:\n\n* Control UI: `http://<tailscale-ip>:18789/`\n* WebSocket: `ws://<tailscale-ip>:18789`\n\nNote: loopback (`http://127.0.0.1:18789`) will **not** work in this mode.\n\n### Public internet (Funnel + shared password)\n\nPrefer `OPENCLAW_GATEWAY_PASSWORD` over committing a password to disk.\n\n* Tailscale Serve/Funnel requires the `tailscale` CLI to be installed and logged in.\n* `tailscale.mode: \"funnel\"` refuses to start unless auth mode is `password` to avoid public exposure.\n* Set `gateway.tailscale.resetOnExit` if you want OpenClaw to undo `tailscale serve`\n  or `tailscale funnel` configuration on shutdown.\n* `gateway.bind: \"tailnet\"` is a direct Tailnet bind (no HTTPS, no Serve/Funnel).\n* `gateway.bind: \"auto\"` prefers loopback; use `tailnet` if you want Tailnet-only.\n* Serve/Funnel only expose the **Gateway control UI + WS**. Nodes connect over\n  the same Gateway WS endpoint, so Serve can work for node access.\n\n## Browser control (remote Gateway + local browser)\n\nIf you run the Gateway on one machine but want to drive a browser on another machine,\nrun a **node host** on the browser machine and keep both on the same tailnet.\nThe Gateway will proxy browser actions to the node; no separate control server or Serve URL needed.\n\nAvoid Funnel for browser control; treat node pairing like operator access.\n\n## Tailscale prerequisites + limits\n\n* Serve requires HTTPS enabled for your tailnet; the CLI prompts if it is missing.\n* Serve injects Tailscale identity headers; Funnel does not.\n* Funnel requires Tailscale v1.38.3+, MagicDNS, HTTPS enabled, and a funnel node attribute.\n* Funnel only supports ports `443`, `8443`, and `10000` over TLS.\n* Funnel on macOS requires the open-source Tailscale app variant.\n\n* Tailscale Serve overview: [https://tailscale.com/kb/1312/serve](https://tailscale.com/kb/1312/serve)\n* `tailscale serve` command: [https://tailscale.com/kb/1242/tailscale-serve](https://tailscale.com/kb/1242/tailscale-serve)\n* Tailscale Funnel overview: [https://tailscale.com/kb/1223/tailscale-funnel](https://tailscale.com/kb/1223/tailscale-funnel)\n* `tailscale funnel` command: [https://tailscale.com/kb/1311/tailscale-funnel](https://tailscale.com/kb/1311/tailscale-funnel)",
  "code_samples": [
    {
      "code": "Open: `https://<magicdns>/` (or your configured `gateway.controlUi.basePath`)\n\n### Tailnet-only (bind to Tailnet IP)\n\nUse this when you want the Gateway to listen directly on the Tailnet IP (no Serve/Funnel).",
      "language": "unknown"
    },
    {
      "code": "Connect from another Tailnet device:\n\n* Control UI: `http://<tailscale-ip>:18789/`\n* WebSocket: `ws://<tailscale-ip>:18789`\n\nNote: loopback (`http://127.0.0.1:18789`) will **not** work in this mode.\n\n### Public internet (Funnel + shared password)",
      "language": "unknown"
    },
    {
      "code": "Prefer `OPENCLAW_GATEWAY_PASSWORD` over committing a password to disk.\n\n## CLI examples",
      "language": "unknown"
    }
  ],
  "headings": [
    {
      "level": "h2",
      "text": "Modes",
      "id": "modes"
    },
    {
      "level": "h2",
      "text": "Auth",
      "id": "auth"
    },
    {
      "level": "h2",
      "text": "Config examples",
      "id": "config-examples"
    },
    {
      "level": "h3",
      "text": "Tailnet-only (Serve)",
      "id": "tailnet-only-(serve)"
    },
    {
      "level": "h3",
      "text": "Tailnet-only (bind to Tailnet IP)",
      "id": "tailnet-only-(bind-to-tailnet-ip)"
    },
    {
      "level": "h3",
      "text": "Public internet (Funnel + shared password)",
      "id": "public-internet-(funnel-+-shared-password)"
    },
    {
      "level": "h2",
      "text": "CLI examples",
      "id": "cli-examples"
    },
    {
      "level": "h2",
      "text": "Notes",
      "id": "notes"
    },
    {
      "level": "h2",
      "text": "Browser control (remote Gateway + local browser)",
      "id": "browser-control-(remote-gateway-+-local-browser)"
    },
    {
      "level": "h2",
      "text": "Tailscale prerequisites + limits",
      "id": "tailscale-prerequisites-+-limits"
    },
    {
      "level": "h2",
      "text": "Learn more",
      "id": "learn-more"
    }
  ],
  "url": "llms-txt#tailscale-(gateway-dashboard)",
  "links": []
}