OpenShell sandboxes

Network and egress

How DefenseClaw's egress proxy decides every site a sandboxed agent contacts, the open, balanced and strict network profiles, the blocklist, unblocks, asks, large-upload alerts and what the agent is told when it is blocked.

A sandbox has no network of its own. It has three ways out, and DefenseClaw watches all three:

  • Web traffic goes through DefenseClaw's egress proxy, which the sandbox reaches at host.openshell.internal through HTTPS_PROXY and HTTP_PROXY. The proxy decides and logs every site.
  • Model calls go to the harness's model provider directly through OpenShell, which adds the real credential outside the sandbox. The sandbox only ever holds a placeholder.
  • Anything else, such as a client that ignores the proxy settings, is refused by OpenShell, which files a connection request. DefenseClaw decides most of those on its own and asks you about the rest.

How a site is decided

The proxy checks each request in a fixed order, and the first match decides. Every sandbox is decided by its own policy: another sandbox's pack, block list or unblocks never apply to it.

yes
no
yes
no
yes
no
yes
no
open
balanced
Agent runtimeAgent opens a sitethrough the proxy
DecisionThis machine or ametadata address?
PolicyBlockednever unblockable
DecisionOn your org's oryour block list?
PolicyBlockedunblock cannot lift
DecisionUnblocked, or onan allow list?
SystemAllowedand logged
DecisionOn the blocklistfeed?
PolicyBlockedyou can unblock
DecisionNetwork profiledefault
Systemopen: any nameIP address blocked
Policybalanced: blockedyou can unblock
How the egress proxy decides a site, simplified. Private networks and the port list are checked with the first step. The full order has twelve steps, including your organization's allow-only list.

In words:

  1. This machine, link-local and cloud metadata addresses are always blocked. Nothing opens them. To let the sandbox use one service on your machine, start it with --host-port PORT, which you then approve.
  2. Private networks such as 10.x, 192.168.x and intranet names are blocked unless an allow entry names them exactly.
  3. Ports other than those on the proxy port list are blocked. The default list is 80 and 443.
  4. Your organization's block list, then its allow-only list, if it has one. Nothing you do unblocks these.
  5. Your block list, the pack's entries plus openshell.egress.block. An unblock does not lift it; remove the entry instead.
  6. Your unblocks allow the site.
  7. The allow list allows the site, and exempts it from the blocklist feed.
  8. The blocklist feed blocks paste sites, anonymous file drops, webhook catchers, tunnels and anonymizers. You can unblock these.
  9. The profile's default decides everything else. open allows any host name but blocks a bare IP address until you unblock it, because an IP address would get around the name-based blocklist. balanced blocks anything outside its curated allowlist, and you can unblock it.

How a destination is decided lists all twelve steps, including the organization settings.

Network profiles

Every sandbox runs one policy pack. The built-in packs set the network profile and more:

open (default)balancedstrict
Web accessAny site by name, except the blocklist, private networks and this machineOnly a curated developer allowlist: package registries, source hosting, toolchains and documentationNone: only the harness's model provider
Bannernetwork: open + blocklistnetwork: allowlist (balanced)network: provider hosts only (strict)
Connection requestsDecided automaticallyTriaged, the unclear ones askedEvery one asked
Large-upload alert25 MiB10 MiB5 MiB
Your projectMounted liveMounted liveCopied
Skip-permissions modeOnOnOff: the harness keeps its prompts

Pick one for a run:

defenseclaw sandbox run claude --profile balanced
defenseclaw sandbox run claude --profile strict

Or set it for every run with openshell.profile in ~/.defenseclaw/config.yaml. On a Mac every run works on a copy, whichever pack it uses.

The open profile is a deliberate trade-off

With open, an agent tricked by a prompt injection could send your code to any site that is not on the blocklist. DefenseClaw's tool-call checks, the blocklist, large-upload alerts, hidden secrets and endpoint-bound credentials limit the damage. The proxy sees HTTPS only as encrypted tunnels, so it cannot tell a download from an upload. For sensitive code, use balanced or strict.

See policy packs and admin controls to write your own pack or set limits for your organization.

Allow and block your own sites

To let every sandbox reach an intranet host, such as a package mirror, add its exact name or address to the allow list. To keep sandboxes away from a site, add it to the block list:

openshell:
  egress:
    allow:
      - npm.mirror.corp.example
    block:
      - files.example.net

A change reaches running sandboxes on the next config reload.

Unblock a site

When a block is a false positive, lift it for one sandbox or for all of them:

defenseclaw sandbox unblock webhook.site --sandbox myapp-7f3a
defenseclaw sandbox unblock webhook.site --always

