Application ports
The E2B gateway forwards HTTP and WebSocket requests for {port}-{sandboxID}.<domain> to the same port on loopback inside the sandbox. This page shows how to reach a port, how to make it private, how requests reach the application, and what to expect from desktops and Chrome DevTools.
Overview
When the gateway starts, KIBOTOS_E2B_DOMAIN sets the sandbox domain, for example sandbox.example. After that:
| Domain | Purpose |
|---|---|
api.sandbox.example | Control API |
49983-{sandboxID}.sandbox.example | The envd protocol, handled by a separate listen address |
{port}-{sandboxID}.sandbox.example | Application ports, forwarded to the same port on loopback inside the sandbox |
- Application ports share the listen address of the control API. There is no extra port.
portis a decimal number from 1 to 65535, andsandboxIDis the sandbox ID returned by the API.- The deployer provides wildcard DNS resolution, TLS certificates and their renewal, and reverse proxy support for WebSocket upgrades and streaming responses. See Configure the reverse proxy.
Public and private ports
As in E2B, application ports are public by default. The network.allowPublicTraffic create option decides:
- If there is no
networkfield, ornetworkhas noallowPublicTraffic(for example, it has only outbound fields), the ports are public. - Only an explicit
allowPublicTraffic: falsemakes the ports private: requests must then carry the traffic token.
The following create options make the ports private. The connection options are the same as in the example:
const sandbox = await Sandbox.create('kibotos-minimal', {
// apiKey, domain, apiUrl as in the example
network: { allowPublicTraffic: false },
});The setting is fixed at creation. GET /sandboxes/{id} and the list endpoint echo network.allowPublicTraffic with the value from creation. For which SDK calls keep the echoed fields, see Sandboxes and lifecycle.
Traffic token
- Requests to a private sandbox must carry
e2b-traffic-access-token. - Requests to a public sandbox can omit the token. A wrong or duplicated token still returns 401.
- An API key or envd token cannot stand in for the traffic token.
The following request reaches port 8080 of a private sandbox:
curl -H "e2b-traffic-access-token: $traffic_token" "https://8080-$sandbox_id.sandbox.example/"- When a request carries a token that fails signature verification, the gateway returns 401 immediately, without checking whether the sandbox exists.
- When the token is correct, or a public sandbox is accessed without a token, a sandbox that does not exist returns 404.
- Whether the current execution of the sandbox still exists is decided by the node. After the execution ends, the node rejects the old credential and the gateway returns 401. The SDK only needs to connect again.
Host header
By default, the application receives the original Host of the request. The network.maskRequestHost create option rewrites it:
const sandbox = await Sandbox.create('kibotos-minimal', {
// apiKey, domain, apiUrl as in the example
network: { maskRequestHost: 'localhost:${PORT}' },
});- A valid non-empty string is persisted and rewrites the Host in HTTP and WebSocket requests to application ports.
${PORT}in it is replaced with the actual port. - When the option is absent,
null, or an empty string, the original Host is preserved. - An invalid value or wrong type returns 400.
- The option can be set only at creation. It does not affect the outbound network policy and does not apply to the envd port.
GET /sandboxes/{id}and the list endpoint echonetwork.maskRequestHostwith the value from creation. An empty value is not echoed.
Host handling matches the official E2B implementation (pool/client.go in infra ee356007d2362150895f024239d1d7e2e41114aa): the Host is rewritten only when maskRequestHost was set at creation. The gateway never rewrites it to a fixed localhost.
Forwarding behavior
Requests seen by the application
- The original URL path and query string, the application's own
Authorization, and other end-to-end request headers are preserved, as is the Host unlessmaskRequestHostrewrites it. - The gateway removes hop-by-hop headers, the headers listed in the
Connectionheader, the traffic token, the envd token, the API key, and the gateway's internal routing headers. - The Kibotos session credential is used only to connect to the node and never appears in application requests or logs.
- The gateway does not replay requests. When the application returns 401 or 403, the caller decides whether to retry.
HTTP
- Each request opens its own connection to the guest. Connections are not reused, and requests are not retried.
- For HTTP/1 requests with a body, the gateway allows transfer in both directions before it handles authentication. A 401, a 503, or an early response from the application can then come back before the upload finishes, instead of deadlocking while waiting for the request body.
- Upload and download run in parallel, and backpressure propagates to both ends. After the request body is fully written, the gateway closes the write direction. After reading EOF from the guest side, it confirms that the connection ended normally.
- Responses do not declare a
Content-Lengthup front. If an error occurs mid-transfer, the HTTP response is truncated. Responses without a body, such as HEAD, 204, and 304, send their headers only after the gateway confirms that the connection ended normally.
WebSocket
- The handshake and data frames are forwarded unchanged. When the client closes its write direction, only the write direction toward the guest is closed, and the application can keep sending data.
- After the upgrade to WebSocket, an error can only close the connection. The gateway does not fabricate a close frame or send an HTTP status code afterward.
Idle timeout
The gateway closes a connection on an application port when no bytes have moved in either direction for 15 minutes.
Important
On Kibotos, a WebSocket with no data at all for 15 minutes is closed. Upstream E2B applies an idle limit only between requests and sets no limit on upgraded connections. Applications that stay quiet for long periods need to send their own heartbeats.
- As long as bytes are moving, the connection is not affected by this limit.
- When the client pauses reading, data waits for it to resume and is not truncated.
When connections close
The following events close an open connection: the request is canceled, the sandbox is deleted, the gateway shuts down, or the sandbox's execution ends or its data-plane credential is revoked.
- The lifetime of the session credential limits only new connections. Established connections are not closed when the credential expires, and the gateway sets no fixed maximum connection duration.
- When an execution ends or a credential is revoked, the node closes the connection. The gateway does not poll the sandbox state.
- A
timeoutrequest that extends the lifetime does not affect existing connections. After the lifetime is shortened, ctrl stops the sandbox when it expires, and the connections close with it. The gateway stores no expiration time of its own and performs no expiration action.
Session credentials
The gateway reaches the guest only through service connections that the node establishes. It does not resolve the host network or connect to the guest's IP directly, and it has no tunnel protocol of its own.
For each (sandbox, port) pair, the gateway caches a SERVICE_CONNECT session credentialA short-lived credential for the data plane. You request it from ctrl, together with the address of the node that runs the Workspace. that covers only that port and lives as long as the current execution (lifetime = EXECUTION). The first time an instance accesses a port, the gateway requests the credential from ctrl once. After that, it uses the cached credential to open service connections to the guest.
When the node rejects this credential:
- The gateway discards it and immediately asks ctrl for a new one. If ctrl indicates that the execution has changed, the gateway returns 401. Otherwise it returns 502, and the next request uses the new credential.
- If the new credential is also rejected, the gateway waits 1 second before asking ctrl again for this port, doubling the wait each time up to 30 seconds. Requests during the wait return 502 immediately.
- Once the node successfully establishes a connection, normal behavior resumes.
Status codes
| Situation | Status code |
|---|---|
| Querying the sandbox state failed, or Kibotos is temporarily unavailable | 503 |
| The token was revoked, or the sandbox has restarted | 401 |
| The node rejected the session credential while the execution has not changed, or the port is in the wait period described in Session credentials | 502 |
Only 401 means the caller needs to refresh the token. Infrastructure failures return 503 so that callers do not refresh needlessly.
Desktop and Chrome DevTools
The following applies to callers such as Memoh that use the desktop and browser through ports 6080 and 6081:
- 6080 is the desktop entry point of noVNC/websockify inside the guest. It requires an X desktop, a VNC server, and noVNC in the guest image. The gateway only forwards traffic and does not provide a desktop.
- 6081 is the port gateway of websockify in the image. Query parameters such as
/?token=9222are how it chooses the target port. They are not the gateway's traffic token, and the gateway passes them through unchanged. The websockify process connects the WebSocket frames to the guest loopback. - The gateway does not rewrite the Chrome DevTools discovery JSON. Callers usually parse
webSocketDebuggerUrl, keep only the path and query, and then connect to 9222 through 6081. - When forwarded through 6081, the inner request's Host is loopback, which Chromium accepts. When 9222 is accessed directly as a public port, Chromium may reject the request because the Host is not loopback. This is Chromium behavior, and the gateway does not disguise the Host.
- If the image has no 6081, callers can access the public port directly, but Chromium is not guaranteed to accept it. Verify these capabilities separately with real images.
Next steps
- E2B compatibility: the supported SDK areas and what is not supported.
- Network: the outbound network policy of a sandbox.
- Sandboxes and lifecycle: pause, resume, and expiration, which end port connections.
- Services and ports: reaching ports in a Workspace with
kiboor the native API.
Metrics
The E2B SDK's sandbox.getMetrics() and Sandbox.getMetrics(id) return CPU, memory, and disk samples for a sandbox, as on E2B. This page covers the two metrics endpoints, the fields of each sample, and how time ranges and buckets work.
Choosing an environment
This page helps you get the software your Agent needs into a Workspace: use a Template the deployer published, install at runtime, or have a new Template built.