Skip to main content
By default the sandboxed process has unrestricted network access. nono provides two ways to restrict it: block everything, or filter outbound traffic to a set of allowed domains.

Blocking All Network

nono run --allow-cwd --block-net -- cargo build
No outbound connections are permitted. See Localhost IPC for allowing specific ports as exceptions. In a profile, set network.block to true:
{
  "network": { "block": true }
}

Domain Filtering

The proxy starts whenever any domain-filtering flag is present: --network-profile, --allow-domain, --credential, or --upstream-proxy (from CLI args or profile config). It restricts outbound traffic to a set of allowed domains.
# Allow specific domains
nono run --allow-cwd --allow-domain api.openai.com --allow-domain api.anthropic.com -- my-agent

# Use a predefined network profile
nono run --allow-cwd --network-profile claude-code -- claude
Proxy architecture flow The proxy runs in the unsandboxed parent process. TLS is end-to-end between the child and upstream — the proxy never sees plaintext.

How It Works

When domain filtering is active:
  1. An HTTP proxy starts on a random localhost port (or a fixed port via --proxy-port)
  2. The child sandbox restricts outbound to only localhost:<port>
  3. HTTP_PROXY and HTTPS_PROXY are set in the child environment
  4. Every CONNECT request is validated against the domain allowlist
A 256-bit session token is generated for each proxy session. The child must include it in every request (Proxy-Authorization header), preventing other localhost processes from using the proxy. The proxy resolves DNS itself and checks all resolved IPs against the deny list before connecting. This prevents DNS rebinding attacks where a malicious DNS server maps an allowed hostname to an internal IP.

Proxy Port

By default the proxy binds to an OS-assigned ephemeral port. Use --proxy-port to fix it when the application requires a known port:
nono run --profile openclaw --proxy-port 19999 --listen-port 18789 -- openclaw gateway
Applications that read OPENAI_BASE_URL, ANTHROPIC_BASE_URL, etc. from the environment don’t need --proxy-port — nono sets these automatically.

Network Profiles

Network profiles are named sets of allowed domains composed from groups. They are defined in network-policy.json (embedded in the binary) and activated with --network-profile or via a profile’s network.network_profile field.

Built-in Profiles

ProfileGroupsUse Case
minimalllm_apisLLM API access only
developerllm_apis, package_registries, github, sigstore, documentationGeneral development
claude-codellm_apis, package_registries, github, sigstore, documentationClaude Code agent
codexSame as developer + bundled openai credentialCodex agent
opencodeSame as developer + bundled google-ai credentialOpenCode agent
enterpriseAll groups + google_cloud, azure, aws_bedrockCorporate environments

Groups

Each group maps to a set of allowed hostnames and wildcard suffixes:
GroupHosts
llm_apisapi.openai.com, api.anthropic.com, generativelanguage.googleapis.com, …
package_registriesregistry.npmjs.org, pypi.org, crates.io, …
githubgithub.com, api.github.com, raw.githubusercontent.com, …
sigstorefulcio.sigstore.dev, rekor.sigstore.dev, tuf-repo-cdn.sigstore.dev
documentationdocs.python.org, developer.mozilla.org, doc.rust-lang.org, …
google_cloud*.googleapis.com
azure*.openai.azure.com, *.cognitiveservices.azure.com
aws_bedrock*.bedrock.amazonaws.com, *.bedrock-runtime.amazonaws.com

Adding Domains

Use --allow-domain on the command line or allow_domain in a profile to add domains on top of the network profile:
{
  "meta": { "name": "my-agent" },
  "filesystem": { "allow": ["$WORKDIR"] },
  "network": {
    "network_profile": "developer",
    "allow_domain": ["my-internal-api.example.com"]
  }
}
nono run --allow-cwd --network-profile developer --allow-domain my-internal-api.example.com -- my-agent

Upstream Proxy

For corporate environments with a mandatory outbound proxy, chain domain-filtered traffic through it with --upstream-proxy. This is typically paired with the enterprise network profile:
nono run --allow-cwd --network-profile enterprise \
  --upstream-proxy squid.corp:3128 -- my-agent
CONNECT requests are chained through the corporate proxy. Cloud metadata endpoints are still denied regardless.

Bypassing the Upstream Proxy

Some domains may need to bypass the upstream proxy and connect directly:
nono run --allow-cwd --network-profile enterprise \
  --upstream-proxy squid.corp:3128 \
  --upstream-bypass git.internal.corp \
  --upstream-bypass "*.dev.local" \
  -- my-agent
Bypass patterns support exact hostnames and *. wildcard suffixes (case-insensitive). Matching hosts route directly; everything else goes through the upstream proxy. This can also be configured in a profile:
{
  "network": {
    "network_profile": "enterprise",
    "upstream_proxy": "squid.corp:3128",
    "upstream_bypass": ["git.internal.corp", "*.dev.local"]
  }
}
Or via environment variables:
export NONO_UPSTREAM_PROXY=squid.corp:3128
export NONO_UPSTREAM_BYPASS=git.internal.corp,*.dev.local
nono run --allow-cwd --network-profile enterprise -- my-agent

Endpoint Filtering

When credential injection routes traffic through the reverse proxy, you can further restrict which HTTP method+path combinations are allowed on a per-service basis. This enforces least-privilege at the API level — the agent can reach an allowed domain but only use specific endpoints.

CLI Usage

