Summary
The iOS app's GatewayConnectDeepLink.fromSetupCode method silently rejects QR setup codes generated by openclaw qr when the gateway is bound to LAN (gateway.bind=lan). The method uses LoopbackHost.isLoopbackHost which only allows 127.0.0.1/::1, but LAN mode generates ws://192.168.x.x:18789 URLs with RFC 1918 private IPs.
Root cause
In apps/shared/OpenClawKit/Sources/OpenClawKit/DeepLinks.swift, line 40:
if !tls, !LoopbackHost.isLoopbackHost(hostname) {
return nil
}
This check was introduced in commit ebae6f918e1 (PR #21970) as a security hardening measure. However, it is too strict — it blocks the primary use case for LAN QR pairing.
The same file's sibling utility LoopbackHost.isLocalNetworkHost (in LoopbackHost.swift) already handles RFC 1918 private IPs, .local hostnames, Tailscale .ts.net addresses, and bare hostnames. This method exists but is not used by fromSetupCode or the deep link parser (line 135).
The same over-restriction exists in DeepLinkParser.parse at line 135 for the openclaw://gateway deep link path.
Steps to reproduce
- Configure gateway with
gateway.bind=lan in openclaw.json
- Start gateway:
openclaw gateway
- Generate QR:
openclaw qr
- QR encodes payload like:
{"url":"ws://192.168.1.249:18789","token":"..."}
- Open iOS app onboarding → Scan QR
- Nothing happens —
fromSetupCode returns nil, scan silently fails
Expected behavior
The iOS app should accept ws:// setup codes pointing to local network addresses (RFC 1918, .local, bare hostnames, Tailscale IPs), since these represent the primary LAN pairing workflow.
Suggested fix
Change isLoopbackHost to isLocalNetworkHost on both lines:
DeepLinks.swift:40 (fromSetupCode):
if !tls, !LoopbackHost.isLocalNetworkHost(hostname) {
return nil
}
DeepLinks.swift:135 (DeepLinkParser.parse, gateway case):
if !tls, !LoopbackHost.isLocalNetworkHost(hostParam) {
return nil
}
This preserves the security intent (reject ws:// to public internet hosts) while allowing the documented LAN pairing flow.
Environment
- OpenClaw:
main (latest)
- iOS app: built from source
- Gateway bind:
lan
- Device: iPhone on same LAN as gateway host
Related issues
— Joel Nishanth · offlyn.AI
Summary
The iOS app's
GatewayConnectDeepLink.fromSetupCodemethod silently rejects QR setup codes generated byopenclaw qrwhen the gateway is bound to LAN (gateway.bind=lan). The method usesLoopbackHost.isLoopbackHostwhich only allows127.0.0.1/::1, but LAN mode generatesws://192.168.x.x:18789URLs with RFC 1918 private IPs.Root cause
In
apps/shared/OpenClawKit/Sources/OpenClawKit/DeepLinks.swift, line 40:This check was introduced in commit
ebae6f918e1(PR #21970) as a security hardening measure. However, it is too strict — it blocks the primary use case for LAN QR pairing.The same file's sibling utility
LoopbackHost.isLocalNetworkHost(inLoopbackHost.swift) already handles RFC 1918 private IPs,.localhostnames, Tailscale.ts.netaddresses, and bare hostnames. This method exists but is not used byfromSetupCodeor the deep link parser (line 135).The same over-restriction exists in
DeepLinkParser.parseat line 135 for theopenclaw://gatewaydeep link path.Steps to reproduce
gateway.bind=laninopenclaw.jsonopenclaw gatewayopenclaw qr{"url":"ws://192.168.1.249:18789","token":"..."}fromSetupCodereturnsnil, scan silently failsExpected behavior
The iOS app should accept
ws://setup codes pointing to local network addresses (RFC 1918,.local, bare hostnames, Tailscale IPs), since these represent the primary LAN pairing workflow.Suggested fix
Change
isLoopbackHosttoisLocalNetworkHoston both lines:DeepLinks.swift:40(fromSetupCode):DeepLinks.swift:135(DeepLinkParser.parse, gateway case):This preserves the security intent (reject
ws://to public internet hosts) while allowing the documented LAN pairing flow.Environment
main(latest)lanRelated issues
— Joel Nishanth · offlyn.AI