Adds optional `gateway.tailscale.serviceName` support for Tailscale Serve so the Gateway Control UI can be exposed through a named Tailscale Service while existing hostname-based Serve and Funnel behavior stays unchanged.
The implementation validates `svc:<dns-label>`, passes the Service name to `tailscale serve`, clears named Service config with `tailscale serve clear <service>` when resetOnExit runs, and uses the derived Service hostname in startup logs, status output, and pairing URLs.
Verification:
- node scripts/run-vitest.mjs src/infra/tailscale.test.ts src/gateway/server-tailscale.test.ts src/config/config.gateway-tailscale-bind.test.ts src/gateway/startup-auth.test.ts src/commands/status.scan.shared.test.ts src/pairing/setup-code.test.ts
- .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main --parallel-tests "node scripts/run-vitest.mjs src/infra/tailscale.test.ts src/gateway/server-tailscale.test.ts src/config/config.gateway-tailscale-bind.test.ts src/gateway/startup-auth.test.ts src/commands/status.scan.shared.test.ts src/pairing/setup-code.test.ts"
- git diff --check
- git merge-tree --write-tree origin/main origin/pr/88691
Closes#88629.
Co-authored-by: Charles OpenClaw <charles-openclaw@9bcfae.inboxapi.ai>
Adds opt-in `gateway.tailscale.preserveFunnel`. When `tailscale.mode = "serve"`
and an externally configured Tailscale Funnel route already covers the gateway
port, OpenClaw checks `tailscale funnel status --json` before re-applying
`tailscale serve` and skips both Serve and the `resetOnExit` teardown for that
run, preserving operator-managed Funnel exposure across gateway restarts.
The Funnel-status parser handles every documented Tailscale target scheme
(http, https, https+insecure) via an RFC 3986 scheme strip, plus loopback
hostnames (127.0.0.1, localhost, ::1) and bare-port forms. AllowFunnel-disabled
hosts and other-port routes are ignored.
Closes#57241.