Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/opensandbox-group/OpenSandbox/llms.txt

Use this file to discover all available pages before exploring further.

OpenSandbox network architecture has two distinct control planes. The egress plane enforces per-sandbox outbound policy through a sidecar that runs alongside the sandbox container, intercepting DNS queries and blocking connections to disallowed destinations. The ingress plane routes external HTTP traffic into sandbox instances running on Kubernetes, giving callers a stable URL for each sandbox port. Together, these two planes let you precisely control what a sandbox can reach on the internet while also making sandbox-hosted services accessible from outside the cluster.

Egress Policy

The egress sidecar runs in the same network namespace as the sandbox container. It operates a DNS proxy on 127.0.0.1:15353, redirecting all port-53 traffic through iptables rules. Allowed domains are resolved normally; denied domains return NXDOMAIN. In dns+nft mode, resolved IP addresses for allowed domains are also added to an nftables allow set with TTL, enforcing policy at the network layer as well.
The egress sidecar requires [egress].image to be set in the OpenSandbox server configuration. Without this setting, sandboxes created with a network_policy will fail to start. Contact your platform administrator if you receive an error about a missing egress image.

Creating a Sandbox with a Network Policy

Pass a network_policy object at sandbox creation time to attach the egress sidecar and define the outbound allowlist. Any domain not listed in egress rules is blocked by default when defaultAction is "deny".
from opensandbox import SandboxSync
from opensandbox.models.sandboxes import NetworkPolicy, NetworkRule, SandboxImageSpec

sandbox = SandboxSync.create(
    image=SandboxImageSpec("opensandbox/code-interpreter:latest"),
    network_policy=NetworkPolicy(
        defaultAction="deny",
        egress=[
            NetworkRule(action="allow", target="api.anthropic.com"),
            NetworkRule(action="allow", target="registry.npmjs.org"),
            NetworkRule(action="allow", target="*.pypi.org"),
        ],
    ),
)
The network_policy parameter structure as JSON:
{
  "defaultAction": "deny",
  "egress": [
    { "action": "allow", "target": "api.anthropic.com" },
    { "action": "allow", "target": "registry.npmjs.org" },
    { "action": "allow", "target": "*.pypi.org" }
  ]
}
Supported target formats:
  • FQDN: api.github.com
  • Wildcard subdomain: *.pypi.org
  • IP address: 10.0.0.5
  • CIDR range: 10.244.0.0/16

Getting and Patching Egress Rules at Runtime

You can inspect and modify a sandbox’s egress policy after creation without restarting the sandbox.
# Get current egress policy
policy = sandbox.get_egress_policy()
print(policy)

# Patch (append) new rules to the existing policy
sandbox.patch_egress_rules([
    NetworkRule(action="allow", target="api.openai.com"),
])

Default-Deny Behavior

When defaultAction is set to "deny", any domain not explicitly listed in the egress rules is blocked. Blocked connection attempts fail with a DNS NXDOMAIN response for denied hostnames. In dns+nft mode, packets to non-allowed IPs are dropped at the network layer even if DNS resolution somehow succeeds. The sidecar also supports static deny.always and allow.always rule files baked into the egress image. These files have higher priority than any user-defined network_policy and cannot be overridden at runtime — useful for platform-wide enforcement of cluster-internal CIDR isolation.

Kubernetes In-Cluster Services

When defaultAction is "deny", reaching a Kubernetes Service requires both DNS and network layer allowances:
# Allowing only the DNS name is not enough if the resolved ClusterIP
# is in a denied CIDR range such as 10.96.0.0/12
NetworkPolicy(
    defaultAction="deny",
    egress=[
        # 1. Allow DNS resolution of the service hostname
        NetworkRule(action="allow", target="postgres.opensandbox.svc.cluster.local"),
        # 2. Allow the Service CIDR so nftables does not drop the TCP connection
        NetworkRule(action="allow", target="10.96.0.0/12"),
    ],
)

Ingress Gateway

The ingress gateway routes external HTTP and WebSocket traffic into sandbox instances running on Kubernetes. It provides a stable, authenticated URL for each sandbox port without requiring the sandbox Pod to be directly reachable from outside the cluster.

How It Works