--always adds the host to openshell.egress.unblocked in config.yaml. You can also unblock from the TUI (u in the Sandboxes panel) or from the macOS app's notification, menu bar or Sandboxes view.

An unblock lifts blocklist-feed blocks, IP-address blocks and, with balanced, sites outside the allowlist. It never opens this machine, private networks or metadata addresses, and never overrides your block list or your organization's. If your organization turns unblocking off (openshell.admin.allow_unblock: false), the command says so:

blocked by your organization's DefenseClaw policy: egress.unblock

strict has no web proxy, so there is nothing to unblock there; its direct connection requests wait as asks instead.

Asks

A connection that does not go through the proxy is refused by OpenShell, which files a request to open it. DefenseClaw decides most of those itself. It asks you only about doors into your machine or network (a port on this machine, a private address, an intranet name) and about requests OpenShell's policy advisor flags. balanced also asks about sites outside its allowlist, and strict about every request.

defenseclaw sandbox approvals --watch
defenseclaw sandbox approve myapp-7f3a ap_5f2c9a1e7b3d4c60
defenseclaw sandbox reject myapp-7f3a ap_5f2c9a1e7b3d4c60

approve --always keeps the decision for future sandboxes, except for your own machine and private networks, which open only for one sandbox at a time. An approval takes effect at the sandbox's next quiet moment, because every OpenShell policy change closes its open connections.

Large uploads

The proxy counts what each sandbox sends. When a sandbox sends more than the threshold to a site it had not contacted before, DefenseClaw reports it in the feed and as a finding. The upload itself goes on.

To cut such uploads off, turn the block on:

openshell:
  egress:
    large_upload_mb: 10
    block_large_uploads: true

With the block on, the upload stops before the chunk that would take it past the threshold, and later connections to that site are refused. The feed shows:

✗ files.example.net (large upload blocked: this sandbox tried to send more than 10 MiB to a destination it had not contacted before)  → unblock: defenseclaw sandbox unblock files.example.net --sandbox myapp-7f3a

Sites you unblocked or put on an allow list are exempt: uploads there are only reported. The balanced allowlist does not exempt a site, so a first large push to github.com is cut too. Add such hosts to openshell.egress.allow before you turn the block on. An organization can turn the block on for every sandbox with openshell.admin.block_large_uploads.

See large-upload alert threshold.

What the agent is told

A blocked agent should stop and tell you, not look for another way out. DefenseClaw tells it so in two ways.

A plain HTTP request gets a 403 with a JSON body written for the agent (shortened here):

{
  "error": "egress_blocked",
  "message": "DefenseClaw blocked sandbox egress to webhook.site:80 (webhook_catcher): Webhook catchers record every request sent to them for whoever holds the URL, a common exfiltration sink.",
  "host": "webhook.site",
  "port": 80,
  "category": "webhook_catcher",
  "source": "feed",
  "mode": "open",
  "sandbox": "myapp-7f3a",
  "unblockable": true,
  "how_to_unblock": "Tell the user DefenseClaw blocked this destination. They can allow it for this sandbox with `defenseclaw sandbox unblock webhook.site --sandbox myapp-7f3a`, for every sandbox with `defenseclaw sandbox unblock webhook.site --always`, or from the DefenseClaw activity feed. Do not try to reach it another way."
}

An HTTPS request fails at the tunnel, so tools show only a connection error such as curl: (56) CONNECT tunnel failed, response 403. After the shell or web-fetch tool call, DefenseClaw's hooks add a note to the harness's context that names the blocked site, why it was blocked and the unblock command, and tells the agent not to try another way. A cut large upload is reported the same way.

HarnessGets the note from the hooks
Claude Code, Codex, GitHub Copilot CLI, Cursor, DevinYes
Other harnessesNo: the activity feed and your terminal report the block

Each block is told once, and only to the sandbox that hit it.

The proxy credential in verbose output

The proxy address the sandbox gets holds a user name and password of its own, so curl -v prints a Proxy-Authorization: Basic … header, and the agent may warn you that a credential leaked. It is not one of your secrets. DefenseClaw makes one for each sandbox; it works only on the egress proxy on this machine's loopback address, lets the sandbox reach only what its policy allows, and stops working when the sandbox is deleted.

See what happened

defenseclaw sandbox activity -f
defenseclaw sandbox status myapp-7f3a
defenseclaw sandbox policy explain

activity is the live feed of sites, blocks, asks and findings. The Egress row of status counts the sites contacted and blocked and the bytes sent and received. policy explain shows each effective setting and the layer it came from: the pack, your config, a run option or your organization.