Install
openclaw skills install @ciklopentan/node-connect-smithDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps. Use when manual connect fails, local Wi-Fi works but VPS/tailnet does not, or errors mention pairing required, unauthorized, bootstrap token invalid or expired, gateway.bind, gateway.remote.url, Tailscale, or plugins.entries.device-pair.config.publicUrl.
openclaw skills install @ciklopentan/node-connect-smithGoal: find the one intended route from node -> gateway, verify OpenClaw config matches that route, then fix pairing/auth.
Classify the intended route before executing any command. If the route is unclear, ask at most two short questions:
same machinesame LANsame Tailscale tailnetpublic URL / reverse proxyIf the route is still ambiguous after two questions, stop and ask for:
openclaw devices list shows a pending pairing requestDo not guess from can't connect.
Do not mix topologies.
localhost or LAN IPs.If topology is same machine, short-circuit the network ladder:
127.0.0.1, localhost, or the tunnel endpoint)gateway.bind=lan, Tailscale, or publicUrl unless the route is no longer localPrefer config-driven route checks. Read only the minimum config needed to identify the intended route.
⚠️ Sequential execution only. Do not run approval checks, nodes checks, extra route/auth checks, or fallback commands until the numbered steps explicitly branch to them. Never run commands speculatively.
same machine, use a local-only route check first and skip remote-route advice unless the user is actually leaving the local machine.openclaw config get gateway.mode
openclaw config get gateway.bind
openclaw config get gateway.tailscale.mode
openclaw config get gateway.remote.url
openclaw config get plugins.entries.device-pair.config.publicUrl
Before treating config as the full truth, check whether the user is troubleshooting a manually entered route.
Ask this only when relevant:
Rules:
Run only the smallest config subset that matches the mismatch you already identified. Do not dump every route/auth key unless the mismatch type is still unclear.
If the mismatch is LAN vs loopback / local bind confusion:
openclaw config get gateway.bind
If the mismatch is Tailscale route confusion (direct tailnet bind vs Serve/Funnel vs no tailnet IP):
openclaw config get gateway.bind
openclaw config get gateway.tailscale.mode
tailscale status --json
If the mismatch is public URL / reverse proxy / Funnel / remote-gateway confusion:
openclaw config get plugins.entries.device-pair.config.publicUrl
openclaw config get gateway.remote.url
openclaw config get gateway.tailscale.mode
If the mismatch is auth-mode expectation on an otherwise correct route:
openclaw config get gateway.auth.mode
openclaw config get gateway.auth.allowTailscale
If the mismatch type is still unclear after reading the route selectors, then fall back once to the broader set:
openclaw config get gateway.mode
openclaw config get gateway.bind
openclaw config get gateway.tailscale.mode
openclaw config get gateway.remote.url
openclaw config get gateway.auth.mode
openclaw config get gateway.auth.allowTailscale
openclaw config get plugins.entries.device-pair.config.publicUrl
Run approval-state checks only after the route matches the intended topology, or when the app error explicitly shows an auth / pairing problem.
Device-pair approval path:
openclaw devices list
openclaw devices approve <requestId>
Use openclaw devices approve <requestId> after listing current pending requests. Prefer the explicit current request id over --latest, because a retried pairing request can supersede an older pending entry.
Auth detail-code branch after the route matches:
AUTH_TOKEN_MISSING → paste/set the required token first; do not rotate devices or rewrite route settings yet.PAIRING_REQUIRED → approve the pending device request.AUTH_TOKEN_MISMATCH with canRetryWithDeviceToken=true → allow one trusted retry first; if it still fails, use the token-drift recovery path.AUTH_DEVICE_TOKEN_MISMATCH → rotate or re-approve the affected device token instead of treating it like a fresh route problem.bootstrap token invalid or expired → old bootstrap payload; generate a fresh bootstrap flow only after the route is correct.unauthorized without detail code → verify the intended auth mode, token/password, and Tailscale expectation.Token / device-token drift recovery path:
openclaw config get gateway.auth.token
openclaw devices list
openclaw devices rotate --device <deviceId> --role operator
If rotation is not enough, remove the stale pairing, approve the current pending request again, then reconnect.
Use these only after route + device pairing are no longer the active problem, and only when you are diagnosing a node that should expose declared commands/capabilities beyond the default companion-app pairing flow.
Do not use this phase for default Android/iOS/macOS companion-app pairing diagnosis. Use it only when the node explicitly needs node-pair trust / declared command visibility after device pairing already succeeded.
openclaw nodes pending
openclaw nodes approve <requestId>
openclaw nodes status
The goal is to identify which route OpenClaw is configured to expose and compare that with the route the user actually needs.
Match the active config path against the rows below.
| Config signal | Expected topology | If that does not match the intended route |
|---|---|---|
gateway.bind=lan | same Wi-Fi / LAN | keep the diagnosis on LAN; do not switch to Tailscale or public URL unless remote access is actually required |
gateway.bind=tailnet | same Tailscale tailnet (direct tailnet bind, not Serve/Funnel) | verify the user intentionally wants a direct tailnet bind; if they expected Serve/Funnel or a different remote route, treat it as a route mismatch instead of silently accepting it |
gateway.tailscale.mode=serve | same Tailscale tailnet (Serve) | verify Tailscale Serve is the intended route; loopback bind can still be correct here because Serve exposes a reachable tailnet URL while the gateway stays on 127.0.0.1; do not debug LAN IPs first |
gateway.tailscale.mode=funnel | public URL via Tailscale Funnel (not generic reverse proxy) | verify Tailscale Funnel is really the intended public route; check Funnel-specific auth expectations before debugging generic proxy paths; do not debug LAN IPs first |
plugins.entries.device-pair.config.publicUrl | public URL / reverse proxy | inspect the public URL / proxy path, not LAN-only config |
gateway.remote.url | remote gateway route | inspect the remote gateway route, not local bind settings |
loopback-only config such as gateway.bind=loopback or local-only manual entry | valid only for same-machine flows unless a separate explicit proxy/Serve layer is intentionally advertising the reachable remote URL | fix the route first before changing auth/pairing assumptions |
If the route is still unclear after the route selectors, fall back to:
openclaw config get gateway.bind
openclaw config get gateway.tailscale.mode
openclaw config get plugins.entries.device-pair.config.publicUrl
openclaw config get gateway.remote.url
Then identify the effective route manually and return to the route map.
If the effective config is still loopback-only:
gateway.bind=auto is not enough if the effective route is still loopback for a route that should be LAN, direct tailnet bind, or public URLgateway.bind=langateway.tailscale.mode=serve or use gateway.bind=tailnet if direct tailnet bind is intentionalplugins.entries.device-pair.config.publicUrl, gateway.remote.url, or intentional Tailscale Funnel routeIf gateway.bind=tailnet is set but no tailnet IP was found:
If remote mode is intended but gateway.remote.url is empty:
If the app says pairing required or the auth detail code is PAIRING_REQUIRED:
openclaw devices list
openclaw devices approve <requestId>
If the auth detail code is AUTH_TOKEN_MISMATCH:
canRetryWithDeviceToken=true, allow one trusted retry firstIf the auth detail code is AUTH_DEVICE_TOKEN_MISMATCH:
If device pairing succeeds but a node still does not expose the declared commands / capabilities you expected:
openclaw nodes pending
openclaw nodes approve <requestId>
openclaw nodes status
If the app says bootstrap token invalid or expired:
If the app says unauthorized without a more specific auth detail code:
gateway.auth.allowTailscale must match the intended flowReply with one concrete diagnosis and one route.
If there is not enough signal yet, ask for setup + exact app text instead of guessing.
Good:
The gateway is still local-only, so a node on another network can never reach it. Enable the route that matches your topology, verify the app is using that route, then approve the pending device pairing.Bad:
Maybe LAN, maybe Tailscale, maybe port forwarding, maybe public URL.gateway.bind, gateway.tailscale.mode, gateway.remote.url, plugins.entries.device-pair.config.publicUrl) instead of attempting a second speculative fix.