# Egress Proxy

Sandbox egress proxy routes allowed outbound TCP traffic through a customer-managed SOCKS5 proxy.

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, and VPN tunnels are not part of this feature.

<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>

## 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 \\
    --allow-domain api.internal.example.com \\
    --allow-port 443/tcp \\
    --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>
