Skip to content

fix(cli): remap the default managed service port on WSL - #62533

Open
echo-ilovech3ss wants to merge 2 commits into
anomalyco:devfrom
echo-ilovech3ss:fix/wsl-default-port-62532
Open

echo-ilovech3ss wants to merge 2 commits into
anomalyco:devfrom
echo-ilovech3ss:fix/wsl-default-port-62532

Conversation

@echo-ilovech3ss

@echo-ilovech3ss echo-ilovech3ss commented Nov 7, 2026 •

Copy link
Copy Markdown

Issue for this PR

Closes #62532

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

On WSL2 with default NAT networking, the previous default port 49374 (0xC0DE) falls inside the Windows TCP port-exclusion range at boot. A Windows browser cannot reach the forwarded localhost port, causing opencode pair links to fail with ERR_CONNECTION_REFUSED.

Following review feedback:

How did you verify your code works?

  • Ran bun test test/service.test.ts -t "managed service ports": all tests pass.
  • Verified defaultPort behavior across channels and platforms: win32 = 0x70de, linux = 0xc0de, wsl = 0x60de, and local channel ports (0x70df, 0xc0df, 0x60df). Tested 200 preview channel hashes to ensure Windows and WSL ports stay below 0x8000 and do not collide.
  • Ran tsc -b and full bun run check pre-push suite.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

Added by the opencode bot (AI-generated).

Related: #55907, #50672, #54988

Public API: changes the WSL default managed service port from 49374 to 24798 (Windows, macOS and Linux unchanged; WSL tunnels or saved links to :49374 need updating)

… all platforms

WSL2 NAT forwards localhost ports from a Windows browser through the
host port-exclusion range, which can cover the old 49374 default and
leave opencode pair links unreachable with ERR_CONNECTION_REFUSED.

Move the channel default into the 0x7000 block (0x70DE = 28894) by
remapping every channel port >= 0x8000, not just win32, so the same
port is reachable from native Windows and WSL2 NAT browsers.

Closes anomalyco#62532
@github-actions github-actions Bot added the needs:compliance This means the issue will auto-close after 2 hours. label Nov 7, 2026

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

Requesting changes. I couldn't verify the WSL2 NAT problem itself (this sandbox only runs Linux). I checked defaultPort on the base and with the PR: every platform now gets 28894 (and 28895 for local), where before only Windows did. The tests in packages/cli/test/service.test.ts and tsc -b both pass.

Public API: the managed service's default port changes from 49374 to 28894 on macOS, Linux and WSL (local channel: 49375 to 28895). Windows is unchanged. There are no config, flag or SDK changes. This is not fully backwards compatible:

  • An existing ssh -L 49374:… tunnel, firewall rule, or LAN pairing link saved with :49374 stops working after the upgrade, unless the user runs opencode service set port 49374.

Concerns:

  1. Windows and WSL now share one port. On the base, the native Windows service uses 28894 and WSL uses 49374, so a Windows desktop service and a WSL service can run side by side. With this PR both try to bind 28894:
    • With mirrored networking the two share one loopback, so whichever starts second fails with "port in use". That is the problem reported in #55907 and #50672.
    • With NAT, WSL's localhost forwarding also needs that same port on the Windows side.
      So this trades one WSL failure for another. WSL needs its own default below 0x8000 that differs from the Windows one. #54988 takes this route by giving each distro its own port.
  2. The change is wider than it needs to be. Moving every macOS and Linux user off 49374 isn't needed to fix a WSL-only problem. It also leaves the platform parameter unused while the tests still pass it. Limiting the remap to WSL (for example, by checking WSL_DISTRO_NAME) would keep the fix contained.
  3. The docs drop the per-platform port table. If the defaults go back to differing by platform, web.mdx needs that table again.

Related: #55907, #50672

// leave the printed pair link unreachable. Moving into the 0x7000 block below 32768
// keeps the low 12 bits (0xC0DE becomes 0x70DE) and stays reachable from Windows,
// WSL2 NAT, and every other platform.
if (port >= 0x8000) return 0x7000 | (port & 0xfff)

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.

This removes the platform check, so WSL now gets 28894, the same port as a native Windows service. With mirrored networking (and with NAT localhost forwarding), WSL and Windows share loopback ports, so a Windows desktop service and a WSL service would now fight over 28894. #55907 and #50672 describe exactly that failure. It also moves every macOS and Linux user off 49374 for no benefit to this bug. Could you limit the change to WSL, for example by checking WSL_DISTRO_NAME or /proc/version, and give WSL its own port below 0x8000 that differs from the Windows default?

