Skip to content

Share this host: only offer addresses the server actually answers (no dead IPv6 links) - #31447

Merged
nwparker merged 1 commit into
mainfrom
nwparker/pairing-address-listener-family
Oct 21, 2026
Merged

nwparker merged 1 commit into
mainfrom
nwparker/pairing-address-listener-family

Conversation

@nwparker

@nwparker nwparker commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 2 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​179 $\color{#cf222e}{\Huge{\mathbf{−}}}$​46 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​133
Prod 3 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​39 $\color{#cf222e}{\Huge{\mathbf{−}}}$​4 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​35

ELI5

When you share this computer with another device, Orca offers a list of addresses to put in the link. Orca's server only answers on IPv4 addresses, but that list also offered IPv6 addresses. If you picked one (or it was the only one), the link pointed at a door nobody answers, and the other device said "Host unavailable". Now the list only offers addresses the server actually answers.

What Changed

  • Before: on a Mac (or any host) with a global or ULA IPv6 address, Settings → Remote Orca Servers → Share this host → Another device listed that IPv6 address under Connection address. Generating a link with it produced ws://[<v6>]:6768, while the runtime listens on 0.0.0.0:6768 (IPv4 only), so every connection, even from the host itself, was refused. The mobile QR pairing list had the same entries, and on an IPv6-only host the default address was the dead IPv6 one.
  • After: the address list (and the automatically chosen default, which is picked from the same list) only contains addresses of a family the runtime listener accepts. With today's 0.0.0.0 listener that means IPv4 only. On a host with no usable IPv4 address, the runtime link reports "no reachable address" instead of minting a dead link; mobile pairing falls back to Relay, or LAN-only refuses with its existing guidance.
  • Mechanism: getPairingNetworkInterfaces (the single source for the picker and for getDefaultPairingAddress) now takes a required listenerHost and drops interfaces whose family that host does not serve. The value comes from a new OrcaRuntimeRpcServer.getNetworkExposureBindHost(), which returns the host the listener binds once it is widened for pairing (pinnedBindHost ?? 0.0.0.0). It reports the widened host rather than the current bind because the listener is still on loopback until the user generates a link. An IPv6 bind (::) is treated as dual-stack, so if the bind ever changes, IPv6 entries come back with no other edits.

Not changed: addresses the user types (Custom), orca serve --pairing-address, and the SSH/orcad tunnel paths. Those are explicit choices that may sit behind a proxy or tunnel. Headless orca serve never enumerated interfaces. The default bind scope and family are not touched.

Why

The issue offered two options: advertise only addresses the listener serves, or also listen on IPv6. Binding :: would expose the runtime on another address family. That is a security and product decision already being tracked in #31387 and #31223, so it does not belong in a bug fix. Making the offered list honest about the listener fixes the dead link without widening exposure. Because the filter is derived from the listener's bind host at the one function every picker and default goes through, it stays correct if the bind changes later. A hardcoded "no IPv6" rule would not.

Wire compatibility: nothing on the wire changes. This only affects which address the host puts into new links, and the address it now picks is one older clients can already reach.

Linked Issue

Fixes #31161

Thanks @fongyuan-2011 for the precise report (listener lsof output, [::1] refusal, and the confirmed IPv4 workaround made the cause obvious).

The Chinese wording feedback about the orca serve hint is already addressed on main by #31082 (shipped in v1.4.233): the hint now reads "在无窗口模式下运行 Orca?请在那台电脑上运行" with the command in a separate <code> element.

Visual Proof

N/A. The only visible change is that IPv6 entries no longer appear in the Connection address dropdown. No layout or component changed.

Testing

  • I manually tested these changes locally

  • Automated tests added/updated, or explained why not below

  • New src/main/ipc/mobile-pairing-listener-family.test.ts:

    • IPv4 listener: IPv6 entries are omitted.
    • Dual-stack listener: both families are kept, and link-local is still excluded.
    • IPv6-only host with an IPv4 listener: no runtime link is minted.
    • A real OrcaRuntimeRpcServer, once widened to 0.0.0.0, offers and advertises only the IPv4 address.
    • Three of these fail on main.
  • src/main/ipc/mobile.test.ts: stubs now report the listener bind host. The two IPv6-inclusion tests moved to the new file under a dual-stack listener.

  • Verified live on macOS (this machine has ULA IPv6 on bridge100 and a Tailscale v6). With real os.networkInterfaces() and a real Node listener on 0.0.0.0, main's function offered fd53:… (connection refused) and the Tailscale v6 (timed out). With the fix only IPv4 entries are offered, and every one accepted a TCP connection. Node 0.0.0.0 refusing ::1 while :: accepts both was also confirmed live.

  • Not verified live: the full Electron Settings UI flow and pairing a second Mac.

  • Ran every step of the "static analysis and typecheck" job locally, and all passed except one: the orcad terminal smoke failed in its native file-watcher preflight, which has a hard 5s timeout, while the machine was at a load average of about 230. That preflight runs before any pairing code. CI runs the smoke.

AI Disclosure

Review

  • Codex review (gpt-6.1-sol, high): clean.
  • Independent Opus review: no P0–P2 findings. Fixed P3s: the comment overclaimed for single-address pins, :: spelled other ways is now treated as dual-stack, and the test cleans up its temp dir.

Agent skill upstream boundary

  • Not applicable, or this change copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.

Notes

Security: no exposure change. Cross-platform: this is pure Node net.isIP logic and the Windows default-route path is unchanged. SSH/remote: explicit, tunnel, and orcad addresses are unchanged. Mobile: the QR default no longer picks an unreachable IPv6, and Relay still pairs without a direct address. Backwards compatibility: no wire change. Performance: none.

Checklist

  • This PR is small and focused
  • I explained what changed and why (ELI5, the user-facing before/after, the mechanism, and why over the alternatives)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred)

The runtime WebSocket listener widens to 0.0.0.0, which never accepts
IPv6, yet the Connection address picker and the default pairing address
offered global IPv6 interfaces, minting links nothing answers. Filter the
interface list by the family of the host the listener binds when serving
the network.

Reported-by: @fongyuan-2011
@coderabbitai

coderabbitai Bot commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 13 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 10 included reviews currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 5bf8b80d-1e81-4c86-9da2-0932628e1889
📥 Commits

Reviewing files that changed from the base of the PR and between 45ee56e and f171b2f.

📒 Files selected for processing: 5
  • src/main/ipc/mobile-pairing-listener-family.test.ts
  • src/main/ipc/mobile.test.ts
  • src/main/ipc/mobile.ts
  • src/main/runtime/pairing-network-interfaces.ts
  • src/main/runtime/runtime-rpc/runtime-rpc-network-exposure.ts
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@nwparker
nwparker merged commit c7f722b into main Oct 21, 2026
40 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Remote Orca Server advertises IPv6 pairing URL while listening only on IPv4 (Host unavailable)

1 participant