Use --allow-endpoint to restrict a credential service to specific patterns:
# Allow only chat completions through OpenAI
nono run --allow-cwd --credential openai \
  --allow-endpoint 'openai:POST:/v1/chat/completions' \
  -- my-agent

# Allow GitHub issue reads and comment writes, block everything else
nono run --allow-cwd --credential github \
  --allow-endpoint 'github:GET:/repos/*/issues/**' \
  --allow-endpoint 'github:POST:/repos/*/issues/*/comments' \
  -- my-agent
When any endpoint rules are configured for a service, requests that don’t match receive 403 Forbidden and are logged in the audit trail.

Profile Configuration

Endpoint rules can also be defined on custom credentials within profiles:
{
  "network": {
    "custom_credentials": {
      "gitlab": {
        "upstream": "https://gitlab.example.com",
        "credential_key": "gitlab_token",
        "endpoint_rules": [
          { "method": "GET", "path": "/api/v4/projects/*/merge_requests/**" },
          { "method": "POST", "path": "/api/v4/projects/*/merge_requests/*/notes" }
        ]
      }
    }
  }
}
See Credential Injection — Custom Credential Definitions for the full custom_credentials schema.

Pattern Syntax

Path patterns use standard glob syntax (same as .gitignore and nono profile include patterns):
PatternMatches
/v1/chat/completionsExact path only
/repos/*/issuesOne segment wildcard (e.g., /repos/myrepo/issues)
/api/**Zero or more segments (e.g., /api/v1/data/export)
*Any method (when used as the method field)

Localhost IPC

Use --open-port to allow bidirectional localhost TCP on a specific port (connect + listen). This enables IPC between sandboxed processes — for example, an MCP server in one sandbox and an AI agent in another.
# Terminal 1: MCP server listening on port 3000
nono run --block-net --open-port 3000 --allow ./mcp-server -- node server.js

# Terminal 2: Agent connecting to the MCP server
nono run --block-net --open-port 3000 --allow ./client -- claude
--open-port works alongside domain filtering. Outbound to allowed hosts goes through the proxy; IPC stays on localhost:
nono run --network-profile claude-code --open-port 3000 --allow-cwd -- claude
This generates Seatbelt rules (macOS) or Landlock rules (Linux) that allow both connect() and bind() on the specified port, in addition to the proxy port. See CLI Reference for full details and platform limitations.

Listen-Only Ports

Use --listen-port when a sandboxed process needs to accept inbound connections but does not need to initiate outbound connections on that port (e.g., a server or gateway):
nono run --allow-cwd --network-profile developer --listen-port 3000 -- npm run dev
See CLI Reference for platform limitations.

Always-Denied Destinations

The following destinations are always blocked by the proxy, regardless of configuration. These cannot be overridden.
DestinationWhy
169.254.169.254AWS/GCP/Azure instance metadata
metadata.google.internalGCP metadata alias
metadata.azure.internalAzure metadata alias
169.254.0.0/16 (resolved IP)IPv4 link-local — DNS rebinding protection
fe80::/10 (resolved IP)IPv6 link-local — DNS rebinding protection
Private network addresses (RFC1918: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) are allowed to support enterprise environments.

Platform Behavior

Linux

Network filtering uses Landlock V4+ per-port TCP rules. The sandbox restricts connect() to only the proxy port. All other outbound TCP is blocked at the kernel level. Requirements: Landlock ABI v4+ (Linux 6.7+)

macOS

Network filtering uses Seatbelt rules. The sandbox allows only (remote tcp "localhost:PORT") and denies all other network operations.
(deny network*)
(allow network-outbound (remote tcp "localhost:PORT"))
(allow system-socket (socket-domain AF_INET) (socket-type SOCK_STREAM))
(allow system-socket (socket-domain AF_INET6) (socket-type SOCK_STREAM))

WSL2

The WSL2 kernel (6.6) ships with Landlock V3, which does not include TCP network filtering (V4 requires kernel 6.7+). Additionally, the seccomp-based proxy fallback is unavailable due to WSL2’s own seccomp notify listener. What works: --block-net (blocks all networking via SECCOMP_RET_ERRNO). What doesn’t work: Per-port filtering (--open-port, --listen-port) and proxy-based domain filtering (--allow-domain, --network-profile, --credential). Domain filtering is blocked by default on WSL2 — set wsl2_proxy_policy: "insecure_proxy" in your profile’s security config to opt in to degraded execution. See Credential Proxy on WSL2 for details. When Microsoft upgrades the WSL2 kernel to 6.7+, per-port filtering will activate automatically. See WSL2 Support for full details.

Audit Logging

All proxy decisions are logged via tracing:
ALLOW CONNECT api.openai.com:443
DENY  CONNECT 169.254.169.254:80 reason=denied_cidr
Enable verbose logging to see proxy decisions:
nono run -vv --network-profile claude-code --allow-cwd -- my-agent

Limitations

  • HTTP/1.1 only — The CONNECT tunnel passes raw bytes (HTTP/2 works end-to-end), but the reverse proxy mode speaks HTTP/1.1 to upstream
  • No per-port filtering on macOS — Seatbelt cannot filter outbound by destination port
  • Domain filtering requires supervised execution — The proxy runs in the unsandboxed parent process, so nono wrap (Direct mode) is incompatible. Use nono run instead.
  • WSL2: no per-port filtering or domain filtering by default — See WSL2 section above

Next Steps