Comment thread packages/cli/test/service.test.ts Outdated
expect(ports.every((port) => port.linux >= 10_000 && port.linux < 0x8000)).toBe(true)
expect(ports.every((port) => port.windows >= 10_000 && port.windows < 0x8000)).toBe(true)
expect(ports.filter((port) => port.linux < 0x8000 && port.windows !== port.linux)).toEqual([])
expect(ports.every((port) => port.windows === port.linux)).toBe(true)

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.

This assertion requires the Linux and Windows defaults to be equal, which is the situation that causes the collision when a WSL service and a Windows service share loopback. If WSL gets its own port, this should check that it differs from the Windows default instead.

WSL2 NAT forwarding passes through Windows host port-exclusion ranges, which can cover 49374. Remapping all platforms to 28894 caused port-in-use conflicts between Windows desktop services and WSL services sharing localhost (anomalyco#55907, anomalyco#50672) and changed the default for native macOS and Linux users.

Keep native Windows on 28894 (0x70DE) and macOS/Linux on 49374 (0xC0DE). Remap WSL to the 0x6000 block (0x60DE = 24798) so it stays below 0x8000 without colliding with Windows. Restore the per-platform port table in documentation.
@github-actions github-actions Bot removed the needs:compliance This means the issue will auto-close after 2 hours. label Nov 7, 2026
@github-actions

github-actions Bot commented Nov 7, 2026

Copy link
Copy Markdown
Contributor

Thanks for updating your PR! It now meets our contributing guidelines. 👍

@echo-ilovech3ss

Copy link
Copy Markdown
Author

Thanks for the thorough review @opencodejr. Pushed an update addressing each point:

  1. Distinct WSL default below 0x8000: Rather than sharing Windows's 28894 (0x70DE), WSL is now remapped into the adjacent 0x6000 block (0x60DE = 24798, local channel = 24799). This stays below 0x8000 (avoiding the Windows host exclusion range and WSL mirrored networking range), while remaining completely disjoint from Windows's 0x7000 block (28894/28895) so Windows desktop services and WSL services can run concurrently on localhost without collision (desktop: background service spawn fails "port in use" on Windows/WSL2 mirrored networking — app stuck at logo, error not logged #55907, service: hardcoded default port 49374 locks out any second client on shared loopback (multi-user/WSL mirrored), surfacing only a generic timeout #50672).
  2. Narrowed scope & active platform check: Reverted macOS and native Linux back to 49374 (0xC0DE), keeping existing SSH tunnels and firewall rules intact. WSL is detected by checking WSL_DISTRO_NAME in the environment (or explicit "wsl" platform), keeping the platform parameter active and verifiable across platforms in tests.
  3. Restored docs table: Restored the per-platform port table in web.mdx with entries for macOS & Linux (49374), Windows (28894), and WSL (24798), and reverted general code examples back to 49374.

Tests in packages/cli/test/service.test.ts have been updated and pass locally (bun test -t "managed service ports" and tsc -b).

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

My earlier concerns are resolved. WSL now gets its own default (24798), so it no longer clashes with the Windows service on 28894. macOS and Linux stay on 49374. The per-platform port table in web.mdx is back. I couldn't reproduce the WSL2 NAT failure itself, because that needs a Windows host. bun test test/service.test.ts and tsc -b both pass in packages/cli.

Public API: only WSL's default managed service port changes, from 49374 to 24798 (49375 to 24799 for the local channel). Windows, macOS and native Linux keep their current defaults. WSL users with an ssh -L tunnel or a LAN pairing link saved for :49374 need to update it, or pin the old port with opencode service set port 49374. There are no config, flag or SDK changes.

The remaining point is optional and not a blocker (see the inline comment).

Related: #55907, #50672, #54988

env?: NodeJS.ProcessEnv,
) {
const port = channelPort(channel)
const isWsl = platform === "wsl" || (env?.WSL_DISTRO_NAME !== undefined && Boolean(env.WSL_DISTRO_NAME))

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.

Only one test uses the new env parameter, and it repeats the WSL_DISTRO_NAME check that the default for platform already does. The test could pass "wsl" instead, so the parameter can be dropped to keep one way of detecting WSL. One more note: WSL_DISTRO_NAME is only set for processes started through wsl.exe. A service started over SSH into the distro, under sudo, or from a systemd unit will still get 49374. That is no worse than today, but checking /proc/sys/kernel/osrelease for microsoft would also cover those cases, if you want them.

@echo-ilovech3ss echo-ilovech3ss changed the title fix(cli): default managed service port below Windows dynamic range on all platforms fix(cli): remap the default managed service port on WSL Nov 8, 2026

This branch has not been deployed

No deployments
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.

docs: WSL default port 49374 falls inside Windows reserved range, so pair links fail in a Windows browser

1 participant