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 on0.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:portsisArray<{ hostPort?: number; guestPort: number; protocol?: string }>(camelCase).
Egress allowlist network (NetworkSpec)
Python constructor: NetworkSpec(mode, allow_net=[])
Three typical combinations:
Node:networkis the object{ mode: "enabled" | "disabled", allowNet?: string[] }(camelCaseallowNet).
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 to127.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/curlon the host reportsConnection refusedor times out. - Fix: the in-sandbox service must bind to
0.0.0.0, for examplepython -m http.server 18789 --bind 0.0.0.0, Flaskapp.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 to0.0.0.0.
- How to verify: run
exec("nslookup", "<host>")and check whetherresult.stdoutcontains0.0.0.0, rather than checkingexit_code(a sinkholednslookupmay still returnexit_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 hosthost_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.

