Skip to content

fix(pairing): align mobile setup with secure endpoints#60128

Merged
obviyus merged 7 commits into
mainfrom
fix/mobile-pairing-secure-endpoints
Apr 3, 2026
Merged

fix(pairing): align mobile setup with secure endpoints#60128
obviyus merged 7 commits into
mainfrom
fix/mobile-pairing-secure-endpoints

Conversation

@obviyus

@obviyus obviyus commented Apr 3, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Problem: Android now requires TLS for non-loopback gateway endpoints, but pairing setup code / QR generation could still emit remote plaintext ws:// URLs and Android surfaced vague follow-on errors.
  • Why it matters: Tailscale and remote pairing flows looked supported, then failed late with generic QR or TLS-fingerprint errors instead of a usable setup path.
  • What changed: pairing setup generation now fails closed on insecure remote ws://; Android setup/manual/QR validation shares one secure-endpoint rule and shows actionable remediation; TLS probe errors now explain whether no secure endpoint exists or the secure endpoint was unreachable; docs now point remote/mobile users to Tailscale Serve or wss://.
  • What did NOT change (scope boundary): Android TLS hardening stays in place; no auth protocol or wire-format changes; localhost / emulator cleartext exceptions remain for local debugging only.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

  • Closes #
  • Related #
  • This PR fixes a bug or regression

Root Cause / Regression History (if applicable)

  • Root cause: Android's non-loopback TLS requirement and the pairing/setup-code producer were out of sync. The producer still treated remote plaintext ws:// endpoints as valid mobile pairing targets.
  • Missing detection / guardrail: no shared eligibility rule for mobile pairing endpoints, so setup-code generation, QR parsing, manual entry, and TLS probe failures each handled the same invalid state differently.
  • Prior context (git blame, prior PR, issue, or refactor if known): Android remote TLS hardening landed separately from setup-code generation and docs, which still implied raw tailnet / remote ws:// could work.
  • Why this regressed now: once Android enforced TLS for remote hosts, old QR/setup flows started producing setup codes that were structurally valid but operationally unusable.
  • If unknown, what was ruled out: N/A.

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file: src/pairing/setup-code.test.ts, src/cli/qr-cli.test.ts, src/cli/qr-dashboard.integration.test.ts, apps/android/app/src/test/java/ai/openclaw/app/ui/GatewayConfigResolverTest.kt, apps/android/app/src/test/java/ai/openclaw/app/GatewayBootstrapAuthTest.kt
  • Scenario the test should lock in: remote non-loopback plaintext ws:// must fail before mobile pairing; loopback / emulator cleartext still works; Android shows secure-endpoint guidance instead of generic parse/fingerprint errors.
  • Why this is the smallest reliable guardrail: the bug lived at the seam between pairing payload generation and Android endpoint validation, so one side alone was not enough.
  • Existing test that already covers this (if any): apps/android/app/src/test/java/ai/openclaw/app/node/ConnectionManagerTest.kt already covered the TLS-required policy for non-loopback endpoints.
  • If no new test is added, why not: N/A.

User-visible / Behavior Changes

  • openclaw qr now fails instead of emitting remote insecure mobile pairing setup codes.
  • CLI and docs now describe the flow as mobile pairing, not iOS-only pairing.
  • Android onboarding, connect-tab validation, and diagnostics now consistently say remote mobile nodes require wss:// or Tailscale Serve.
  • Android first-time TLS probe failures now distinguish missing TLS from unreachable secure endpoint.

Diagram (if applicable)

