# Egress Proxy

Sandbox egress proxy routes supported outbound TCP paths through a customer-managed SOCKS5 proxy. Check the adapter limitations below before depending on it for a fixed egress route.

Use it when a sandbox needs a customer-controlled egress IP, customer-side audit, or access to services reachable from the proxy network.

## Behavior

- Only TCP traffic is supported.
- Sandbox network policy is still enforced before the proxy connection is opened.
- Platform deny rules still apply.
- Proxy credentials are referenced through credential bindings and are not
  stored in runtime allocation metadata or exposed to the sandbox process.
- UDP, QUIC, HTTP proxy endpoints, and VPN tunnels are not part of this feature. Configuring a SOCKS5 proxy does not disable non-TCP traffic that the network policy otherwise allows.

<Callout variant="info">
For domain-classified TCP traffic, the ctld network runtime sends the hostname to the SOCKS5 proxy so the proxy can resolve it from the customer network side. The sandbox process still needs to open a TCP connection first, so make sure the application can resolve or otherwise reach the initial destination address.
</Callout>

## Adapter Compatibility

The shared TCP relay and TLS/HTTPS upstream paths use the configured SOCKS5
proxy. Two current adapter combinations open direct upstream connections:

- plain HTTP inspection for HTTP/MCP protocol controls or credential injection;
- SSH credential injection through the `ssh_proxy` adapter.

These combinations do not provide a guaranteed SOCKS5 egress route. The example
below limits tenant traffic to classified TLS on TCP port 443. Platform policy
and template rule merging still apply, as described in [Network](/docs/sandbox/network).

The proxy endpoint must be reachable from the platform's network runtime, rather
than only from the sandbox. Loopback, link-local, cloud metadata, multicast and
platform-denied endpoint addresses are rejected.

## Configure Proxy

<Endpoint method="PUT">
/api/v1/sandboxes/{'{id}'}/network
</Endpoint>

Create a credential source for proxies that require username/password auth, then attach the proxy to the sandbox network policy.

