Skip to content

fix(sandbox): give each WSL sandbox its own guest broker stub port - #228

Merged
quickbeard merged 3 commits into
mainfrom
fix/wsl-stub-port
Aug 13, 2026
Merged

quickbeard merged 3 commits into
mainfrom
fix/wsl-stub-port

Conversation

@quickbeard

Copy link
Copy Markdown
Owner

Stacked on #227.

Problem

Every backend parked the guest-loopback broker stub on port 8788. That's fine for vz/bwrap — their guests have private loopbacks — but all WSL2 distributions share one VM loopback: with two Windows sandboxes running, the second socat lost the 8788 bind silently (backgrounded with &), and the second sandbox's model traffic landed on the first sandbox's broker, where the per-start auth secret rejected it with 401s.

Fix

  • The WSL provider reserves a per-sandbox stub port with the same reserveLoopbackPort() used for the server port — the Windows loopback is the right uniqueness domain because WSL republishes guest loopback binds there.
  • resolveEgress now receives { guestBrokerPort } so the desktop's gateway guest config points the model provider at the actual stub. vz passes its image-baked 8788 (codev-init), bwrap its GUEST_BROKER_PORT — no behavior change on those backends.
  • e2e-wsl.ts gains --name for concurrent instances; each instance's broker upstream returns a per-name marker, so cross-sandbox leakage shows up as a wrong marker instead of a false pass, and the guest probe discovers the stub port from the running socat instead of assuming 8788.

Verification

Unit: bun test src/wsl/ (egress test now asserts the port handed to resolveEgress is the one socat listens on, and that it isn't the shared 8788); typecheck clean in codev-sandbox and desktop.

Live, two concurrent sandboxes on Windows 11 / WSL 2.7.11 (--name alpha --hold + --name beta):

alpha: broker via cgroup: OK   (marker broker-alpha)
beta:  broker via cgroup: OK   (marker broker-beta)   ← ran while alpha held
beta:  firewall: OK, e2e OK
alpha re-probe after beta's full lifecycle:
  port:51582
  BROKER:{"marker":"broker-alpha"}

Before this fix, beta's stub would have died at bind time and its probe would have hit alpha's broker.

🤖 Generated with Claude Code

@quickbeard
quickbeard force-pushed the feat/wsl-egress-firewall branch 3 times, most recently from c09d7aa to b8f19db Compare August 13, 2026 15:42
@quickbeard
quickbeard deleted the branch main August 13, 2026 15:46
@quickbeard quickbeard closed this Aug 13, 2026
@quickbeard quickbeard reopened this Aug 13, 2026
@quickbeard
quickbeard changed the base branch from feat/wsl-egress-firewall to main August 13, 2026 15:49
@quickbeard
quickbeard merged commit a97e002 into main Aug 13, 2026
0 of 5 checks passed
@quickbeard
quickbeard deleted the fix/wsl-stub-port branch August 13, 2026 15:52
quickbeard and others added 3 commits August 13, 2026 15:50
Every backend parked the guest-loopback broker stub on 8788, which is
fine for vz/bwrap (private guest loopbacks) but not for WSL: all WSL2
distros share the VM loopback, so a second concurrent sandbox's socat
lost the bind silently and its model traffic landed on the first
sandbox's broker, where the per-start auth secret rejected it. The WSL
provider now reserves a per-sandbox stub port (against the Windows
loopback, where WSL republishes guest binds) and resolveEgress receives
the port so the gateway guest config points at it; vz and bwrap pass
their fixed 8788. e2e-wsl gains --name for concurrent instances and
per-instance broker markers so cross-sandbox leakage fails loudly.

Verified live: two sandboxes ran concurrently, each probe returned its
own broker's marker, and the first sandbox's broker still answered
correctly after the second's full lifecycle.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Docker COPYs these files into Linux guest images verbatim, so a CRLF
checkout (Windows autocrlf default) ships \r into the guest: the office
layer died on `set: pipefail: invalid option name` when built from a
Windows working tree, and codev-init would break the same way as the vz
guest's PID 1.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A Windows image build hands describeAsset a backslash path, which
split("/") returns whole, so the manifest URL embedded the local build
path verbatim.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

1 participant