Networking¶
Sandboxes have internet access by default. To turn it off:
from sandweave import Sandbox
with Sandbox(network="offline") as env:
print(env.run("python -c 'print(2 + 2)'").stdout)
For a cluster sandbox:
Replace lab with the full printed cluster address when connecting from another
machine. Offline networking applies inside the sandbox. The Python client
and worker can still communicate with the controller and perform sandbox actions.
Connect sandboxes¶
Create a service network, then join independent sandboxes to it. This works with any gVisor template, including custom GNOME desktops:
from sandweave import Sandbox, create_service_network, delete_service_network
target = "lab" # Or the full cluster address. Omit target for local sandboxes.
group = create_service_network(target=target)
api = desktop = None
try:
api = Sandbox(target=target, service_network=group,
aliases=["api"], networks=["private"], network="offline")
api.exec("python -m http.server 8000 --bind 0.0.0.0")
desktop = Sandbox(target=target, template="gnome", service_network=group,
aliases=["desktop"], networks=["private"], network="offline")
# The desktop can reach http://api:8000, including from its browser.
print(desktop.info["service_network"])
finally:
for env in (desktop, api):
if env is not None:
env.terminate()
env.close()
delete_service_network(group, target=target)
The server binds inside its sandbox. This does not publish a host port.
network= still controls outside access: offline members can talk to peers,
but cannot reach the internet. Internet, allowlist and proxy policies continue
to govern all other traffic.
service_networkidentifies the group and its worker. All members are placed on that worker. If it is full, cluster requests wait for capacity rather than moving to another worker;startup_timeoutbounds that wait.networksnames the segments a member can use, defaulting to["default"]. Members must share at least one name to communicate, even by IP address.aliasessupplies DNS names, defaulting to[]. Names become visible when a member joins, including to already-running peers. An alias must be unique among members sharing a named network.env.info["service_network"]contains the ID, aliases, named networks and service IPv4 address. That address remains fixed for the member's lifetime.
Private packets pass through the worker's hub over private local sockets. The hub checks membership and assigns the source identity; guests cannot choose another member's source address or bypass the hub to reach a different segment. This is a worker-local network, not an encrypted link between workers. A member of two named networks can intentionally act as an application gateway between them, so grant multi-network membership only where intended.
Termination removes membership. close() alone disconnects an ordinary sandbox
handle; it does not terminate its runtime. Networks persist until explicitly
deleted, and deletion refuses active members. A lost worker's network does not
migrate; create a new network for replacement sandboxes.
For proxy-aware tools, include peer aliases in their proxy bypass setting
(for example NO_PROXY=api), so private requests use the service network. An
unknown DNS name falls back to the sandbox's ordinary egress policy.
Filesystem caches are supported. Restoring one creates fresh membership when
you explicitly pass service_network=...; it does not restore a source member's
address. Memory checkpoints of joined members are unsupported because rolling
back one member independently would also roll back live connections to peers.
Existing Harbor service groups remain separate and cannot join another group.
The client, controller and workers need Sandweave 0.2.24 or newer.
Install dependencies before going offline¶
Worker preparation may download a runtime or template on first use, even when the requested sandbox is offline. Install software and save a filesystem cache before running an offline workload:
from sandweave import Sandbox
with Sandbox(setup="./install-tools.sh") as builder:
ready = builder.cache("tools-ready")
with Sandbox(cache=ready, network="offline") as env:
print(env.run("python --version").stdout)
Provide your own install-tools.sh. The second sandbox starts from the saved
files. A setup script that requires internet cannot download packages after
offline mode is enabled.
Restrict destinations¶
Allow only selected hostnames or IPv4 addresses:
from sandweave import Network, Sandbox
network = Network("allowlist", allowed_hosts=("pypi.org", "*.pythonhosted.org"))
with Sandbox(network=network) as env:
print(env.run("python -m pip install requests").returncode)
The policy permits IPs returned by DNS for those names. It does not check HTTP
Host or TLS SNI names when sites share an IP. Unlisted destinations are blocked;
IPv4 addresses and CIDRs are also accepted. This requires the 0.2.20rc7 preview
on the client, controller and workers. Harbor tasks apply their phase policies
automatically.
Use a proxy¶
import json
from sandweave import Network, Sandbox
with open("proxies.json") as file:
proxies = json.load(file)
with Sandbox(network=Network(proxy=proxies)) as env:
print(env.run("curl -s https://api.ipify.org").stdout)
print(env.info["network"])
proxies.json contains a JSON list of URLs, for example:
Keep this file private. You can also pass one URL directly to Network(proxy=...).
By default, each sandbox randomly selects one supplied proxy and keeps it for its
lifetime. Selection happens separately for each pool sandbox. Random selection can reuse
an address; pass a specific URL when you need an exact assignment. Ten static
proxy addresses provide at most ten distinct exits.
Setup scripts, SDK commands and their child processes receive HTTP_PROXY,
HTTPS_PROXY, ALL_PROXY and their lowercase forms. Tools such as curl and
Python requests use these settings. Browsers and other applications that ignore
proxy environment variables need their own proxy configuration. This mode does
not transparently redirect arbitrary socket calls.
The network policy outside the guest permits outgoing TCP only to the selected
proxy's resolved addresses and port. Direct connections, direct DNS, UDP and
IPv6 are blocked. Ignoring the proxy settings therefore fails instead of using
the worker's public IP. SDK connections and local guest services remain usable.
env.info["network"] reports the selected endpoint without credentials.
HTTP, HTTPS and socks5h proxy URLs are accepted; the client application must
support the chosen protocol. HTTPS destinations through an HTTP proxy use
CONNECT, preserving destination TLS. socks5h requests remote DNS resolution;
Python requests needs its SOCKS dependency for that protocol. Proxy endpoints
must resolve to public IPv4 addresses. Hostnames are resolved on the worker and
pinned in the guest's hosts file for the sandbox's lifetime.
The configured proxy also applies to setup commands. To install dependencies using direct internet and run episodes through proxies, build a filesystem cache first:
with Sandbox(setup="./install-tools.sh") as builder:
ready = builder.cache("research-tools")
with Sandbox(cache=ready, network=Network(proxy=proxies)) as env:
print(env.run("curl -s https://api.ipify.org").stdout)
Worker-side downloads, such as runtime preparation and image imports, use the worker's connection. They are separate from traffic inside the sandbox. Memory snapshots retain the selected proxy and its resolved addresses. Filesystem-cache restores may select another proxy or switch to internet/offline mode. Private sandbox records and snapshots can contain the supplied credentials.
The QUEST-RL measurements compare the same research requests directly and through ten proxies.
Proxy policies¶
ProxyPolicy controls which proxies are eligible and how a pool distributes
them. The policy is configuration; each pool maintains its own assignment state.
import json
from sandweave import Network, Pool, ProxyPolicy, Sandbox
with open("proxies.json") as file:
proxies = json.load(file)
network = Network(proxy=proxies, policy=ProxyPolicy("same_region"))
with Pool(size=8, warm=2, network=network) as pool:
with pool.acquire() as env:
print(env.info["network"])
For region policies, group URLs by the region labels you supply:
{
"uk": ["http://USER:PASS@UK_HOST_1:PORT", "http://USER:PASS@UK_HOST_2:PORT"],
"us": ["http://USER:PASS@US_HOST_1:PORT", "http://USER:PASS@US_HOST_2:PORT"]
}
Labels are matched exactly. Sandweave uses this supplied metadata; it does not infer location from the URL. Other policies also accept a flat URL list or one URL.
distribution |
Behavior within one pool |
|---|---|
random (default) |
Each new sandbox independently selects a random eligible proxy. |
round_robin |
Successive sandbox assignments cycle through eligible proxies. |
same_proxy |
Select one proxy once and use it for every sandbox. |
same_region |
Select one supplied region uniformly, then cycle through that region's proxies. |
Restrict any policy to a specific region inside the policy itself:
network = Network(proxy=proxies, policy=ProxyPolicy("random", region="uk"))
with Sandbox(network=network) as env:
print(env.run("curl -s https://api.ipify.org").stdout)
A standalone sandbox selects one eligible proxy and keeps it. With same_region,
it first selects a region, then a proxy in that region. Sharing a Network
object between independent sandboxes does not create a shared rotation counter.
Pools coordinate assignments across all their workers. Region labels are sorted before flattening a grouped catalog; URL order within each region is preserved. Rotation follows sandbox creation order. Warm sandboxes can become ready and be leased in a different order. Preparing the baseline does not consume a position in the member rotation, but uses the same region/proxy constraint.
Each sandbox keeps its proxy across requests, pause and resume. Retrying the same
assignment keeps that proxy; a replacement sandbox receives a new assignment.
Cluster pools save their chosen region or proxy and rotation position with their
assignments, preserving them through controller restarts and Pool.connect().
Local pools keep this state in their owning Python process. Each pool has an
independent selection, even when several pools use the same Network object.
An assignment never silently switches to another proxy, region or direct internet.
same_proxy selects the same endpoint; whether the provider changes that endpoint's
exit IP is controlled by the provider. env.info["network"] includes the supplied
region and explicit policy when present, along with the credential-free endpoint.
Pool proxy policies use filesystem baselines, including the baseline a pool
prepares automatically. A memory snapshot already captures a particular proxy;
restore it with Sandbox(snapshot=...) to retain that binding. Explicit proxy
policies on a pool reject memory baselines rather than changing captured network
state. Filesystem caches can be reused with another policy or region.
Proxy policies require Sandweave 0.2.10 or newer on the client, controller and participating workers. Older processes that would ignore a policy are rejected.
Cluster connections¶
The cluster's HTTP, HTTPS, or SSH address controls how the client reaches the
controller. The sandbox's network option controls its outgoing traffic.
They are separate settings. See connect a cluster.
Forwarded sandbox commands and file transfers do not expose arbitrary guest TCP ports. VNC access uses the worker's separate loopback port and your own network or tunnel arrangement.
Runtime support¶
Offline, proxy and allowlist networking are supported by the default gVisor runtime. The native Apptainer runtime uses the host network and rejects these settings.