<Tabs
  tabs={[
    {
      label: "Go",
      language: "go",
      code: `source, err := client.CreateCredentialSource(ctx, apispec.CredentialSourceWriteRequest{
    Name: "corp-proxy-source",
    ResolverKind: apispec.CredentialSourceResolverKindStaticUsernamePassword,
    Spec: apispec.CredentialSourceWriteSpec{
        StaticUsernamePassword: apispec.NewOptStaticUsernamePasswordSourceSpec(
            apispec.StaticUsernamePasswordSourceSpec{
                Username: os.Getenv("SOCKS5_USERNAME"),
                Password: os.Getenv("SOCKS5_PASSWORD"),
            },
        ),
    },
})
if err != nil {
    log.Fatal(err)
}

credentialRef := "corp-proxy"
_, err = sandbox.UpdateNetworkPolicy(ctx, apispec.SandboxNetworkPolicy{
    Mode: apispec.SandboxNetworkPolicyModeBlockAll,
    Egress: apispec.NewOptNetworkEgressPolicy(apispec.NetworkEgressPolicy{
        TrafficRules: []apispec.TrafficRule{
            {
                Name: apispec.NewOptString("allow-private-api"),
                Action: apispec.TrafficRuleActionAllow,
                Domains: []string{"api.internal.example.com"},
                Ports: []apispec.PortSpec{
                    {
                        Port: 443,
                        Protocol: apispec.NewOptString("tcp"),
                    },
                },
                AppProtocols: []apispec.TrafficRuleAppProtocol{
                    apispec.TrafficRuleAppProtocolTLS,
                },
            },
        },
        Proxy: apispec.NewOptEgressProxyPolicy(apispec.EgressProxyPolicy{
            Type: apispec.EgressProxyTypeSocks5,
            Address: "proxy.example.com:1080",
            CredentialRef: apispec.NewOptString(credentialRef),
        }),
    }),
    CredentialBindings: []apispec.CredentialBinding{
        {
            Ref: credentialRef,
            SourceRef: source.Name,
            Projection: apispec.ProjectionSpec{
                Type: apispec.CredentialProjectionTypeUsernamePassword,
                UsernamePassword: &apispec.UsernamePasswordProjection{},
            },
        },
    },
})
if err != nil {
    log.Fatal(err)
}`
    },
    {
      label: "Python",
      language: "python",
      code: `import os

from sandbox0 import Client
from sandbox0.apispec.models.credential_binding import CredentialBinding
from sandbox0.apispec.models.credential_projection_type import CredentialProjectionType
from sandbox0.apispec.models.credential_source_resolver_kind import CredentialSourceResolverKind
from sandbox0.apispec.models.credential_source_write_request import CredentialSourceWriteRequest
from sandbox0.apispec.models.credential_source_write_spec import CredentialSourceWriteSpec
from sandbox0.apispec.models.egress_proxy_policy import EgressProxyPolicy
from sandbox0.apispec.models.egress_proxy_type import EgressProxyType
from sandbox0.apispec.models.network_egress_policy import NetworkEgressPolicy
from sandbox0.apispec.models.port_spec import PortSpec
from sandbox0.apispec.models.projection_spec import ProjectionSpec
from sandbox0.apispec.models.sandbox_network_policy import SandboxNetworkPolicy
from sandbox0.apispec.models.sandbox_network_policy_mode import SandboxNetworkPolicyMode
from sandbox0.apispec.models.static_username_password_source_spec import StaticUsernamePasswordSourceSpec
from sandbox0.apispec.models.traffic_rule import TrafficRule
from sandbox0.apispec.models.traffic_rule_action import TrafficRuleAction
from sandbox0.apispec.models.traffic_rule_app_protocol import TrafficRuleAppProtocol
from sandbox0.apispec.models.username_password_projection import UsernamePasswordProjection

client = Client(token=os.environ["SANDBOX0_TOKEN"])

source = client.create_credential_source(
    CredentialSourceWriteRequest(
        name="corp-proxy-source",
        resolver_kind=CredentialSourceResolverKind.STATIC_USERNAME_PASSWORD,
        spec=CredentialSourceWriteSpec(
            static_username_password=StaticUsernamePasswordSourceSpec(
                username=os.environ["SOCKS5_USERNAME"],
                password=os.environ["SOCKS5_PASSWORD"],
            ),
        ),
    )
)

sandbox = client.sandbox(os.environ["SANDBOX_ID"])
credential_ref = "corp-proxy"

sandbox.update_network_policy(
    SandboxNetworkPolicy(
        mode=SandboxNetworkPolicyMode.BLOCK_ALL,
        egress=NetworkEgressPolicy(
            traffic_rules=[
                TrafficRule(
                    name="allow-private-api",
                    action=TrafficRuleAction.ALLOW,
                    domains=["api.internal.example.com"],
                    ports=[PortSpec(port=443, protocol="tcp")],
                    app_protocols=[TrafficRuleAppProtocol.TLS],
                )
            ],
            proxy=EgressProxyPolicy(
                type_=EgressProxyType.SOCKS5,
                address="proxy.example.com:1080",
                credential_ref=credential_ref,
            ),
        ),
        credential_bindings=[
            CredentialBinding(
                ref=credential_ref,
                source_ref=source.name,
                projection=ProjectionSpec(
                    type_=CredentialProjectionType.USERNAME_PASSWORD,
                    username_password=UsernamePasswordProjection(),
                ),
            )
        ],
    )
)`
    },
    {
      label: "TypeScript",
      language: "typescript",
      code: `import { Client, apispec } from "sandbox0";

const client = new Client({ token: process.env.SANDBOX0_TOKEN! });

const source = await client.credentialSources.create({
    name: "corp-proxy-source",
    resolverKind: "static_username_password",
    spec: {
        staticUsernamePassword: {
            username: process.env.SOCKS5_USERNAME!,
            password: process.env.SOCKS5_PASSWORD!,
        },
    },
});

const sandbox = client.sandbox(process.env.SANDBOX_ID!);
const credentialRef = "corp-proxy";

await sandbox.updateNetworkPolicy({
    mode: apispec.models.SandboxNetworkPolicyModeEnum.BlockAll,
    egress: {
        trafficRules: [
            {
                name: "allow-private-api",
                action: apispec.models.TrafficRuleAction.Allow,
                domains: ["api.internal.example.com"],
                ports: [{ port: 443, protocol: "tcp" }],
                appProtocols: [
                    apispec.models.TrafficRuleAppProtocol.TrafficRuleAppProtocolTls,
                ],
            },
        ],
        proxy: {
            type: apispec.models.EgressProxyType.EgressProxyTypeSocks5,
            address: "proxy.example.com:1080",
            credentialRef,
        },
    },
    credentialBindings: [
        {
            ref: credentialRef,
            sourceRef: source.name,
            projection: {
                type: apispec.models.CredentialProjectionType.UsernamePassword,
                usernamePassword: {},
            },
        },
    ],
});`
    },
    {
      label: "CLI",
      language: "bash",
      code: `cat <<'EOF' > corp-proxy-source.yaml
name: corp-proxy-source
resolverKind: static_username_password
spec:
    staticUsernamePassword:
        username: proxy-user
        password: proxy-password
EOF

s0 credential create -f corp-proxy-source.yaml

s0 sandbox network update -s "$SANDBOX_ID" \\
    --mode block-all \\
    --traffic-rule '{"name":"allow-private-api","action":"allow","domains":["api.internal.example.com"],"ports":[{"port":443,"protocol":"tcp"}],"appProtocols":["tls"]}' \\
    --proxy socks5://proxy.example.com:1080 \\
    --proxy-credential-ref corp-proxy \\
    --proxy-credential-source corp-proxy-source`
    }
  ]}
/>

`credentialRef` is optional. Leave it empty for a SOCKS5 proxy that does not require authentication. In the CLI, omit `--proxy-credential-ref` and `--proxy-credential-source` for unauthenticated proxies.

## Limits

Egress proxy is a TCP routing feature. It does not proxy DNS datagrams, UDP application protocols, or QUIC. If a workload uses HTTP/3 or QUIC, configure the client to use TCP/TLS instead.

## Next Steps

<CardGroup>
  <Card title="Sandbox Services" href="/docs/sandbox/services" cta="Continue">
    Expose named sandbox ports through public HTTP service routes.
  </Card>

  <Card title="Sandbox Functions" href="/docs/sandbox/functions" cta="Continue">
    Expose public handlers without running long-lived HTTP listeners.
  </Card>

  <Card title="Webhooks" href="/docs/sandbox/webhooks" cta="Continue">
    Receive signed sandbox lifecycle, service, and file-related events.
  </Card>
</CardGroup>
