Sandbox WebSocket egress

Sandbox wss:// uses the HTTPS interception path: the container proxy terminates TLS with the Cloudflare Intercept CA, Project egress applies policy, and a referenced Secret substitutes placeholders in the HTTP/1.1 upgrade. Clients need no Iterate-specific proxy, CA, or transport code.

Codex CLI#

Stock Codex derives wss://api.openai.com/v1/responses from its OpenAI base URL and sends Authorization: Bearer <api key> on the upgrade. This is pinned to Codex 0.144.4's provider capability, WebSocket connection, and CODEX_API_KEY reader.

const secretPath = "/secrets/openai";
await itx.secrets.get(secretPath).update({
  egress: { urls: ["https://api.openai.com"] },
  material: openAiApiKey,
});
await itx.sandboxes.get("/sandboxes/codex").create({
  instanceType: "lite",
  env: { CODEX_API_KEY: `getSecret("${secretPath}")` },
});

The sandbox receives only the placeholder. codex login --with-api-key is a persistence flow and is unsuitable for a write-only placeholder.

Contract#

  • Header and URL-path placeholders follow normal project egress rules.
  • Query-string and application-frame placeholders are not substituted; frames remain opaque.
  • Secret-bearing subprotocol authentication is unsupported: a substituted Sec-WebSocket-Protocol is never reflected to the sandbox.
  • websocket-handshake.ts computes Sec-WebSocket-Accept from the sandbox client's key, removes hop-specific headers, and returns a selected protocol only when the client offered it.
  • Project and Secret Durable Objects preserve the native Response.webSocket; there is no application frame pump.

Verification#

The mandatory deployed sandbox-egress.e2e.test.ts proves the Intercept CA, HTTPS and WSS project routing, secret substitution without sandbox disclosure, built-in Node duplex text, and released ws@8.21.0 text/binary/subprotocol and exact close semantics. An outside-container control validates the fixture.

The separate billable smoke installs unmodified @openai/codex@0.144.4 and requires a complete JSONL turn, no HTTP fallback, exactly one secret use, and no key disclosure. It is opt-in because the package is large and the model call costs money:

OS_E2E_STOCK_CODEX_WEBSOCKET=1 doppler run --config <preview_N> -- \
  pnpm --dir apps/os exec vitest run --config e2e/vitest.config.ts \
  e2e/vitest/sandbox-codex-websocket.e2e.test.ts

Known Node limitation#

The sandbox's built-in Node WebSocket exchanges frames and sends its 1000 close, but does not report the reciprocal close event. The same deployed fixture returns the exact code and reason to an outside-container client and to ws@8.21.0 through both bare and authenticated paths, so the transport can carry the close frame. Pair pumps at application boundaries did not change the result.

Cloudflare's pinned container-intercept test also exercises echo and calls close() without asserting the reciprocal event. Until the deployed built-in-client assertion passes, claim transparent handshake and duplex behavior broadly, full close semantics for ws, and stock Codex support—not identical close behavior for every runtime.

This path covers HTTP/1.1 WebSocket upgrades, not gRPC, HTTP/2 interception, or first-frame credential injection.

Was this page helpful?