Skip to main content
The sandbox has its own network stack behind a user-mode proxy, reachable at the fixed guest IP 192.168.127.2. That is why a forwarded service must bind 0.0.0.0 rather than 127.0.0.1.

Quick Example

Port forwarding: expose an in-sandbox HTTP service to the host

The following starts an HTTP server inside the sandbox (listening on 0.0.0.0:18789) and reaches it from the host at 127.0.0.1:18789. It is runnable as-is.

Egress allowlist: permit only specific hosts

Node: port forwarding plus egress allowlist

Parameters and Returns

Port forwarding (ports)

Accepted by SimpleBox(..., ports=[...]) and BoxOptions(ports=[...]). Each element may be a tuple or a dict: Underlying PortSpec fields (source: sdks/python/src/options.rs):
Node: ports is Array<{ hostPort?: number; guestPort: number; protocol?: string }> (camelCase).

Egress allowlist network (NetworkSpec)

Python constructor: NetworkSpec(mode, allow_net=[]) Three typical combinations:
Node: network is the object { mode: "enabled" | "disabled", allowNet?: string[] } (camelCase allowNet).

Return value (for verification)

SimpleBox.exec(...) returns an ExecResult:

Troubleshooting

Port forwarding succeeds but the host cannot connect: the service bound to 127.0.0.1

This is the most common issue. If the in-sandbox service binds to 127.0.0.1 (loopback), traffic forwarded in by gvproxy arrives on the sandbox NIC (192.168.127.2) and cannot reach loopback.
  • Symptom: urllib.request.urlopen / curl on the host reports Connection refused or times out.
  • Fix: the in-sandbox service must bind to 0.0.0.0, for example python -m http.server 18789 --bind 0.0.0.0, Flask app.run(host="0.0.0.0"), or uvicorn --host 0.0.0.0.

The egress allowlist appears not to work / mistaking “blocked” for “errors out”

An allowlist block is not “the connection errors out”; it is a DNS sinkhole: a disallowed host resolves to 0.0.0.0.
  • How to verify: run exec("nslookup", "<host>") and check whether result.stdout contains 0.0.0.0, rather than checking exit_code (a sinkholed nslookup may still return exit_code == 0).
  • So to decide “was it blocked”, inspect the stdout content, as in the second Quick Example.

After mode="disabled", all networking commands fail (this is expected)

In disabled mode the sandbox has no network interface: nslookup, pip install, apk add, and similar commands fail (exit_code != 0), while echo, ls, and reading or writing files work normally. This is by design. To use the network, switch to mode="enabled".

exec returns a non-zero exit code without raising

exec does not raise on a non-zero exit code; instead it returns ExecResult(exit_code != 0). Always check result.exit_code. When the command itself does not exist (for example, nslookup is not installed in the image), a standard RuntimeError / bare Error may be raised (not a BoxliteError subclass), so use a broad try/except Exception.

Host port already in use / re-creating a box with the same name

Startup fails when the host host_port is already in use. Switch to a free port, or confirm no leftover box from a previous run is still holding the port. When using name=, combining it with reuse_existing=True lets you reuse an existing box of the same name and avoid “already exists” errors.

Notes on UDP port forwarding

protocol="udp" is accepted at the SDK layer.
Current limitation: the CLI -p port forwarding treats forwarding as TCP. Configure UDP port forwarding through the SDK.

No hardware virtualization prevents the sandbox from starting

The network code itself is fine, but if the environment lacks virtualization (Linux without /dev/kvm, KVM not passed through in a container, Windows without WSL2 + KVM), the box fails during startup and raises an exception. macOS (Apple Silicon) uses Hypervisor.framework automatically and does not need /dev/kvm. Catch it with try/except (Python) or try/catch (Node); the process will not crash.