Skip to content

fix(cli): give Windows and WSL their own service default ports - #54988

Open
Hona wants to merge 1 commit into
anomalyco:devfrom
Hona:windows-service-port
Open

Hona wants to merge 1 commit into
anomalyco:devfrom
Hona:windows-service-port

Conversation

@Hona

@Hona Hona commented Oct 13, 2026 •

Copy link
Copy Markdown
Member

Issue for this PR

Closes #53351

Type of change

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

What does this PR do?

  • Windows hands out 0xC000-0xFFFF (49152-65535) for outgoing connections, and Hyper-V/WinNAT reserves 100-port blocks of that range that move on reboot. 0xC0DE (49374) sits inside it, so the managed service fails at random with "already in use by another process" while nothing listens on the port.
  • On Windows, a default port in that range is XORed with 0x4096 (a nod to the v1 port). That clears bit 14, so every such port lands in 0x8000-0xBFFF, and ports already below 0xC000 stay put.
  • WSL distros share localhost with Windows and with each other, so each distro hashes its own default port, the same way preview channels do.
  • The port is still the service lock: startup and election logic are unchanged, and a configured port still wins.
const port = channelPort(channel, distro)
// Windows hands out 0xC000-0xFFFF for outgoing connections and Hyper-V reserves blocks of it. XOR with 0x4096, a nod
// to the v1 port, clears bit 14 and steps out of that range: 0xC0DE becomes 0x8048.
if (platform === "win32" && port >= 0xc000) return port ^ 0x4096
return port
0xC0DE = 1100 0000 1101 1110   49374, inside 0xC000-0xFFFF
       ^ 0100 0000 1001 0110   0x4096
0x8048 = 1000 0000 0100 1000   32840, outside it
Default port macOS / Linux Windows WSL
Release channels 0xC0DE (49374) 0x8048 (32840) hash of service.json:<distro>
local 0xC0DF (49375) 0x8049 (32841) hash of service-local.json:<distro>
Preview channels hash of channel hash, XORed below 0xC000 hash of service-<channel>.json:<distro>

How did you verify your code works?

  • Unit tests cover the Windows mapping (every preview channel lands below 0xC000, lower ports are unchanged) and per-distro WSL ports.
  • On Windows with an excluded range covering the configured port, serve --service still fails as before; the new Windows default sits outside the dynamic range.

Screenshots / recordings

Not a UI change.

Checklist

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

Added by the opencode bot (AI-generated).

Related: #50672, #52234, #50498

Windows hands out 0xC000-0xFFFF for outgoing connections and Hyper-V reserves blocks of it, so 0xC0DE could be taken at random. On Windows, default ports in that range are XORed with 0x4096 (a nod to the v1 port), which moves 0xC0DE to 0x8048 (32840). WSL distros share localhost with Windows and each other, so each distro hashes its own default port. The port is still the service lock; startup logic is unchanged.
Copilot AI balanced review requested due to automatic review settings October 13, 2026 01:41

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@opencode-agent opencode-agent 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.

The Windows remap looks right. I couldn't reproduce the Windows failure on Linux, so I checked the port mapping directly. I also ran serve --service from source with WSL_DISTRO_NAME set: base listened on 49375, this PR on the per-distro hash (27965). The upgrade path is fine because clients find the service through the registration file and the old-version service on 49374 gets replaced. service.test.ts passes (26/26) and the cli typecheck is clean.

Public API: the default managed-service port changes on Windows (49374 → 32840, 49375 → 32841) and becomes per-distro under WSL. No config keys, flags or HTTP API change, and a configured port still wins. Anything that hard-codes 49374 to reach the service on Windows or WSL (e.g. ssh -L forwards, bookmarks, remembered device URLs) needs the new port.

One concern, inline: the per-distro WSL ports can still fall inside the range Windows reserves for Hyper-V and outgoing connections (49152–65535).

Related: #50672, #52234, #50498

const port = channelPort(channel, distro)
// Windows hands out 0xC000-0xFFFF for outgoing connections and Hyper-V reserves blocks of it. XOR with 0x4096, a nod
// to the v1 port, clears bit 14 and steps out of that range: 0xC0DE becomes 0x8048.
if (platform === "win32" && port >= 0xc000) return port ^ 0x4096

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.

WSL ports skip this remap because platform is "linux" there. But with NAT localhost forwarding or mirrored networking, the WSL port is also used on the Windows side, so Windows' 0xC000–0xFFFF reservations still apply. Roughly a third of distros will hash into that range. For example, Ubuntu-22.04 → 50424, kali-linux → 56357 and Arch → 51286, which brings back the same random "already in use" failure for those distros. Could the remap also run when distro is set (e.g. (platform === "win32" || distro) && port >= 0xc000)?

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.

Windows: managed service fails when default port 49374 is in an excluded TCP range

2 participants