fix(ios): allow plaintext ws:// for LAN/Tailscale hosts; prefer password over bootstrap token (#47887)#65185
fix(ios): allow plaintext ws:// for LAN/Tailscale hosts; prefer password over bootstrap token (#47887)#65185draix wants to merge 2 commits intoopenclaw:mainfrom
Conversation
…ord over bootstrap token (openclaw#47887) Four related bugs caused iOS LAN gateway onboarding to fail: 1. ws:// silently upgraded to wss:// for all non-loopback hosts GatewayConnectionController.shouldRequireTLS returned true for any non-loopback host, including RFC-1918 addresses (192.168.x.x, 10.x.x.x, 172.16-31.x.x), link-local (169.254.x.x), .local mDNS names, and Tailscale .ts.net addresses. Gateways on these networks commonly run without TLS, so the forced upgrade broke plain-LAN onboarding entirely. Fix: add isLocalNetworkHost() that returns true for RFC-1918, link-local, CG-NAT/Tailscale (100.64/10), .local, and .ts.net. shouldRequireTLS now returns false for these hosts when useTLS=false. 4. bootstrapToken used even when explicit password is present Mixed auth payloads (both bootstrapToken and password) were using the bootstrap path, causing AUTH_BOOTSTRAP_TOKEN_INVALID errors when a stale bootstrap token was already consumed. Fix: suppress authBootstrapToken whenever explicitPassword != nil, making password take precedence as intended. Bugs 2 and 3 (double-socket bootstrap consumption, setup-code generator emitting bootstrap-only payloads) are addressed on the gateway/TypeScript side in separate commits. Tests: 9 new tests in GatewayConnectionSecurityTests covering LAN host detection (RFC-1918, .local, .ts.net, public hosts) and TLS override behavior.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 21bca7f9cb
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
|
|
||
| // MARK: - LAN host TLS override tests (#47887) | ||
|
|
||
| @Test @MainActor func localNetworkHost_rfc1918_10x() async { |
There was a problem hiding this comment.
Move LAN tests inside GatewayConnectionSecurityTests
These new @Test functions are declared after the suite’s closing brace, so they are file-scope tests and no longer have access to makeController(), which is a private member of GatewayConnectionSecurityTests. In iOS test builds this causes compile errors (cannot find 'makeController' in scope), so the patch breaks the test target instead of adding coverage.
Useful? React with 👍 / 👎.
Greptile SummaryThis PR fixes two iOS gateway connection bugs: it stops
Confidence Score: 1/5Not safe to merge: the test target will not compile due to new tests being outside the test struct. A P0 compilation error in the test file (new tests outside the struct closure referencing a private method) means the test target cannot be built or run. A pre-existing test assertion also directly contradicts the new behavior and would fail once the scope issue is fixed. Both must be resolved before this can land. apps/ios/Tests/GatewayConnectionSecurityTests.swift requires the most attention: the new tests need to be moved inside the struct body and the stale assertion on line 110 needs to be updated.
|
|
The CI failures in this PR are pre-existing regressions in
Neither of these files is touched by this PR. The same failures appear on other open PRs branched from the same I've rebased onto the latest |
|
Thanks for the context here. I swept through the related work, and this is now duplicate or superseded. Close as superseded: maintainer PR #78140 now tracks the same #47887 mobile pairing/auth work with the narrower documented transport policy, while this PR remains unmergeable and has blocking review issues. So I’m closing this here and keeping the remaining discussion on the canonical linked item. Review detailsBest possible solution: Keep #47887 tracked through #78140 or its successor, landing a private-LAN/link-local/.local plaintext policy plus password-over-bootstrap precedence across Swift and TypeScript with aligned docs, changelog, and mobile tests. Do we have a high-confidence way to reproduce the issue? Yes. Source inspection shows current main still forces manual iOS non-loopback hosts to TLS and still selects bootstrap before password in Swift auth; the PR's own diff also statically reproduces the broken test placement. Is this the best way to solve the issue? No. The password precedence change is narrow, but the PR broadens raw tailnet plaintext contrary to current mobile guidance and leaves tests structurally broken; #78140 is the better canonical fix path. Security review: Security review needs attention: No supply-chain changes were found, but the diff broadens plaintext WebSocket eligibility to tailnet endpoints contrary to current mobile transport guidance.
What I checked:
Likely related people:
Codex review notes: model gpt-5.5, reasoning high; reviewed against c744b2c236b2. |
steipete
left a comment
There was a problem hiding this comment.
Thanks for the focused write-up. I agree #47887 is still real on current main: iOS still forces manual non-loopback hosts through TLS (apps/ios/Sources/Gateway/GatewayConnectionController.swift:687), Swift mixed auth still lets bootstrap win before password (apps/shared/OpenClawKit/Sources/OpenClawKit/GatewayChannel.swift:525), and the TypeScript gateway client still has the same mixed-auth precedence gap (src/gateway/client.ts:733). So I would keep this open, but not merge this diff as-is.
Blocking issues:
apps/ios/Tests/GatewayConnectionSecurityTests.swift: the new// MARK: - LAN host TLS override testsblock is added after the closing}ofGatewayConnectionSecurityTests. Those@Testfunctions callmakeController(), which is a private instance helper inside the test struct, so the tests need to move inside the struct. While touching this, update the existing stale.localassertion inmanualConnectionsForceTLSForNonLoopbackHosts; it still expectsopenclaw.local+useTLS: falseto force TLS, which contradicts the intended LAN behavior.apps/ios/Sources/Gateway/GatewayConnectionController.swift: the PR adds a second local-network classifier instead of using the existing shared one inapps/shared/OpenClawKit/Sources/OpenClawKit/LoopbackHost.swift:43. Please route the iOS controller through the shared helper or tighten that helper if the policy needs changing, so macOS/iOS/shared connection code does not drift.- The PR allows plaintext for
*.ts.net, but current mobile pairing docs explicitly say tailnet/public first-time connects still requirewss://or Tailscale Serve/Funnel, while only private LAN directws://remains supported (docs/gateway/discovery.md:107). Please either narrow the plaintext exception to private LAN/mDNS/link-local only, or update the docs and security rationale in the same PR after maintainer agreement.
Best shape from here: keep the password-over-bootstrap Swift fix, add the matching TypeScript authPassword precedence fix from the issue, reuse the shared host classifier for private LAN plaintext, and keep Tailscale/public transport policy explicit. Once those are aligned, this should be a good candidate for #47887 rather than a stale close.
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Fix iOS LAN/setup-code pairing policy for #47887. - Allow explicit private LAN and .local plaintext ws:// setup/manual connects where policy allows it. - Keep public hosts, .ts.net, and Tailscale CGNAT plaintext fail-closed. - Prefer explicit passwords over stale bootstrap tokens in Swift and TypeScript gateway clients. - Update setup-code/device-pair coverage, docs, and changelog with source credit for #65185. Verification: - pnpm install - git diff --check origin/main..HEAD - pnpm exec oxfmt --check --threads=1 src/gateway/client.ts src/gateway/client.test.ts src/pairing/setup-code.ts src/pairing/setup-code.test.ts extensions/device-pair/index.ts extensions/device-pair/index.test.ts - pnpm format:docs:check - pnpm test src/gateway/client.test.ts src/pairing/setup-code.test.ts extensions/device-pair/index.test.ts - cd apps/shared/OpenClawKit && swift test --filter 'DeepLinksSecurityTests|GatewayNodeSessionTests' - pnpm lint:swift passes with the existing TalkModeRuntime.swift type-body-length warning Blocked locally: - iOS app-target xcodebuild tests require unavailable watchOS 26.4 runtime here. - Testbox check:changed previously failed because the image lacks swiftlint; local swiftlint passes.
Fix iOS LAN/setup-code pairing policy for openclaw#47887. - Allow explicit private LAN and .local plaintext ws:// setup/manual connects where policy allows it. - Keep public hosts, .ts.net, and Tailscale CGNAT plaintext fail-closed. - Prefer explicit passwords over stale bootstrap tokens in Swift and TypeScript gateway clients. - Update setup-code/device-pair coverage, docs, and changelog with source credit for openclaw#65185. Verification: - pnpm install - git diff --check origin/main..HEAD - pnpm exec oxfmt --check --threads=1 src/gateway/client.ts src/gateway/client.test.ts src/pairing/setup-code.ts src/pairing/setup-code.test.ts extensions/device-pair/index.ts extensions/device-pair/index.test.ts - pnpm format:docs:check - pnpm test src/gateway/client.test.ts src/pairing/setup-code.test.ts extensions/device-pair/index.test.ts - cd apps/shared/OpenClawKit && swift test --filter 'DeepLinksSecurityTests|GatewayNodeSessionTests' - pnpm lint:swift passes with the existing TalkModeRuntime.swift type-body-length warning Blocked locally: - iOS app-target xcodebuild tests require unavailable watchOS 26.4 runtime here. - Testbox check:changed previously failed because the image lacks swiftlint; local swiftlint passes.
Summary
Four related bugs caused iOS LAN gateway onboarding to fail. This PR addresses two of them in the iOS/shared Swift layer (bugs 1 and 4 from the issue report); bugs 2 and 3 affect the TypeScript gateway side and are tracked separately.
Bug 1:
ws://silently upgraded towss://for LAN hostsGatewayConnectionController.shouldRequireTLSreturnedtruefor any host that wasn't loopback, including:192.168.x.x,10.x.x.x,172.16-31.x.x169.254.x.x*.local*.ts.net, CG-NAT100.64.x.xGateways on these networks commonly run without TLS (the documented default for LAN), so the forced upgrade caused connection hangs before auth even started.
Fix: Add
isLocalNetworkHost()that correctly identifies these address families.shouldRequireTLSnow returnsfalsefor local-network hosts whenuseTLSis explicitlyfalse.Bug 4:
bootstrapTokenused even when explicitpasswordis presentMixed auth payloads (both
bootstrapTokenandpassword) always chose the bootstrap path. When the bootstrap token was already consumed (single-use), subsequent connection attempts gotAUTH_BOOTSTRAP_TOKEN_INVALIDwhile a validpasswordwas available.Fix: Suppress
authBootstrapTokenwheneverexplicitPassword != nil, making password take precedence.Files changed
apps/ios/Sources/Gateway/GatewayConnectionController.swiftshouldRequireTLS: exclude local-network hostsisLocalNetworkHost(): new static helper_test_isLocalNetworkHost(): test hookapps/shared/OpenClawKit/Sources/OpenClawKit/GatewayChannel.swiftauthBootstrapTokenwhenexplicitPassword != nilTests
9 new tests in
GatewayConnectionSecurityTests:.localmDNS names.ts.netnamesresolveManualUseTLSbehaviour for LAN hosts, public hosts, and explicituseTLS=trueBuild verified:
xcodebuild BUILD SUCCEEDEDforgeneric/platform=iOS.Fixes #47887