Every sandbox container runs execd on port 44772. execd bundles a lightweight reverse proxy that intercepts requests with the /proxy/{port} prefix and forwards them to 127.0.0.1:{port} inside the same container. This means a single host port (or ingress rule) per sandbox is sufficient to reach all container ports. The ingress gateway supports two routing modes:
ModeHow routing works
Header modeThe gateway reads a sandbox-ID header and dispatches to the correct pod
URI modeThe sandbox ID is embedded in the URL path for clients that cannot set custom headers

Getting Endpoint URLs

Use get_endpoint() to retrieve the public URL for a specific port on a running sandbox:
# Get the public URL for port 8080 inside the sandbox
url = sandbox.get_endpoint(port=8080)
print(url)
# e.g. https://ingress.example.com/proxy/8080
#      or http://<host>:<proxy-port>/proxy/8080 in single-host mode
For callers that live inside the same Docker network or Kubernetes cluster, resolve the internal address directly to bypass the host proxy mapping:
# Returns the sandbox container IP directly, e.g. http://172.17.0.3:8080
internal_url = sandbox.get_endpoint(port=8080, resolve_internal=True)

Single-Host Routing

In single-host (Docker bridge) mode, only execd’s proxy port (44772) is published on the host. All sandbox container ports are reachable through the proxy path:
  • get_endpoint(port=X) returns {public_host}:{host_proxy_port}/proxy/{X}
  • HTTP, Server-Sent Events, and WebSocket traffic all share the same mapped host port
  • No additional ports need to be published, simplifying firewall management
If execd’s proxy port (44772) or the optional 8080 host mapping is missing, get_endpoint() responds with HTTP 500 and a message stating which mapping was unavailable.

Full Example: Egress Policy with Credential Vault

The following example combines a network policy with Credential Vault to run Claude Code inside a sandbox. Only api.anthropic.com and registry.npmjs.org are reachable; all other outbound traffic is denied.
import os
from datetime import timedelta

from opensandbox import SandboxSync
from opensandbox.models.sandboxes import (
    Credential,
    CredentialBinding,
    CredentialProxyConfig,
    NetworkPolicy,
    NetworkRule,
    SandboxImageSpec,
)

ANTHROPIC_HOST = "api.anthropic.com"

sandbox = SandboxSync.create(
    image=SandboxImageSpec("opensandbox/code-interpreter:latest"),
    timeout=timedelta(minutes=15),
    env={
        "ANTHROPIC_BASE_URL": "https://api.anthropic.com",
        "ANTHROPIC_API_KEY": "fake-key-inside-sandbox",
    },
    network_policy=NetworkPolicy(
        defaultAction="deny",
        egress=[
            NetworkRule(action="allow", target=ANTHROPIC_HOST),
            NetworkRule(action="allow", target="registry.npmjs.org"),
        ],
    ),
    credential_proxy=CredentialProxyConfig(enabled=True),
)

try:
    sandbox.credential_vault.create(
        credentials=[
            Credential(
                name="anthropic-api-key",
                source={"value": os.environ["ANTHROPIC_API_KEY"]},
            )
        ],
        bindings=[
            CredentialBinding(
                name="anthropic-api",
                match={
                    "schemes": ["https"],
                    "ports": [443],
                    "hosts": [ANTHROPIC_HOST],
                    "methods": ["GET", "POST"],
                    "paths": ["/v1/*"],
                },
                auth={
                    "type": "apiKey",
                    "name": "x-api-key",
                    "credential": "anthropic-api-key",
                },
            )
        ],
    )

    result = sandbox.commands.run("claude -p '1+1'")
    print("".join(part.text for part in result.logs.stdout))
finally:
    sandbox.kill()
    sandbox.close()

Runtime Compatibility

The egress sidecar uses an iptables nat table REDIRECT rule for DNS interception. This is supported by runc (default) and all Kata Containers variants (kata-qemu, kata-clh, kata-fc), but not by gVisor — gVisor’s netstack does not implement the nat table. The OpenSandbox server returns HTTP 400 if you request network_policy together with secure_runtime.type = "gvisor". For full runtime compatibility details, see the Secure Runtimes guide.

Build docs developers (and LLMs) love