Domain-Level Access Control

Network Filtering

Block all network access, allow specific domains, filter individual API endpoints, and route through upstream proxies. The proxy starts on a random localhost port and the kernel sandbox restricts the child to that port only.

How it works

By default, sandboxed processes have unrestricted network access. When you enable domain filtering via --network-profile, --allow-domain, or --credential, an HTTP proxy starts on a random localhost port (or a fixed port via --proxy-port). The child sandbox is restricted to that port only, and a 256-bit session token authenticates every request.

terminal
# Block all network access
nono run --allow-cwd --block-net -- cargo build
# Allow specific domains only
nono run --allow-cwd --allow-domain api.openai.com \
--allow-domain api.anthropic.com -- my-agent
# Use a built-in network profile
nono run --allow-cwd --network-profile claude-code -- claude
# Add custom domains to a profile
nono run --allow-cwd --network-profile developer \
--allow-domain my-internal-api.example.com -- my-agent

Built-in Network Profiles

Pre-configured domain sets for common workflows:

  • minimal — LLM API access only
  • developer — General development
  • claude-code — Claude Code agent
  • codex — Codex agent with OpenAI credential
  • enterprise — Corporate environments

Always-Denied Destinations

These destinations are blocked regardless of configuration and cannot be overridden:

  • 169.254.169.254 — AWS/GCP/Azure metadata
  • metadata.google.internal — GCP metadata
  • 169.254.0.0/16 — IPv4 link-local
  • fe80::/10 — IPv6 link-local

Domain group profiles

Network profiles are composed from domain groups. Key groups include llm_apis (OpenAI, Anthropic, Google), package_registries (npm, PyPI, crates.io), github, sigstore, and cloud platform groups for google_cloud, azure, and aws_bedrock. You can extend any profile with additional domains via CLI flags or profile configuration.

profiles/my-agent.json
{
"network": {
"network_profile": "developer",
"allow_domain": ["my-internal-api.example.com"]
}
}
audit log (verbose)
$ nono run -vv --network-profile claude-code --allow-cwd -- my-agent
ALLOW CONNECT api.openai.com:443
ALLOW CONNECT github.com:443
DENY CONNECT 169.254.169.254:80 reason=denied_cidr
ALLOW REVERSE openai POST /v1/chat/completions -> 200

Endpoint filtering

Beyond domain-level control, you can restrict access to specific API endpoints. Patterns support exact paths, single-segment wildcards (*), and multi-segment wildcards (**). Method can be a specific HTTP verb or * for any.

terminal
# Allow only chat completions on OpenAI
nono run --allow-cwd --credential openai \
--allow-endpoint 'openai:POST:/v1/chat/completions' \
-- my-agent
# Fine-grained GitHub API access
nono run --allow-cwd --credential github \
--allow-endpoint 'github:GET:/repos/*/issues/**' \
--allow-endpoint 'github:POST:/repos/*/issues/*/comments' \
-- my-agent
profiles/gitlab-read.json
{
"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"
}
]
}
}
}
}

Upstream proxy

For corporate environments, route all filtered traffic through an upstream proxy. Bypass rules let internal hosts skip the upstream proxy while still being subject to nono domain filtering. Configuration also works via NONO_UPSTREAM_PROXY and NONO_UPSTREAM_BYPASS environment variables.

terminal
# Route through corporate proxy
nono run --allow-cwd --network-profile enterprise \
--upstream-proxy squid.corp:3128 -- my-agent
# Bypass proxy for internal hosts
nono run --allow-cwd --network-profile enterprise \
--upstream-proxy squid.corp:3128 \
--upstream-bypass git.internal.corp \
--upstream-bypass "*.dev.local" \
-- my-agent
profiles/enterprise.json
{
"network": {
"network_profile": "enterprise",
"upstream_proxy": "squid.corp:3128",
"upstream_bypass": [
"git.internal.corp",
"*.dev.local"
]
}
}

Localhost IPC

Open specific localhost ports for inter-process communication, even when all external network access is blocked. Use --open-port for bidirectional access or --listen-port for listen-only. Both can be combined with domain filtering.

terminal
# Bidirectional localhost IPC on port 3000
nono run --block-net --open-port 3000 \
--allow ./mcp-server -- node server.js
# Combine with domain filtering
nono run --network-profile claude-code \
--open-port 3000 --allow-cwd -- claude
# Listen-only port for dev servers
nono run --allow-cwd --network-profile developer \
--listen-port 3000 -- npm run dev

Platform behaviour

Linux: Requires Landlock ABI v4+ (Linux 6.7+). Uses per-port TCP rules for full enforcement.

macOS: Uses Seatbelt rules limiting the child to localhost:PORT only. No per-port destination filtering.

WSL2: --block-net works; domain filtering is disabled by default due to kernel limitations. Enable with wsl2_proxy_policy: "insecure_proxy" in profile security config.

See the networking docs for full details.

Get started with nono

Runtime safety infrastructure that works on macOS, Linux, Windows, and in CI.