> ## Documentation Index
> Fetch the complete documentation index at: https://nono.sh/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Module Functions

> Top-level functions in nono_py

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

```python theme={null}
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.

<ParamField path="caps" type="CapabilitySet" required>
  The capability set defining permitted operations.
</ParamField>

**Raises:**

* `RuntimeError` - Platform not supported or sandbox initialization failed

### Example

```python theme={null}
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")
```

<Warning>
  **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
</Warning>

### Error Handling

```python theme={null}
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

```python theme={null}
is_supported() -> bool
```

Check if sandboxing is supported on this platform.

**Returns:** `True` if sandboxing is available.

### Example

```python theme={null}
from nono_py import is_supported

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

### Platform Requirements

| Platform | Requirement                            |
| -------- | -------------------------------------- |
| Linux    | Kernel 5.13+ with Landlock enabled     |
| macOS    | macOS 10.5+ (always available)         |
| Windows  | Not supported (always returns `False`) |

<Tip>
  Use `support_info()` for more detailed information about why sandboxing might not be available.
</Tip>

***

## support\_info

```python theme={null}
support_info() -> SupportInfo
```

Get detailed information about sandbox support on this platform.

**Returns:** `SupportInfo` object with platform details.

### Example

```python theme={null}
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

```python theme={null}
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

```python theme={null}
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.

<ParamField path="config" type="ProxyConfig" required>
  Proxy configuration including allowed hosts, credential routes, and bind settings.
</ParamField>

**Returns:** `ProxyHandle` with `env_vars()`, `credential_env_vars()`, `drain_audit_events()`, and `shutdown()`.

**Raises:**

* `RuntimeError` - Proxy failed to start (port in use, etc.)

### Example

```python theme={null}
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

```python theme={null}
embedded_policy_json() -> str
```

Return the bundled policy JSON document as a raw string.

**Returns:** JSON text for the embedded policy.

### Example

```python theme={null}
from nono_py import embedded_policy_json

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

***

## load\_policy

```python theme={null}
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

```python theme={null}
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

```python theme={null}
load_embedded_policy() -> Policy
```

Parse the bundled policy JSON and return it as a `Policy`.

### Example

```python theme={null}
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:

```python theme={null}
from nono_py import (
    apply,
    embedded_policy_json,
    is_supported,
    load_embedded_policy,
    load_policy,
    sandboxed_exec,
    start_proxy,
    support_info,
)
```

## Related

* [CapabilitySet](/python/api/capability-set) - Build capabilities to pass to `apply()`
* [ProxyConfig](/python/api/proxy-config) - Configure the network proxy
* [SnapshotManager](/python/api/snapshot-manager) - Filesystem snapshots and rollback
* [SupportInfo](/python/api/support-info) - Returned by `support_info()`
* [QueryContext](/python/api/query-context) - Check permissions without applying