Before:
[remote ws:// config] -> [QR/setup code generated] -> [Android scan/connect] -> [generic invalid QR or TLS fingerprint error]

After:
[remote ws:// config] -> [setup generation rejected with Tailscale Serve / wss:// fix]
[remote ws:// pasted/scanned on Android] -> [explicit insecure-remote guidance] -> [use secure endpoint]

Security Impact (required)

  • New permissions/capabilities? (Yes/No) No
  • Secrets/tokens handling changed? (Yes/No) No
  • New/changed network calls? (Yes/No) No
  • Command/tool execution surface changed? (Yes/No) Yes
  • Data access scope changed? (Yes/No) No
  • If any Yes, explain risk + mitigation: openclaw qr now rejects an insecure remote pairing target that used to encode. This narrows behavior in the secure direction and prevents generating broken mobile setup codes.

Repro + Verification

Environment

  • OS: macOS
  • Runtime/container: Node 22 + Bun/pnpm repo toolchain, Android Gradle unit tests
  • Model/provider: N/A
  • Integration/channel (if any): Android mobile pairing / gateway QR setup
  • Relevant config (redacted): gateway.bind=custom|lan|tailnet, gateway.tailscale.mode=serve, optional gateway.remote.url / plugins.entries.device-pair.config.publicUrl

Steps

  1. Configure a gateway that resolves to a remote non-loopback plaintext ws:// URL.
  2. Run openclaw qr or scan/paste that setup target into Android.
  3. Compare old generic failure vs new early rejection / explicit secure-endpoint guidance.

Expected

  • Remote mobile pairing only proceeds with wss:// or Tailscale Serve.
  • Plaintext remote ws:// is rejected early with actionable remediation.

Actual

  • Verified: CLI rejects insecure remote mobile pairing URLs; Android parser/UX shows secure-endpoint guidance; valid secure / loopback flows still pass.

Evidence

Attach at least one:

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

  • Verified scenarios: targeted TS tests for setup-code + QR flows; Android unit tests for parser + bootstrap/TLS failure copy; full pnpm build; full pnpm check.
  • Edge cases checked: localhost / 127.0.0.1 cleartext remains allowed; Android emulator alias remains allowed; secure remote wss:// remains allowed; Tailscale Serve path remains preferred.
  • What you did not verify: full device/manual Android smoke on hardware; no end-to-end Tailscale Serve live run in this PR.

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? (Yes/No) No
  • Config/env changes? (Yes/No) No
  • Migration needed? (Yes/No) Yes
  • If yes, exact upgrade steps: if you were pairing Android/mobile against a raw remote ws:// endpoint, switch that flow to Tailscale Serve / Funnel or another wss:// gateway URL. Localhost and emulator cleartext flows do not change.

Risks and Mitigations

  • Risk: operators using old raw tailnet/LAN mobile pairing flows now hit a hard CLI/app rejection.
    • Mitigation: the new errors point directly at gateway.tailscale.mode=serve, gateway.remote.url, or plugins.entries.device-pair.config.publicUrl with wss://, and the docs now match the runtime policy.

@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation app: android App: android gateway Gateway runtime cli CLI command changes size: M maintainer Maintainer-authored PR labels Apr 3, 2026
@obviyus obviyus self-assigned this Apr 3, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0821b85e15

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/pairing/setup-code.ts Outdated
@greptile-apps

greptile-apps Bot commented Apr 3, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR closes the gap between Android's non-loopback TLS requirement and the pairing setup-code producer by failing closed on insecure remote ws:// URLs in both the CLI and the Android client. It also replaces the generic TLS-probe failure message with a typed GatewayTlsProbeResult that distinguishes "no TLS endpoint" from "unreachable endpoint", and propagates a shared gatewayEndpointValidationMessage across all Android input surfaces (QR scan, setup code, manual entry).

Confidence Score: 5/5

Safe to merge — security fix is correctly scoped, all call sites updated, and test coverage is solid.

No P0/P1 issues found. The core validation logic in both TypeScript and Kotlin is correct, exception classification in GatewayTls.kt is appropriate, and the three Android input sources (QR, setup code, manual) all share the same enforcement path. The http→ws/https→wss conversion inside validateMobilePairingUrl is dead code in practice but harmless. The GatewayTlsProbeResult data class allows a both-null state, but the null arm in the when expression handles it gracefully. All remaining observations are P2 or lower.

No files require special attention.

Reviews (1): Last reviewed commit: "fix(pairing): align mobile setup with se..." | Re-trigger Greptile

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 05142fbdd9

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/android/app/src/main/java/ai/openclaw/app/ui/GatewayConfigResolver.kt Outdated
@obviyus
obviyus force-pushed the fix/mobile-pairing-secure-endpoints branch from 8e0709d to e67e0a2 Compare April 3, 2026 09:03

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 525f999c03

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/pairing/setup-code.ts
Comment on lines +126 to +129
try {
parsed = new URL(url);
} catch {
return "Resolved mobile pairing URL is invalid.";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Handle scoped IPv6 hosts before URL parsing

validateMobilePairingUrl now rejects any resolved setup URL that contains a scoped IPv6 literal (for example ws://[fe80::1%25wlan0]:18789) because new URL(...) throws before the host allowlist runs. This is a regression from the previous flow (which could emit these URLs), and it breaks private-LAN pairing on link-local IPv6 networks where a zone ID is required. Fresh evidence in this commit is the new parse-then-reject branch in src/pairing/setup-code.ts that returns "Resolved mobile pairing URL is invalid." on parse failure.

Useful? React with 👍 / 👎.

@obviyus
obviyus force-pushed the fix/mobile-pairing-secure-endpoints branch from 525f999 to cd2a3f9 Compare April 3, 2026 09:21
@obviyus
obviyus merged commit 9e58a08 into main Apr 3, 2026
10 checks passed
@obviyus
obviyus deleted the fix/mobile-pairing-secure-endpoints branch April 3, 2026 09:21
@obviyus

obviyus commented Apr 3, 2026

Copy link
Copy Markdown
Contributor Author

Landed on main.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cd2a3f98cc

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +146 to +147
if (!tls && !isPrivateLanGatewayHost(host)) {
return GatewayEndpointParseResult(error = GatewayEndpointValidationError.INSECURE_REMOTE_URL)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Decode scoped IPv6 hosts before accepting private ws endpoints

The new private-LAN allowlist now accepts cleartext scoped IPv6 inputs like ws://[fe80::1%25wlan0]:18789, but the parser keeps the host as fe80::1%25wlan0 and does not normalize %25 back to %. That value is later used as the socket host (for example in TLS probing/connection setup), where Java treats it as interface 25wlan0 and fails hostname resolution, so these newly accepted setup/manual endpoints still cannot connect in practice. Normalize scoped IPv6 hostnames during parsing (decode %25 to % for the socket host) before returning a successful parse result.

Useful? React with 👍 / 👎.

steipete pushed a commit to duncanita/openclaw that referenced this pull request Apr 4, 2026
steipete pushed a commit to duncanita/openclaw that referenced this pull request Apr 4, 2026
steipete pushed a commit to duncanita/openclaw that referenced this pull request Apr 4, 2026
lovewanwan pushed a commit to lovewanwan/openclaw that referenced this pull request Apr 28, 2026
lovewanwan pushed a commit to lovewanwan/openclaw that referenced this pull request Apr 28, 2026
lovewanwan pushed a commit to lovewanwan/openclaw that referenced this pull request Apr 28, 2026
ogt-redknie pushed a commit to ogt-redknie/OPENX that referenced this pull request May 2, 2026
ogt-redknie pushed a commit to ogt-redknie/OPENX that referenced this pull request May 2, 2026
ogt-redknie pushed a commit to ogt-redknie/OPENX that referenced this pull request May 2, 2026
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 9, 2026
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 9, 2026
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 9, 2026
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 24, 2026
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 24, 2026
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 24, 2026
Nachx639 pushed a commit to Nachx639/clawdbot that referenced this pull request Jun 17, 2026
Nachx639 pushed a commit to Nachx639/clawdbot that referenced this pull request Jun 17, 2026
Nachx639 pushed a commit to Nachx639/clawdbot that referenced this pull request Jun 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app: android App: android cli CLI command changes docs Improvements or additions to documentation gateway Gateway runtime maintainer Maintainer-authored PR size: L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant