Skip to content

fix(desktop): honor local-only exposure in the WSL backend bind host - #9066

Closed
dlowzzxx wants to merge 1 commit into
pingdotgg:mainfrom
dlowzzxx:fix/issue-9056-wsl-local-only-bind
Closed

dlowzzxx wants to merge 1 commit into
pingdotgg:mainfrom
dlowzzxx:fix/issue-9056-wsl-local-only-bind

Conversation

@dlowzzxx

@dlowzzxx dlowzzxx commented Sep 1, 2026 •

Copy link
Copy Markdown

Fixes #9056.

Problem

With the WSL backend enabled, resolveWslStartConfig hardcoded a 0.0.0.0 bind regardless of the resolved DesktopServerExposure mode. With Network access off, the Connections panel states "Limited to this machine", but the WSL backend actually answers on every distro interface — including tailscale0 when Tailscale runs inside the distro, which exposes the backend to the entire tailnet with the toggle off (repro and curl evidence in the issue). The Windows primary already binds 127.0.0.1 in local-only mode via resolveDesktopServerExposure; only the WSL path diverged.

Change

  • Pass the primary's resolved exposure mode into resolveWslStartConfig as networkAccessible, read from DesktopServerExposure.getState at both call sites (the wsl-only primary and the resolveWsl secondary).
  • In network-accessible mode the behavior is unchanged (0.0.0.0, for the reasons documented in the original comment — readiness probe and renderer reachability without depending on wslhost forwarding).
  • In local-only mode, bind exactly the address the renderer is already told to use: the distro IP in NAT mode (reachable from Windows without depending on flaky wslhost forwarding, so the readiness-probe regression that motivated the wildcard bind does not come back) or loopback in mirrored mode. A wildcard bind is no longer possible while the UI claims "limited to this machine".

No UI changes, so no screenshots are attached.

Tests

  • resolveWsl wildcards the WSL bind only when network access is enabled — guards the unchanged 0.0.0.0 behavior in network-accessible mode.
  • resolveWsl binds the advertised distro IP when exposure is local-only — local-only + NAT binds the advertised host instead of the wildcard.
  • resolveWsl binds loopback in mirrored mode when exposure is local-only — mirrored mode keeps loopback.

Verification: focused suite apps/desktop/src/backend/DesktopBackendConfiguration.test.ts — 34 passed; tsgo --noEmit on @t3tools/desktop reports no errors in the touched files; oxfmt --check clean on both files.


Note

Medium Risk
Changes WSL listen addresses and desktop protocol proxy targets, which affect reachability and local-only network exposure guarantees on Windows/WSL.

Overview
Fixes local-only server exposure for WSL-backed desktops: the WSL backend no longer always binds 0.0.0.0, which could expose it on distro interfaces (e.g. Tailscale) while the UI still says “limited to this machine.”

WSL bind host now follows the primary’s exposure mode via networkAccessible from DesktopServerExposure.getState: network-accessible keeps 0.0.0.0; local-only binds the same address the renderer uses (distro IP in NAT, 127.0.0.1 in mirrored mode).

Because local-only WSL primaries advertise a non-loopback URL, the custom app:// protocol can follow the backend once it’s ready: DesktopServerExposure adds backendProxyOrigin / noteResolvedBackendOrigin, the backend pool records the resolved origin on onReady, packaged local-only bootstrap passes targetOriginOverride into ElectronProtocol, and the handler consults that override per request.

Reviewed by Cursor Bugbot for commit 749286ce90bd2a2027378f98d114986b1a07c0d3. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Fix WSL backend bind host to honor local-only exposure mode

  • WSL backend now binds to 0.0.0.0 only when exposure mode is network-accessible; in local-only mode it binds to the distro IP (NAT) or loopback (mirrored), preventing unintended exposure on other interfaces. See DesktopBackendConfiguration.ts.
  • Adds backendProxyOrigin and noteResolvedBackendOrigin to DesktopServerExposure so the protocol proxy can follow a live backend-reported origin instead of a static loopback URL. See DesktopServerExposure.ts.
  • Extends proxyRequest in ElectronProtocol.ts with an optional getTargetOrigin callback; the protocol handler reads a dynamic target origin per request.
  • When a backend becomes ready, the backend pool calls noteResolvedBackendOrigin with the backend's httpBaseUrl before opening the main window. See DesktopBackendPool.ts.
  • Risk: resolveWsl now requires a networkAccessible flag; any caller not passing it will fail to compile. The DesktopServerExposure interface change requires all stubs to implement backendProxyOrigin and noteResolvedBackendOrigin.
