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.
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
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 | Requirement |
|---|
| Linux | Kernel 5.13+ with Landlock enabled |
| macOS | macOS 10.5+ (always available) |
| Windows | Not 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)
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.
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,
)