Skip to content

fix(sandbox): resolve the gateway url and buffer broker request bodies - #215

Merged
quickbeard merged 1 commit into
mainfrom
fix/sandbox-broker-egress
Aug 13, 2026
Merged

quickbeard merged 1 commit into
mainfrom
fix/sandbox-broker-egress

Conversation

@quickbeard

Copy link
Copy Markdown
Owner

Two defects found by the first real in-app sandbox chat on macOS. Either one alone left a sandbox unable to reach a model, so the broker work (#209) has never actually served a completion until now.

1. Gateway URL never resolved

resolveSandboxEgress required base_url from the hub auth file — but that field is only written by the custom-provider flow. An SSO login records gateway_url instead, which is the common case. Result: gateway resolved to null, so the sandbox got no broker routes and a guest config containing only {"permission":"allow"} — no provider at all. Observed directly in the guest:

$ cat /codev-config/codev.json
{ "permission": "allow" }

Fix: fall back to loadGatewayUrl(), which normalizeGatewayBaseUrl turns into exactly the /v1 base the host's own working config uses (verified against ~/.config/codev/codev.jsonc: https://netmind.viettel.vn/gateway/v1).

2. Electron's net.fetch rejects streamed request bodies

With the config fixed, the guest reached the broker and every request came back Bad Gateway: net::ERR_INVALID_ARGUMENT. The broker forwards the guest's request body as a stream (Readable.toWeb) plus duplex: "half"; net.fetch accepts neither. Fix: buffer the body in the desktop's fetchImpl and drop duplex. Model requests are small JSON payloads, and the response stays streamed, which is what token streaming needs.

Verification

A real completion, requested from inside the NIC-less guest over the full chain (guest loopback → vsock → host broker → token swap → gateway):

{"id":"chatcmpl-…","model":"MiniMax/MiniMax-M3",
 "choices":[…],"usage":{"prompt_tokens":185,"total_tokens":195}}

Also confirmed in the same run: the guest config now carries the netgate provider pointed at http://127.0.0.1:8788/llm/v1 with the placeholder key (real token stays host-side), and permission: "allow" from #210.

Fork-only file; no DIVERGENCES row. No CHANGELOG entry — this fixes #209/#212 work that is still under Unreleased, so the existing entry describes the shipped behavior correctly.

🤖 Generated with Claude Code

Two defects found by the first in-app sandbox chat, both of which left
the sandbox unable to reach a model.

The egress resolver only read base_url from the hub auth file, which
only the custom-provider flow writes; an SSO login records gateway_url
instead. The common case therefore produced a sandbox with no routes
and a guest config with no provider at all. Fall back to
loadGatewayUrl(), which normalizes to the same /v1 base the host's own
config uses.

Electron's net.fetch then rejected every forwarded request with
net::ERR_INVALID_ARGUMENT: the broker passes the guest's body through
as a stream, which it cannot take. Buffer the body (model requests are
small JSON) and drop the duplex flag; the response stays streamed.

Verified end to end: a chat completion now returns to the guest through
guest loopback -> vsock -> broker -> gateway, with the real token
attached host-side.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@quickbeard
quickbeard merged commit ebd2fd5 into main Aug 13, 2026
0 of 5 checks passed
@quickbeard
quickbeard deleted the fix/sandbox-broker-egress branch August 13, 2026 06:06
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