Network Isolation and Egress Policy
A core security premise of DynoCap is that code generated or executed by an AI agent must not have unrestricted access to the Internet by default.
This guide explains how to configure network egress policies for both raw sandboxes and project tasks.
Network Modes Overview
DynoCap supports three explicit network modes:
| Mode | Virtual Network Device | Outbound Permitted | Use Case |
|---|---|---|---|
none (default) |
None (no virtio-net) |
Zero network traffic | Running untrusted code, tests, and agent modifications. |
filtered |
virtio-net + Host proxy |
HTTPS (443) only to npm registry and Git host | Dependency warming (npm ci) and public Git cloning. |
nat |
virtio-net + macOS NAT |
Unrestricted outbound | Tasks requiring external APIs or non-standard registries. |
1. Offline Mode (--network none)
By default, any dynocap run or dynocap clone command boots without a network device:
# Explicitly or by default
dynocap clone ./my-project sandbox --network noneInside the Linux guest:
- The kernel exposes only the loopback interface (
lo). - There is no
eth0orenp0s1virtual network adapter. - Packet transmission is physically impossible at the hypervisor level.
Even if an attacker inside the VM executes curl, nc, or attempts DNS lookups, the operations fail immediately with Network is unreachable.
2. Filtered Egress Mode (--network filtered)
In filtered mode, DynoCap attaches a virtual network interface backed by a dedicated host-level user-space forwarder (FilteredForwarder.swift):
dynocap clone ./my-project sandbox --network filteredAllowlist Rules
The forwarder inspects initial TLS ClientHello packets and socket connections:
- Port 443 Only: Non-HTTPS ports (e.g. HTTP on port 80, SSH on port 22, raw TCP) are dropped.
- Designated Hostnames: exact names only, never their subdomains.
- The task runtime’s package registry, as declared in the prepared runtime manifest:
node:24,node:22:registry.npmjs.orgpython:3.14:pypi.org,files.pythonhosted.orggo:1.27:proxy.golang.org,sum.golang.orgshell:1: none
- The remote Git origin host if the project was cloned from an HTTPS URL.
- A plain
dynocap runsandbox has no runtime and keepsregistry.npmjs.org.
- The task runtime’s package registry, as declared in the prepared runtime manifest:
A direct connection to another IP or domain (e.g., pastebins, command-and-control servers, webhooks) is terminated at the host boundary.
Filtered egress is not an exfiltration barrier. The allowed hosts accept data:
npm publishreachesregistry.npmjs.org,proxy.golang.orgfetches module paths on attacker-chosen domains, andgit pushreaches the repository host. Assume a filtered sandbox can send out anything it can read. See What filtered egress does not stop.
Inspecting Active Network Configuration
You can verify the network policy of any running sandbox:
dynocap inspect sandbox --jsonThe output will clearly state:
{
"network": "filtered",
"phase": "running"
}3. Unrestricted NAT Mode (--network nat)
For tasks that legitimately require general Internet access, such as downloading datasets, interacting with external web APIs, or installing system-level packages via apk:
dynocap clone ./my-project sandbox --network natUnder NAT mode:
- Apple’s
VZNATNetworkDeviceAttachmentprovides NAT routing through your Mac’s active network interfaces. - The guest obtains an IP via DHCP from macOS.
- Outbound traffic is unrestricted; inbound traffic from external networks remains unreachable.
- The guest can reach your local network and services your Mac listens on through any interface, such as development servers bound to
0.0.0.0.
Caution: Do not use
natfor untrusted agent evaluations when secrets or sensitive files are in scope, as the agent can send arbitrary payloads over the Internet.
Network Restrictions in TypeScript SDK & MCP
When automating tasks programmatically or via LLM tool-calling (MCP):
TypeScript SDK
The SDK defaults to none:
import { Sandbox, Task } from "@dynocap/sdk";
// Offline task
const task = await Task.start({
source: "./my-project",
network: "none",
});
// Require offline enforcement (throws if sandbox has network)
const task = await Task.open("my-task", { requireOffline: true });Model Context Protocol (MCP) Server
To protect developers, the @dynocap/mcp server rejects nat and filtered commands by default. You must explicitly start the MCP server with environment variables enabling them:
# Enable filtered network mode for agents
DYNOCAP_MCP_ALLOW_FILTERED=1 npx @dynocap/mcp
# Enable unrestricted NAT mode
DYNOCAP_MCP_ALLOW_NAT=1 npx @dynocap/mcpIf an LLM requests a sandbox with network: "nat" and the flag is not set, the tool returns:
NAT is disabled by this MCP host; start the server with DYNOCAP_MCP_ALLOW_NAT=1 to permit itThis page is also plain Markdown, for agents and for you.