3.3

sbx policy init, allow network, deny network, and check network

A global preset plus allow and deny rules in two scopes decide every connection, deny wins, and check network asks the same authorizer without sending anything.

Your agent reports that npm install failed with a connection error, and the sandbox keeps no shell history to say why. The question is which rule decided. sbx policy answers it in two halves: ls for the rules that exist, and check network for the decision one host would get.

When you finish this section, you can initialize a policy, add a rule in the right scope, and predict the decision for any host.

The preset and the global policy

policy init: sets the first global policy, not a default for each sandbox. It runs once, before the first sandbox, with allow-all, balanced, or deny-all, and sbx policy reset or sbx daemon start --policy starts over. The capture host chose balanced on v0.47.0, and sbx policy ls then showed one policy, local-policy, with network: 194 allow (the capture). The wide JSON lists eight rules with created_via: default: six network groups and two filesystem rules that allow every path. The list changes between releases without a changelog (conflict C23), so this is the list as recorded on 2026-10-08:

two of the six balanced network groupsjsonFull log
      "id": "default-ai-services",
      "name": "default-ai-services",
      "policy_id": "local-policy",
      "scope": "global",
      "applies_to": "all",
      "resource_type": "network",
      "decision": "allow",
      "resources": [
        "api.anthropic.com:443",
        "statsig.anthropic.com:443",
        "platform.claude.com:443",
…
        "**.openai.com:443",
…
      "id": "default-package-managers",
…
        "registry.npmjs.org:443",
…
        "pypi.org:443",
…
      "provenance": {
        "created_via": "default"
      },
      "actions": [
        "net:connect:tcp"
      ]
Each group is cut after its first resources. The other groups are default-code-and-containers, default-os-packages, default-cloud-infrastructure, and default-cert-validation.

Rule grammar and scope

Allow rule: a comma-separated list of hostnames, domains, IP addresses, or CIDR prefixes. The forms are example.com, *.example.com, **.example.com, api?.example.com, api[12].example.com, example.com:443, [2001:db8::1]:443, a CIDR, and ** for every host. A lone * and an escaped glob are rejected. An allow rule covers TCP unless --protocol says otherwise, and a deny rule covers TCP and UDP. The capture shows both: the allow came back as (example.com [tcp]) and the deny as (example.com [tcp,udp]) (the captures). The deny also warned that UDP egress stays off until feature.udp-egress is on.

Two scopes have existed since v0.29.0 (conflict C41): global, the default, and local, which --sandbox scopes to one sandbox. A rule added with --sandbox m101-policy printed Rule added to policy local (scope: sandbox:m101-policy): <uuid>, and sbx policy inspect shows its scope, layer, origin, and provenance:

the sandbox-scoped allow rulejsonFull log
      "id": "<uuid>",
      "name": "<uuid>",
      "policy_id": "<uuid>",
      "scope": "sandbox:m101-policy",
      "applies_to": "sandbox:m101-policy",
      "resource_type": "network",
      "decision": "allow",
      "resources": [
        "example.com"
      ],
      "origin": "scoped",
      "layer": "local",
      "status": "active",
      "editable": true,
      "sandbox_id": "m101-policy",
      "provenance": {
        "created_via": "added"
      },
      "actions": [
        "net:connect:tcp"
      ]
The outer policy object and the remove_command field are cut.

--deny-network HOST on create or run adds the same kind of per-sandbox deny at creation. The help says why that is safe under governance: a local deny can only make egress narrower, never wider. A kit adds rules with created_via: provisioned, and sbx policy ls --source org on the capture host printed No policies match the selected filters. (the capture).

Deny wins: for the same hostname or CIDR, a deny rule wins over an allow rule. An allowed hostname is not checked against CIDR rules for the IP address it resolves to.

The CLI refuses a deny that conflicts with an allow in the same scope: deny: "example.com" conflicts with existing allow rule "<uuid>" (the capture). The capture removed the allow with sbx policy rm network --sandbox m101-policy --id <uuid> --force before the deny was accepted. Under organization governance only organization allow rules grant access, while local and kit deny rules still apply, and org policies prints that table.

policy ls filters with --source local|org|kit, --decision, --type, --created-via default|added|provisioned|approval, --protocol, --wide for RULE_ID, and --json. A wide row carries METHOD and PATH columns. The docs fill them with --method and --path flags. The v0.47.0 help of sbx policy allow network lists neither flag, so this manual marks local HTTP rules as unverified. The proxy returns to them.

check network

policy check network: a read-only call that runs the same policy authorizer in the daemon that enforces the sandbox network. A host without a port is evaluated with port 443, and the command checks network access only, not the HTTP method or path. It exits 1 on a denial. The capture ran it on example.com in the m101-policy context before any rule, after the allow, and after the deny:

the explicit denial after the deny rulejsonFull log
{
  "action": "net:connect:tcp",
  "allowed": false,
  "context": "sandbox:m101-policy",
  "deny_kind": "explicit",
  "governance": {
    "active": false
  },
  "origin": "local",
  "reason": "Denied by local rule",
  "resource_type": "net:domain",
  "resource_value": "example.com:443",
  "rule": "local:<uuid>",
  "target": "example.com:443",
  "type": "network"
}
The implicit denial in 09-check-verbose.json has deny_kind implicit, no origin, no rule, and the reason No matching allow rule (default deny).

After the allow, the same call returned "allowed": true with no rule field (the capture). The global check of api.anthropic.com did the same under "context": "global" (the capture). The two denials differ in deny_kind: implicit names no rule, and explicit names local:<uuid>. An implicit denial is also what a sandbox turns into an approval request, which the next section shows. Figure 3.3 walks the request through the same order.

Fig. 3.3how one request to example.com:443 is decideddecision
How one request to example.com:443 is decided The authorizer walks five questions for op(action=net:connect:tcp, resource=net:domain:example.com:443) in the sandbox m101-policy. A matching deny rule ends in Denied by local rule with deny_kind explicit and rule local:<uuid>. Organization governance was inactive on the capture host, and when active only organization allow rules grant access. A sandbox-scoped allow or a global or kit allow returns allowed true, as the checks for example.com and api.anthropic.com show. When nothing matches, the result is No matching allow rule with deny_kind implicit, and the sandbox sees an approval request. THE REQUEST op(action=net:connect:tcp, resource=net:domain:example.com:443) · context sandbox:m101-policy a deny rule matches? any scope, any source Denied by local rule deny_kind: explicit · origin: local rule: local:<uuid> · 09-check-deny.json no org governance active? governance.active false on the capture host when true, only org allow rules grant local and kit allow rules go inactive a sandbox allow matches? scope sandbox:m101-policy allowed: true local <uuid> example.com [tcp] 09-check-allowed.json a global or kit allow matches? local-policy, kit:<name> allowed: true default-ai-services api.anthropic.com:443 03-policy-check-anthropic.json nothing matched default deny No matching allow rule (default deny) deny_kind: implicit · 09-check-verbose.json inside: Approval required for example.com:443
A matching deny ends the walk at once, an allow from any active scope admits the host, and a request that matches nothing is denied as implicit. Read top down. Each left box is one question the authorizer asks, and the right box is the answer the capture recorded for it. Dashed rose arrows are denials.

Docker Sandboxes (sbx) and Docker Agent (docker-agent) sbx 0.47.0, docker-agent 1.149.0 · bf4169c · 2026-10-07 · Edition 2026.10