📊 Macroscope summarized f8444a1. 1 file reviewed, 2 issues evaluated, 1 issue filtered, 1 comment posted

🗂️ Filtered Issues

apps/desktop/src/backend/DesktopBackendConfiguration.ts — 1 comment posted, 2 evaluated, 1 filtered
  • line 643: getDistroIp obtains its result from hostname -I, which returns a non-loopback distro address, so the isLocalHostIpv4 branch cannot detect mirrored WSL networking in production. Mirrored mode mirrors the Windows LAN interfaces into WSL; the selected address can therefore be a LAN address, and local-only mode binds the backend to it here. This exposes a backend that the UI labels local-only to the LAN instead of binding 127.0.0.1 for mirrored networking. [ Already posted ]

Summary by CodeRabbit

  • Bug Fixes
    • Improved WSL backend network binding to respect the selected server exposure mode.
    • Local-only connections now bind to the appropriate local or distro address instead of being exposed on all network interfaces.
    • Network-accessible connections continue to support wildcard binding.
    • Improved handling for mirrored-mode environments using loopback addresses.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Sep 1, 2026
Comment thread apps/desktop/src/backend/DesktopBackendConfiguration.ts
@macroscopeapp

macroscopeapp Bot commented Sep 1, 2026 •

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This production WSL change alters listening addresses for the default local-only mode, and an unresolved high-severity finding indicates that a LAN or Tailscale address may still be selected, leaving the backend externally reachable. The test addition is harness-only, but the runtime exposure and security impact warrant human review.

Not approved because:

  • 1 blocking correctness issue found at or above your repo's Minimum Blocking Severity

Adjust the Minimum Blocking Severity for this repo — including turning it Off — in Settings. You can add or adjust custom eligibility rules. Learn more.

@dlowzzxx

dlowzzxx commented Sep 1, 2026

Copy link
Copy Markdown
Author

Pushed 8b4639934 addressing the review findings:

  • Protocol origin (Cursor, high): the desktop protocol's proxy target now follows the backend's resolved renderer-visible URL. DesktopServerExposure records the URL the pool reports on readiness (noteResolvedBackendOrigin / backendProxyOrigin), and the protocol consults it per request for local-only packaged runs. A wsl-only primary serving from its distro IP is therefore reachable on the app:// origin without depending on the wildcard bind; development and network-accessible runs keep today's exact target.
  • Readiness-probe behavior is unchanged — the probe already used the WSL config's advertised URL.

New test: follows the live target override once the backend resolves its URL. Focused suites (protocol 7, backend configuration 34, pool, window, wsl backend) pass; tsgo --noEmit and oxfmt --check clean on touched files.

@github-actions github-actions Bot added size:L 100-499 changed lines (additions + deletions). and removed size:M 30-99 changed lines (additions + deletions). labels Sep 1, 2026

@cursor cursor 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.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 8b46399341df0021639b9a5d794601902d78bf66. Configure here.

Comment thread apps/desktop/src/backend/DesktopServerExposure.ts Outdated
Comment thread apps/desktop/src/app/DesktopApp.ts Outdated
Comment thread apps/desktop/src/backend/DesktopServerExposure.ts Outdated
@dlowzzxx

dlowzzxx commented Sep 1, 2026

Copy link
Copy Markdown
Author

Pushed 749286ce9 addressing both findings:

  • Ref.set order (Cursor high, Macroscope): corrected to Ref.set(resolvedBackendOriginRef, Option.some(origin)) — the write now lands on the ref.
  • Runtime bridging (Macroscope): the registration input is now targetOriginOverride?: Effect.Effect<URL> and ElectronProtocol bridges it to a synchronous read itself via a context captured once in its layer — the escape lives at the Electron boundary, and DesktopApp just passes the effect for local-only packaged runs.

