Skip to content

CLI uses LAN IP instead of loopback for local gateway calls when bind=lan #19004

@mcinteerj

Description

@mcinteerj

Summary

When gateway.bind=lan, the CLI constructs its WebSocket URL using the host's LAN IP (e.g. ws://10.30.156.186:18789) instead of ws://127.0.0.1:18789. This causes the gateway to treat same-host CLI calls as remote connections, requiring device pairing instead of auto-approving via the silent: isLocalClient path.

Root cause

buildGatewayConnectionDetails() in src/gateway/call.ts (lines ~103-113):

const bindMode = config.gateway?.bind ?? "loopback";
const preferLan = bindMode === "lan";
const lanIPv4 = preferLan ? pickPrimaryLanIPv4() : undefined;

const localUrl =
  preferTailnet && tailnetIPv4
    ? `${scheme}://${tailnetIPv4}:${localPort}`
    : preferLan && lanIPv4
      ? `${scheme}://${lanIPv4}:${localPort}`   // ← hits this branch
      : `${scheme}://127.0.0.1:${localPort}`;

When bind=lan, the gateway listens on 0.0.0.0, which accepts connections on all interfaces including loopback. But the CLI resolves to the LAN IP, so the gateway's isLocalDirectRequest() check fails (requires loopback source IP + local hostname), and device pairing is required instead of auto-approved.

Impact

All CLI tools on the gateway host fail with pairing required until the device is manually paired:

  • openclaw gateway call
  • openclaw status
  • openclaw devices list
  • Internal GatewayClient calls from tools (e.g. message tool, heartbeat cron, sync cron)

This is a chicken-and-egg problem: you can't approve pairing via the CLI because the CLI itself needs pairing.

Expected behavior

For gateway.mode=local, the CLI should always connect via 127.0.0.1 regardless of bind mode. The bind address controls which interfaces the server listens on — it shouldn't change which address the co-located client connects to. 0.0.0.0 always accepts loopback.

Only --url overrides and remote mode should use non-loopback addresses.

Suggested fix

// Always use loopback for local mode CLI connections
const localUrl = `${scheme}://127.0.0.1:${localPort}`;

The LAN/tailnet IP selection could be preserved for display/diagnostics (e.g. urlSource) without affecting the actual connection URL.

Environment

  • OpenClaw: 2026.2.16 (deploy/local, based on latest origin/main)
  • OS: Ubuntu 24.04 (ARM64, OCI)
  • Config: gateway.bind=lan, gateway.auth.mode=token
  • Install: pnpm dev (source)

Metadata

Metadata

Assignees

No one assigned

    Labels

    staleMarked as stale due to inactivity

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions