Skip to main content
The nono_py module provides functions for applying sandboxes, starting the network proxy, and checking platform support. It also provides policy-loading helpers for filesystem and network policy resolution.

apply

apply(caps: CapabilitySet) -> None
Apply the sandbox with the given capabilities. This is irreversible. Once called, the current process and all child processes can only access resources granted by the capabilities. There is no way to expand permissions after this call.
caps
CapabilitySet
required
The capability set defining permitted operations.
Raises:
  • RuntimeError - Platform not supported or sandbox initialization failed

Example

from nono_py import CapabilitySet, AccessMode, apply

caps = CapabilitySet()
caps.allow_path("/tmp", AccessMode.READ_WRITE)
caps.block_network()

# After this call, the process is sandboxed
apply(caps)

# These work:
with open("/tmp/test.txt", "w") as f:
    f.write("Hello")

# These fail with PermissionError:
open("/etc/passwd", "r")
No escape hatch. Once apply() succeeds:
  • Permissions cannot be expanded
  • The sandbox persists until process exit
  • All child processes inherit the same restrictions
  • There is no “undo” or “disable” function

Error Handling

from nono_py import CapabilitySet, AccessMode, apply, is_supported

caps = CapabilitySet()
caps.allow_path("/tmp", AccessMode.READ_WRITE)

if not is_supported():
    print("Warning: sandboxing not available, running without protection")
else:
    try:
        apply(caps)
        print("Sandbox applied successfully")
    except RuntimeError as e:
        print(f"Failed to apply sandbox: {e}")
        exit(1)

is_supported

is_supported() -> bool
Check if sandboxing is supported on this platform. Returns: True if sandboxing is available.

Example

from nono_py import is_supported

if is_supported():
    print("Sandbox available")
else:
    print("Sandbox not available")

Platform Requirements

PlatformRequirement
LinuxKernel 5.13+ with Landlock enabled
macOSmacOS 10.5+ (always available)
WindowsNot supported (always returns False)
Use support_info() for more detailed information about why sandboxing might not be available.

support_info

support_info() -> SupportInfo
Get detailed information about sandbox support on this platform. Returns: SupportInfo object with platform details.

Example

from nono_py import support_info

info = support_info()

print(f"Platform: {info.platform}")
print(f"Supported: {info.is_supported}")
print(f"Details: {info.details}")
Output examples: macOS:
Platform: macos
Supported: True
Details: Seatbelt sandbox available
Linux with Landlock:
Platform: linux
Supported: True
Details: Landlock ABI v5 available
Linux without Landlock:
Platform: linux
Supported: False
Details: Landlock not available (kernel too old)

Detailed Platform Check

from nono_py import support_info
import sys

def require_sandbox():
    """Exit if sandboxing is not available."""
    info = support_info()

    if info.is_supported:
        return

    print(f"Error: Sandboxing required but not available", file=sys.stderr)
    print(f"Platform: {info.platform}", file=sys.stderr)
    print(f"Details: {info.details}", file=sys.stderr)

    if info.platform == "linux":
        print("\nTo enable Landlock on Linux:", file=sys.stderr)
        print("  1. Upgrade to kernel 5.13 or later", file=sys.stderr)
        print("  2. Ensure CONFIG_SECURITY_LANDLOCK=y in kernel config", file=sys.stderr)

    sys.exit(1)

require_sandbox()

start_proxy

start_proxy(config: ProxyConfig) -> ProxyHandle
Start the nono network filtering proxy. Creates a localhost proxy server that provides domain filtering, credential injection, and audit logging for sandboxed child processes. The proxy runs on a background thread and is shut down via the returned handle.
config
ProxyConfig
required
Proxy configuration including allowed hosts, credential routes, and bind settings.
Returns: ProxyHandle with env_vars(), credential_env_vars(), drain_audit_events(), and shutdown(). Raises:
  • RuntimeError - Proxy failed to start (port in use, etc.)

Example

from nono_py import ProxyConfig, RouteConfig, start_proxy, sandboxed_exec

config = ProxyConfig(
    allowed_hosts=["api.openai.com"],
    routes=[
        RouteConfig(
            prefix="/openai",
            upstream="https://api.openai.com",
            credential_key="openai-key",
        ),
    ],
)
proxy = start_proxy(config)

# Inject env vars into sandboxed child
env = list(proxy.env_vars().items()) + list(proxy.credential_env_vars().items())
result = sandboxed_exec(caps, ["python", "agent.py"], env=env)

# Collect audit trail and shut down
events = proxy.drain_audit_events()
proxy.shutdown()

embedded_policy_json

embedded_policy_json() -> str
Return the bundled policy JSON document as a raw string. Returns: JSON text for the embedded policy.

Example

from nono_py import embedded_policy_json

raw = embedded_policy_json()
print(raw[:80])

load_policy

load_policy(json: str) -> Policy
Parse a policy JSON document into a Policy object. Returns: Policy with group inspection and resolution helpers. Raises:
  • ValueError - Invalid policy JSON

Example

from nono_py import load_policy

policy = load_policy(
    """
    {
      "groups": {
        "offline": {
          "description": "Disable outbound network",
          "network": { "block": true }
        },
        "proxy_web": {
          "description": "Allow only example.com through the proxy",
          "network": { "allow_proxy": ["example.com"] }
        }
      }
    }
    """
)

print(policy.group_names())
proxy_config = policy.resolve_proxy_config(["proxy_web"])
print(proxy_config.allowed_hosts if proxy_config else [])
Policy.resolve_proxy_config() follows the same network field naming used by the main nono profile format:
  • Canonical key: network.allow_domain
  • Accepted aliases: network.allow_proxy, network.proxy_allow
If multiple groups specify different upstream_proxy / external_proxy values, resolution fails with ValueError instead of silently overwriting the earlier proxy.

load_embedded_policy

load_embedded_policy() -> Policy
Parse the bundled policy JSON and return it as a Policy.

Example

from nono_py import load_embedded_policy

policy = load_embedded_policy()
print("deny_credentials" in policy.group_names())

Import

All functions are available from the top-level module:
from nono_py import (
    apply,
    embedded_policy_json,
    is_supported,
    load_embedded_policy,
    load_policy,
    sandboxed_exec,
    start_proxy,
    support_info,
)