Skip to content

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.

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.

ResourceIsolated between sandboxes?Enforcement
Memory and processesYesFirecracker microVM
Local filesystemYesSeparate microVM storage
E2B project APIYesThe E2B project API key stays in the Dreadnode control plane
Dreadnode runtime APIYesA bearer credential scoped to one sandbox
Evaluation task servicesYesRestricted E2B ingress and paired-runtime egress rules
Public network servicesNoEach service must authenticate its callers
Outbound networkGoverned by policyAn 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.

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.

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.

An egress declaration answers: “which destinations does this workload legitimately need?”

task.yaml
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 name

That 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:

DeclarationEffect
egress absentThe 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

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:

WorkloadAlso reaches
Evaluation agentIts paired environment’s exposed ports
Task environmentThe container registry, when the task ships compose services
Interactive runtimeNothing 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.

SurfaceWhereApplies to
Taskegress.allow in task.yamlBoth sandboxes of every evaluation of that task
Evaluation rundn evaluation create --egress TARGETThat run only; unions with the task’s own list
Interactive runtimesandbox.egress.allow in runtime.yamlThe 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 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.

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.

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 else
egress:
allow:
- staging.example.com

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

Dreadnode limits the credentials available inside each sandbox:

Credential or dataControl
E2B project API keyHeld by the Dreadnode API and never injected into agent sandboxes
Evaluation and job API keysRestricted to the platform scopes required by that workload
Interactive runtime API keysFollow the initiating user’s platform permissions
Runtime bearer credentialUnique to one sandbox; required by the Dreadnode runtime API
SDK remote storageFetches temporary workspace-scoped credentials on demand; provisioning does not inject them
Model gateway keyLimited to permitted models, expires, and cleanup requests revocation
User secretsInjected 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.

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.

LayerResponsibility
E2BFirecracker isolation, sandbox infrastructure, and enforcing the policy the platform sends
DreadnodeUser authorization, workload credentials, runtime authentication, egress policy, lifecycle, and cleanup
YouData 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.