Protocol suite 7/7 including the override test (now driving the override through a Ref-backed Effect), backend configuration 34/34, tsgo and oxfmt clean on touched files.

resolveWslStartConfig hardcoded a 0.0.0.0 bind regardless of the resolved
DesktopServerExposure mode, so a local-only WSL backend answered on every
distro interface (including tailscale0) while the UI claimed it was
limited to this machine. Wildcard-binding now requires network-accessible
exposure; local-only binds the renderer-visible host instead.
@dlowzzxx
dlowzzxx force-pushed the fix/issue-9056-wsl-local-only-bind branch from 749286c to f8444a1 Compare September 14, 2026 18:49
@github-actions github-actions Bot added size:M 30-99 changed lines (additions + deletions). and removed size:L 100-499 changed lines (additions + deletions). labels Sep 14, 2026
@coderabbitai

coderabbitai Bot commented Sep 14, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: e5b3e267-446a-4391-be48-49c4fdb665a7

📥 Commits

Reviewing files that changed from the base of the PR and between f328db3 and f8444a1.

📒 Files selected for processing (2)
  • apps/desktop/src/backend/DesktopBackendConfiguration.test.ts
  • apps/desktop/src/backend/DesktopBackendConfiguration.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

WSL startup now selects its bind host from the server exposure mode. Network-accessible mode uses 0.0.0.0; local-only mode uses the resolved renderer host. Tests cover distro and loopback addresses.

Changes

WSL bind host selection

Layer / File(s) Summary
Exposure mode plumbing
apps/desktop/src/backend/DesktopBackendConfiguration.ts
The configuration reads serverExposure.getState().mode, converts it to networkAccessible, and passes the value through both WSL configuration paths.
Conditional WSL binding and validation
apps/desktop/src/backend/DesktopBackendConfiguration.ts, apps/desktop/src/backend/DesktopBackendConfiguration.test.ts
WSL uses 0.0.0.0 for network-accessible mode and the resolved renderer host for local-only mode. Tests validate distro and loopback hosts, including the generated httpBaseUrl.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix · Severity of issue fixed: Medium

Suggested reviewers: juliusmarminge

Merge Risk: ⚪ Minimal · up to f8444

The updated binding behavior has no confirmed merge-blocking issue in the available evidence.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 2 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: making the WSL backend honor local-only exposure when selecting its bind host.
Description check ✅ Passed The description explains the problem, the implementation, the security impact, and the test verification. It does not use the template headings exactly and omits the checklist, but it contains the req…
Linked Issues check ✅ Passed Issue #9056 requires the WSL backend bind to match the selected exposure mode. The change derives networkAccessible from DesktopServerExposure.getState. Network-accessible mode binds 0.0.0.0. Lo…
Out of Scope Changes check ✅ Passed The protocol proxy changes remain connected to issue #9056. A local-only packaged WSL backend can use a distro IP in NAT mode, so the proxy must follow the backend's resolved origin. Recording that or…
  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

// Instead, bind exactly the address the renderer is told to use: the distro
// IP in NAT mode (reachable from Windows without depending on flaky wslhost
// forwarding) or loopback in mirrored mode.
const wslBindHost = input.networkAccessible ? "0.0.0.0" : rendererHost;

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.

🟠 High backend/DesktopBackendConfiguration.ts:643

Local-only WSL backends bind to an externally reachable address when getDistroIp returns a Tailscale or other non-WSL interface first, so the backend is reachable beyond the local machine. Because wslBindHost uses rendererHost directly, select the WSL vEthernet/eth0 address or reject non-WSL private addresses before using it here.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/desktop/src/backend/DesktopBackendConfiguration.ts around line 643:

Local-only WSL backends bind to an externally reachable address when `getDistroIp` returns a Tailscale or other non-WSL interface first, so the backend is reachable beyond the local machine. Because `wslBindHost` uses `rendererHost` directly, select the WSL vEthernet/`eth0` address or reject non-WSL private addresses before using it here.

@dlowzzxx dlowzzxx closed this by deleting the head repository Oct 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

macroscope-review Opt PRs made by unvouched contributors in for Macroscope review. Vouched contributors auto-reviews size:M 30-99 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: WSL backend binds 0.0.0.0 while "Network access" is off and the UI says "Limited to this machine"

2 participants