> ## Documentation Index
> Fetch the complete documentation index at: https://docs.anonalabs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Networking and egress

> What a sandbox can reach, and who can reach it.

## Egress

Every create compiles a network policy from three layers, strictest first:

1. **The platform floor**, which no setting can open: private and internal ranges (`10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`, `100.64.0.0/10`, loopback, link-local including the cloud metadata address `169.254.169.254`, multicast and reserved ranges, and their IPv6 equivalents), names such as `localhost`, `*.local` and `*.internal`, and the mail and DNS-over-TLS ports 25, 465, 587, 2525 and 853.
2. **Your plan's limits.** Paid plans allow the public internet by default; the free plan allows no public egress.
3. **The sandbox's own policy**: the E2B fields below on create, else your team's egress settings (see [Which policy applies](#which-policy-applies)).

| Field | Meaning |
| - | - |
| `allow_internet_access` | `false` cuts all public egress for this sandbox |
| `network.deny_out` | Block these IPv4 addresses or CIDRs. `0.0.0.0/0` blocks all public egress, the same as `allow_internet_access=false` |
| `network.allow_out` | Allow these destinations (IPs, CIDRs or host names) even when the rest is blocked. Allow entries win over deny entries |
| `network.allow_public_traffic` | Whether the sandbox's ports are reachable without a token (see [Ingress](#ingress)) |

```python egress.py theme={null}
from anona import Sandbox

offline = Sandbox.create("base", allow_internet_access=False)
offline.kill()

only_pypi = Sandbox.create(
    "base",
    network={"deny_out": ["0.0.0.0/0"], "allow_out": ["pypi.org", "files.pythonhosted.org"]},
)
only_pypi.kill()

no_example = Sandbox.create("base", network={"deny_out": ["93.184.215.14/32"]})
no_example.kill()
```

```typescript egress.ts theme={null}
import { Sandbox } from "@anona/sdk";

const offline = await Sandbox.create("base", { allowInternetAccess: false });
await offline.kill();

const onlyPypi = await Sandbox.create("base", {
  network: { denyOut: ["0.0.0.0/0"], allowOut: ["pypi.org", "files.pythonhosted.org"] },
});
await onlyPypi.kill();
```

```bash egress.sh theme={null}
anona-compute run --no-internet -t base -- 'echo this sandbox has no internet'
```

Rules the gateway enforces when it compiles your policy:

* A destination inside the platform floor is refused with `400`, and so is a host name that resolves into it (this blocks DNS-rebinding tricks).
* Host names in `allow_out` need public egress off (`deny_out: ["0.0.0.0/0"]` or `allow_internet_access=false`), as on E2B. Wildcard names and names that do not resolve are refused for now.
* `deny_out` takes IPv4 addresses and CIDRs only.
* Unknown keys inside `network` are refused, so a typo cannot silently drop part of a policy.
* Per plan: up to 512 allow entries and 1,024 deny entries on paid plans (64 and 256 on the free plan), and at most 10 DNS servers.

## Which policy applies

A new sandbox gets the first of these that exists, then the platform floor and your plan's limits on top:

1. The `network` and `allow_internet_access` fields of its create request.
2. Its template's policy (built-in templates have none).
3. Your organization's **team egress settings**: set them on the dashboard's **Network** page (owners and admins), or with `PUT /v1/network`.
4. Your plan's default.

Ask the API which policy a sandbox runs with, and where it came from:

```bash effective_policy.sh theme={null}
curl -sS "https://api.compute.anonalabs.com/v1/sandboxes/$SANDBOX_ID/network/effective" \
  -H "X-API-Key: $E2B_API_KEY" | jq '{source, tier, internet: .policy.allow_internet_access}'
curl -sS https://api.compute.anonalabs.com/v1/network -H "X-API-Key: $E2B_API_KEY" | jq '{tier, revision, limits}'
```

## Change a running sandbox

`update_network` replaces a running sandbox's policy. It goes through the same floor and limits as a create. An update that does not mention `allow_internet_access` keeps the sandbox's current setting.

```python update_network.py theme={null}
from anona import Sandbox

with Sandbox.create("base") as sbx:
    # lock it down to one API after setup
    sbx.update_network({"deny_out": ["0.0.0.0/0"], "allow_out": ["1.1.1.1"]})
```

```typescript update_network.ts theme={null}
import { Sandbox } from "@anona/sdk";

const sbx = await Sandbox.create("base");
await sbx.updateNetwork({ denyOut: ["0.0.0.0/0"], allowOut: ["1.1.1.1"] });
await sbx.kill();
```

The caller needs to be able to control the sandbox: an API key with `sandboxes:use` (and the sandbox on its list, if it has one), or a dashboard member or above.

## Credentials without exposing them

The **credential vault** keeps API tokens out of sandboxes. Store a secret on the dashboard's **Credential vault** page (owners and admins), then name it in a header rule for one host. The egress proxy adds the header to the sandbox's HTTPS requests to that host; the sandbox, its environment and its disk never contain the value.

```python vault_rule.py theme={null}
from anona import Sandbox

# "github_token" was stored in the dashboard's Credential vault first.
sbx = Sandbox.create(
    "base",
    network={
        "deny_out": ["0.0.0.0/0"],
        "allow_out": ["api.github.com"],
        "rules": {"api.github.com": [{"transform": {"headers": {"Authorization": "Bearer ${vault.github_token}"}}}]},
    },
)
print(sbx.commands.run("env | grep -ci token || true").stdout.strip(), "token variables in the sandbox")
sbx.kill()
```

```typescript vault_rule.ts theme={null}
import { Sandbox } from "@anona/sdk";

const sbx = await Sandbox.create("base", {
  network: {
    denyOut: ["0.0.0.0/0"],
    allowOut: ["api.github.com"],
    rules: { "api.github.com": [{ transform: { headers: { Authorization: "Bearer ${vault.github_token}" } } }] },
  },
});
await sbx.kill();
```

* A header value must be exactly `${vault.<name>}`, optionally after `Bearer ` or `Basic `. Literal values are refused, so no credential ends up in a policy.
* The first use binds the secret to that host and header. The vault page lists every binding and revokes one. Proxies cache a value for up to 60 seconds, so a rotation or revocation reaches running sandboxes within a minute.
* The same rules work in the team egress settings, so every new sandbox gets them.
* Secret names are lowercase letters, digits and `_`, up to 63 characters; values up to 8 KiB.

The vault is included in every plan at no charge.

## Ingress

All traffic to a sandbox goes through the Anona gateway's sandbox edge at `https://<port>-<sandbox id>.sbx.compute.anonalabs.com`:

| Port | Who gets in |
| - | - |
| 49983 (`envd`: commands, files) | Only requests with the sandbox's access token, which the SDK holds; signed file URLs for `GET`/`POST /files` |
| Any other port | Anyone, when the sandbox allows public traffic or the port is public; otherwise a valid preview link (or its cookie), or the `e2b-traffic-access-token` header |

Single ports can also be made public, or shared with signed, expiring links; see [Preview links](https://docs.anonalabs.com/compute/guides/preview-links.md). Sandbox URLs live on their own domain, so content served from a sandbox never shares cookies with the API or the dashboard.

## DNS

Sandboxes resolve names through public resolvers (1.1.1.1 and 1.0.0.1), so they cannot resolve names inside Anona's network.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.