# Sandbox Networking
source: https://docs.chalk.ai/docs/sandboxes/networking

## Restrict a sandbox's outbound network access, give it managed SSH keys, and connect it to other workloads.

### Network policy

A sandbox on the default host compute class allows outbound network access by
default. To restrict access, pass a NetworkPolicy as the sandbox's
network_policy (singular). The policy lists the destinations the sandbox can reach:

```
from chalkcompute import Image, NetworkPolicy, Sandbox

policy = NetworkPolicy(
    allowed_hosts=[
        "api.openai.com",
        "github.com",
        "pypi.org",
    ],
)

sandbox = Sandbox(
    image=Image.debian_slim(),
    network_policy=policy,
).run()
```

Each entry in allowed_hosts is an exact hostname, a wildcard subdomain
pattern, or *:

- An exact hostname, such as github.com, matches only that host.
- *.github.com matches every subdomain of github.com, such as
api.github.com, but not github.com itself. To allow both, list both.
- * matches every hostname.

Matching ignores case. Requests to any other destination are blocked. A policy
with an empty allowed_hosts allows no hostnames.

NetworkPolicy accepts one policy per sandbox — network_policy takes a
single NetworkPolicy, not a list.

Network policies apply only to the host compute class. A sandbox on k8s
has the network access of the cluster it runs on.

### CIDR-level routes

Use allowed_routes and denied_routes for destinations that don't have a
stable hostname — a VPC-peered range or an on-premise network, for example.
denied_routes takes precedence over both allowed_routes and
allowed_hosts:

```
from chalkcompute import NetworkPolicy

policy = NetworkPolicy(
    allowed_routes=[
        NetworkPolicy.Route("10.0.0.0/8", ports=[(5432, 5432)]),
    ],
    denied_routes=["169.254.169.254/32"],  # block the cloud metadata endpoint
)
```

Route(route, *, ports=...) takes a destination CIDR and an optional list
of (start_port, end_port) ranges; an empty ports allows every port on
that route.

### Per-host request rules

For per-host request matching or rewriting — for example, substituting a
Secret into an auth header before forwarding to an internal service — pass
allowed_hosts as a mapping of hostname to a list of NetworkPolicy.Rule
instead of a plain list:

```
from chalkcompute import NetworkPolicy

policy = NetworkPolicy(
    allowed_hosts={
        "internal-api.example.com": [
            NetworkPolicy.Rule(
                match=NetworkPolicy.Match(
                    path=NetworkPolicy.Matcher(starts_with="/v1/"),
                ),
                transforms=[
                    NetworkPolicy.Transformer(
                        headers={
                            "Authorization": "Bearer __chalk_secret_API_TOKEN",
                        },
                        headers_secrets={
                            "__chalk_secret_API_TOKEN": "INTERNAL_API_TOKEN",
                        },
                    ),
                ],
                forward_url="https://internal-api.example.com",
            ),
        ],
    },
)
```

Each Rule can match on request path (exact, starts_with, or regex),
HTTP method, query-string entries, and headers — a rule's transforms and
forward_url only apply to requests matching every dimension set on
match. The route matches the original request before any transforms run.

Transformer.headers_secrets maps each literal placeholder token to the
name of a Chalk Secret. For a matching rule, Chalk first applies the literal
values in Transformer.headers, then replaces each placeholder occurrence
in ordinary request-header values with that Secret's contents. Placeholders
can also appear in headers supplied by the workload. Chalk does not substitute
within pseudo-headers, authority or framing headers, hop-by-hop headers, or
Chalk-owned routing headers.

Placeholder matching is case-sensitive. When occurrences overlap, Chalk
selects the leftmost match and then the longest placeholder that starts at
that position. Replacement is non-recursive, so inserted Secret contents are
not scanned for additional placeholders. An absent placeholder has no effect,
and an empty Secret value removes the matched placeholder text. Empty
placeholder tokens are rejected. The sandbox never receives the Secret contents.

### Managed SSH

Managed SSH lets a sandbox authenticate to approved SSH destinations using a Chalk Secret,
without exposing the private key to the sandbox itself. It requires the host compute
class. First, store the private key:

```
chalk secret set GITHUB_DEPLOY_KEY < /path/to/privkey/id_ed25519
```

Then configure the destination when creating the sandbox:

```
from chalkcompute import Image, SandboxClient, SshDest

sandbox_client = SandboxClient.from_env()
sandbox = sandbox_client.create(
    image=Image.debian_slim(),
    managed_ssh={
        "github": SshDest(
            "github.com",
            "GITHUB_DEPLOY_KEY",
            username="git",
        )
    },
)
```

private_key_secret is the name of a Chalk Secret whose value is an SSH
private key. The map key creates the SSH alias used to connect, so the example
supports ssh github and git clone github:example/repo.git.
The default port is 22, but can be overridden by setting an explicit port.

By default, SSH accepts the server's host key on the first connection and
remembers it for the rest of the sandbox's lifetime. Later connections fail if
the server presents a different key. Set host_keys to override this, and to
require specific host keys from the first connection. For example:

```
managed_ssh={
    "a-server": SshDest(
        "cloud.example.com",
        "KEY_FOR_SANDBOX_USE",
        username="my_user",
        host_keys=[
            "ssh-ed25519 AAAA...",
            "ecdsa-sha2-nistp256 AAAA...",
        ],
        port=2222,
    )
}
```

Internally, Chalk loads the configured keys into an ssh-agent and exposes its socket through
both SSH_AUTH_SOCK and IdentityAgent in /root/.ssh/config. Sandbox processes can ask the
agent to sign with any configured key, but cannot read the private-key material.

### Sandbox-to-sandbox and on-premise connectivity

Each sandbox gets its own network namespace with no shared listening sockets
(see Multi-tenancy). Chalk provides no direct
connection between sandboxes and no tunnel into an on-premise network. To
exchange data between workloads, share it through a mounted
Volume or an external service that both can reach over
the network. To reach a private network that your cloud network already
connects to, such as a VPC-peered range, use
CIDR-level routes.

To connect to a sandbox from your own machine, for example with ssh or port
forwarding, use Tunnels.





