Sandbox isolation and network access
How sandboxes isolate workloads, how to scope what an agent can reach on the network, and which controls remain yours.
An agent in one hosted sandbox cannot read the memory, processes, or local filesystem of another sandbox. Standalone and interactive sandboxes can still make network requests to public services. Evaluation sandboxes use a narrower network boundary described below.
This distinction matters when you run untrusted agent code: compute isolation does not imply network isolation.
The isolation boundary
Section titled “The isolation boundary”Dreadnode runs each hosted sandbox in a separate E2B Firecracker microVM. Each microVM has its own Linux kernel, memory, processes, and local filesystem. A process must escape both the guest and the Firecracker virtualization boundary to reach the host or another microVM directly.
Agents may have broad privileges inside their own sandbox. They can run arbitrary commands, install packages, and modify local files. Those privileges stop at the microVM boundary.
| Resource | Isolated between sandboxes? | Enforcement |
|---|---|---|
| Memory and processes | Yes | Firecracker microVM |
| Local filesystem | Yes | Separate microVM storage |
| E2B project API | Yes | The E2B project API key stays in the Dreadnode control plane |
| Dreadnode runtime API | Yes | A bearer credential scoped to one sandbox |
| Evaluation task services | Yes | Restricted E2B ingress and paired-runtime egress rules |
| Public network services | No | Each service must authenticate its callers |
| Outbound network | Governed by policy | An egress policy composed per sandbox — see below |
Evaluation agents and task environments also run in separate sandboxes. Dreadnode restricts each task environment’s public ingress and configures its paired runtime to reach only declared task service hosts plus the platform and model gateway. E2B adds the task sandbox’s traffic credential only when the paired runtime calls those exact hosts, including HTTP and WebSocket services.
flowchart LR
Anonymous["Anonymous caller"]
API["Dreadnode API"]
Gateway["Model gateway"]
Internet["Other public hosts"]
subgraph Pair["E2B evaluation pair"]
direction LR
Runtime["Agent runtime sandbox<br/>deny-all egress"]
Task["Task sandbox<br/>traffic-token ingress"]
end
Anonymous -. "blocked" .-> Task
Runtime -->|"declared host + token header"| Task
Runtime -->|"allowlisted"| API
Runtime -->|"allowlisted"| Gateway
Runtime -. "blocked" .-> Internet
classDef sandbox fill:#171124,stroke:#a78bfa,color:#ececf0,stroke-width:2px
classDef allowed fill:#102018,stroke:#34d399,color:#d5ffe2,stroke-width:2px
classDef blocked fill:#2a0b10,stroke:#d40924,color:#ffd6d6,stroke-width:2px
class Runtime,Task sandbox
class API,Gateway allowed
class Anonymous,Internet blocked
style Pair fill:#08080b,stroke:#26262c,color:#ececf0
linkStyle 0,4 stroke:#d40924,stroke-width:2px
linkStyle 1,2,3 stroke:#34d399,stroke-width:2px
Only the paired agent runtime gets deny-all egress by default. The task sandbox’s outbound traffic follows its egress declaration when present; otherwise it follows E2B defaults.
Access to another sandbox
Section titled “Access to another sandbox”A sandbox cannot enumerate or manage other sandboxes through the E2B project API because Dreadnode does not inject its E2B project API key into agent workloads. Dreadnode also checks the requesting user’s organization, workspace access, and sandbox ownership before allowing sandbox operations through the platform API.
Network services are a separate boundary. E2B assigns public URLs to services running inside a sandbox. By default, anyone who knows one of these URLs can send requests to it. Evaluation task sandboxes disable that default: anonymous requests fail, and E2B adds the provider traffic credential only when the paired runtime calls ports declared by the task.
Dreadnode’s agent runtime API requires a bearer credential scoped to one sandbox. Only the sandbox owner can obtain it, and every client they attach presents the same value. Services supplied by a task, such as a web application or API under test, define their own authentication when used outside an evaluation pair.
Dreadnode applies E2B traffic access tokens internally to evaluation task sandboxes. This control is not customer-configurable. See E2B’s public access controls for the provider capability.
Internet and private networks
Section titled “Internet and private networks”By default an agent reaches the public internet. It can download packages, clone repositories, call public APIs, and send data to external hosts. Evaluation runtimes instead use deny-all egress with exact allowances for their task services, the Dreadnode API, and the model gateway.
Hosted sandboxes additionally have private and link-local ranges blocked at E2B’s network edge —
10.0.0.0/8, 100.64.0.0/10, 127.0.0.0/8, 169.254.0.0/16, 172.16.0.0/12, and
192.168.0.0/16 — which keeps agents off common private-network and cloud metadata addresses. It
restricts no public destination.
Narrow that default by declaring a scope.
Scoping what a sandbox can reach
Section titled “Scoping what a sandbox can reach”An egress declaration answers: “which destinations does this workload legitimately need?”
egress: allow: - target.example.com # the host this task assesses - '*.githubusercontent.com' # a wildcard suffix - 10.20.0.0/16 # a range, for a target with no nameThat task now reaches those three destinations and nothing else on the public internet. It keeps working, because the platform separately allows the destinations it needs to function — you never name those yourself.
Each target is an FQDN, a *.suffix wildcard, an IP address, or a CIDR. A bare * is rejected: it
asks for the default rather than declaring a scope.
Declaring anything opts you out of the default
Section titled “Declaring anything opts you out of the default”This is the part that surprises people. A task that names one host gets that host plus the platform’s own services — not the open internet it would have had by saying nothing. The declaration flips the sandbox to deny-by-default and the list becomes the whole of what it may reach.
So omitting the key and declaring an empty list are different:
| Declaration | Effect |
|---|---|
egress absent | The deployment’s default is unchanged |
allow: [] | A declaration that names nothing — the sandbox reaches only the platform’s own services |
allow: [host] | The sandbox reaches host and the platform’s own services |
What the platform allows for you
Section titled “What the platform allows for you”Every sandbox reaches the platform API, the model gateway, object storage, and the package indices — the last unless the deployment has sealed capability installs. Reaching the gateway is not the same as being able to use it: what actually scopes inference is the per-sandbox gateway key, enumerated to the models the workload declared.
Beyond those, each sandbox gets what its own job requires, derived from the same settings that build its environment:
| Workload | Also reaches |
|---|---|
| Evaluation agent | Its paired environment’s exposed ports |
| Task environment | The container registry, when the task ships compose services |
| Interactive runtime | Nothing beyond the universal set |
Self-hosted deployments add the mirrors named by URL in their sandbox environment — UV_INDEX_URL,
GOPROXY, HF_ENDPOINT, proxies — so an enclave mirror stays reachable when a declaration flips the
default to deny. Each such host has to be fully qualified: a single-label in-cluster name cannot be
matched by a rule and is left out.
Where you declare it
Section titled “Where you declare it”| Surface | Where | Applies to |
|---|---|---|
| Task | egress.allow in task.yaml | Both sandboxes of every evaluation of that task |
| Evaluation run | dn evaluation create --egress TARGET | That run only; unions with the task’s own list |
| Interactive runtime | sandbox.egress.allow in runtime.yaml | The sandbox that runtime starts |
A run’s --egress targets union with the task’s, which makes retargeting the useful case: a task
names target.example.com, and a run points the same task at staging. It cannot narrow a task below
what the task declared.
A runtime’s declaration governs only that runtime’s own sandbox. Borrowing a capability from a runtime does not borrow its network scope.
A declaration narrows, it never widens
Section titled “A declaration narrows, it never widens”A declaration is a statement of scope, not a grant of access. It can only narrow what the deployment already permits, which has three consequences worth knowing before you write one:
- A target the deployment denies fails the run, naming the target, before the sandbox starts. It is never dropped from the policy and run without.
- A target that spans a denied destination is rejected the same way, naming both. Declare the narrower targets instead.
- On a deployment whose sandbox provider cannot enforce egress at all, a declaration fails the run naming the provider. A workload that asked to be contained is never run uncontained.
The format of your targets is checked when you upload. Whether they are reachable is checked when you create a run and again when each sandbox provisions, because a task published on one deployment may be run on another.
What a blocked connection looks like
Section titled “What a blocked connection looks like”On a self-hosted deployment the two halves of the policy fail differently, and an agent that knows the difference wastes fewer steps:
- A blocked name fails at resolution, immediately. The sidecar answers the DNS query with NXDOMAIN, so the client reports that the host could not be resolved. Treat that as policy, not as a typo in the hostname.
- A blocked address is dropped silently. Packets to a literal IP or CIDR the policy does not allow go nowhere, so the connection hangs until the client’s own connect timeout. Set one when a task connects to addresses rather than names.
Hosted sandboxes follow E2B’s own network behavior for a denied destination.
Keeping an agent away from a host
Section titled “Keeping an agent away from a host”There is no deny list. egress accepts one key, allow, and no surface — task, run, runtime, or
deployment — takes a rule that blocks a named destination. You keep an agent off a host by
declaring an allow list that omits it, which is a different move than it sounds: the declaration
is what turns the sandbox deny-by-default, so everything unnamed is blocked, not just the host you
had in mind.
Concretely, to keep an evaluation off your production estate:
# task.yaml — the agent reaches staging and nothing elseegress: allow: - staging.example.comNot deny: [prod.example.com]. That fails validation, and it would be the weaker control anyway:
an allow list bounds what the agent can reach to a set you chose, while a deny list bounds only what
you thought to enumerate.
Two limits worth knowing before you rely on this:
- A declaration is the workload’s own. It is not a guardrail a workspace admin puts around someone else’s runs — anyone who can author a task or own a runtime can widen it back up to whatever the deployment permits. The one ceiling no workload can raise is the operator floor, and on a self-hosted deployment the lever that lowers it is air-gapping.
- On a connected deployment the floor denies almost nothing. Every sandbox with no declaration reaches the internet. If a host must be unreachable from every sandbox regardless of what any task declares, that is a network control on your side — see Network trust boundaries — not a field in a manifest.
Credentials and customer data
Section titled “Credentials and customer data”Dreadnode limits the credentials available inside each sandbox:
| Credential or data | Control |
|---|---|
| E2B project API key | Held by the Dreadnode API and never injected into agent sandboxes |
| Evaluation and job API keys | Restricted to the platform scopes required by that workload |
| Interactive runtime API keys | Follow the initiating user’s platform permissions |
| Runtime bearer credential | Unique to one sandbox; required by the Dreadnode runtime API |
| SDK remote storage | Fetches temporary workspace-scoped credentials on demand; provisioning does not inject them |
| Model gateway key | Limited to permitted models, expires, and cleanup requests revocation |
| User secrets | Injected only when selected for the workload |
Processes inside a sandbox can read credentials and secrets injected into that sandbox. A paired evaluation runtime can send them only to its allowlisted hosts. Wherever public egress remains available, compromised or malicious code can send those values to an external host. Inject only the secrets the workload needs, use the narrowest permissions available, and declare an egress scope when the workload’s destinations are knowable — it bounds where a leaked secret can go.
Lifetime and audit trail
Section titled “Lifetime and audit trail”Dreadnode gives sandboxes a bounded lifetime. Ephemeral evaluation and job sandboxes terminate after use or timeout. Durable interactive runtimes pause on timeout and terminate when reset or deleted. Cleanup requests revocation of linked platform and model-gateway credentials.
Dreadnode records sandbox ownership, provider identity, state transitions, runtime duration, and lifecycle events. Public sandbox traffic passing through Dreadnode’s proxy produces access logs. The platform does not currently provide a complete audit trail of every shell command, filesystem operation, DNS query, or outbound connection inside a sandbox.
Responsibility split
Section titled “Responsibility split”| Layer | Responsibility |
|---|---|
| E2B | Firecracker isolation, sandbox infrastructure, and enforcing the policy the platform sends |
| Dreadnode | User authorization, workload credentials, runtime authentication, egress policy, lifecycle, and cleanup |
| You | Data and secret selection, task-service authentication, and declaring the scope each workload actually needs |
A sandbox with no declaration still reaches the public internet. Scoping is opt-in per workload, so treat “an agent can reach the internet” as the state until a task, run, or runtime says otherwise.
For private-only service access or the per-connection audit trail described above, reach out to us before deployment so we can review the architecture.