From fa6ceab58c802be3cd0f94e964579adc6b7d0e08 Mon Sep 17 00:00:00 2001 From: Miyoung Choi Date: Mon, 9 Mar 2026 18:23:41 -0700 Subject: [PATCH 1/4] restructure safty and policy section --- docs/_ext/policy_table.py | 161 ++++++++++++++++++++ docs/about/overview.md | 2 +- docs/conf.py | 2 + docs/get-started/run-claude.md | 4 +- docs/get-started/run-opencode.md | 2 +- docs/index.md | 2 +- docs/safety-and-privacy/default-policies.md | 37 +++++ docs/safety-and-privacy/index.md | 27 +++- docs/safety-and-privacy/policies.md | 78 ++++------ docs/safety-and-privacy/security-model.md | 86 ----------- docs/sandboxes/providers.md | 2 +- 11 files changed, 259 insertions(+), 144 deletions(-) create mode 100644 docs/_ext/policy_table.py create mode 100644 docs/safety-and-privacy/default-policies.md delete mode 100644 docs/safety-and-privacy/security-model.md diff --git a/docs/_ext/policy_table.py b/docs/_ext/policy_table.py new file mode 100644 index 00000000..76ccaf28 --- /dev/null +++ b/docs/_ext/policy_table.py @@ -0,0 +1,161 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +"""Sphinx extension that generates tables from a sandbox policy YAML file. + +Usage in MyST markdown:: + + ```{policy-table} deploy/docker/sandbox/dev-sandbox-policy.yaml + ``` + +The directive reads the YAML relative to the repo root and emits: + 1. A "Filesystem, Landlock, and Process" table. + 2. One subsection per ``network_policies`` block with endpoint and binary tables. +""" + +from __future__ import annotations + +from pathlib import Path +from typing import Any + +import yaml +from docutils import nodes +from docutils.statemachine import StringList +from sphinx.application import Sphinx +from sphinx.util.docutils import SphinxDirective + + +def _tls_display(ep: dict[str, Any]) -> str: + tls = ep.get("tls") + return tls if tls else "\u2014" + + +def _access_display(ep: dict[str, Any]) -> str: + if "rules" in ep: + rules = ep["rules"] + parts = [] + for r in rules: + allow = r.get("allow", {}) + parts.append(f"``{allow.get('method', '*')} {allow.get('path', '/**')}``") + return ", ".join(parts) + access = ep.get("access") + if access: + return access + return "L4 passthrough" + + +def _binaries_line(binaries: list[dict[str, str]]) -> str: + paths = [f"``{b['path']}``" for b in binaries] + return ", ".join(paths) + + +def _block_subtitle(key: str, name: str) -> str: + label_map = { + "claude_code": "Anthropic API and Telemetry", + "github_ssh_over_https": "Git Clone and Fetch", + "nvidia_inference": "NVIDIA API Catalog", + "github_rest_api": "GitHub API (Read-Only)", + "pypi": "Python Package Installation", + "vscode": "VS Code Remote and Marketplace", + "gitlab": "GitLab", + } + subtitle = label_map.get(key, name) + return f"{key} \u2014 {subtitle}" + + +class PolicyTableDirective(SphinxDirective): + """Render sandbox policy YAML as tables.""" + + required_arguments = 1 + has_content = False + + def run(self) -> list[nodes.Node]: + repo_root = Path(self.env.srcdir).parent + yaml_path = repo_root / self.arguments[0] + + self.env.note_dependency(str(yaml_path)) + + if not yaml_path.exists(): + msg = self.state_machine.reporter.warning( + f"Policy YAML not found: {yaml_path}", + line=self.lineno, + ) + return [msg] + + policy = yaml.safe_load(yaml_path.read_text()) + + lines: list[str] = [] + + fs = policy.get("filesystem_policy", {}) + landlock = policy.get("landlock", {}) + proc = policy.get("process", {}) + + lines.append("### Filesystem, Landlock, and Process") + lines.append("") + lines.append("| Section | Setting | Value |") + lines.append("|---|---|---|") + + ro = fs.get("read_only", []) + rw = fs.get("read_write", []) + workdir = fs.get("include_workdir", False) + lines.append( + f"| **Filesystem** | Read-only | {', '.join(f'``{p}``' for p in ro)} |" + ) + lines.append(f"| | Read-write | {', '.join(f'``{p}``' for p in rw)} |") + lines.append(f"| | Workdir included | {'Yes' if workdir else 'No'} |") + + compat = landlock.get("compatibility", "best_effort") + lines.append( + f"| **Landlock** | Compatibility | ``{compat}`` " + f"(uses the highest ABI the host kernel supports) |" + ) + + user = proc.get("run_as_user", "") + group = proc.get("run_as_group", "") + lines.append(f"| **Process** | User / Group | ``{user}`` / ``{group}`` |") + lines.append("") + + net = policy.get("network_policies", {}) + if net: + lines.append("### Network Policy Blocks") + lines.append("") + + for key, block in net.items(): + name = block.get("name", key) + endpoints = block.get("endpoints", []) + binaries = block.get("binaries", []) + + lines.append(f"**{_block_subtitle(key, name)}**") + lines.append("") + + has_rules = any("rules" in ep for ep in endpoints) + if has_rules: + lines.append("| Endpoint | Port | TLS | Rules |") + else: + lines.append("| Endpoint | Port | TLS | Access |") + lines.append("|---|---|---|---|") + + for ep in endpoints: + host = ep.get("host", "") + port = ep.get("port", "") + tls = _tls_display(ep) + access = _access_display(ep) + lines.append(f"| ``{host}`` | {port} | {tls} | {access} |") + + lines.append("") + lines.append(f"**Binaries:** {_binaries_line(binaries)}") + lines.append("") + + rst = StringList(lines, source=str(yaml_path)) + container = nodes.container() + self.state.nested_parse(rst, self.content_offset, container) + return container.children + + +def setup(app: Sphinx) -> dict[str, Any]: + app.add_directive("policy-table", PolicyTableDirective) + return { + "version": "0.1", + "parallel_read_safe": True, + "parallel_write_safe": True, + } diff --git a/docs/about/overview.md b/docs/about/overview.md index 30040d94..90f8f124 100644 --- a/docs/about/overview.md +++ b/docs/about/overview.md @@ -77,4 +77,4 @@ Use pre-built sandbox images from the [NemoClaw Community](https://github.com/NV - [Architecture Overview](architecture.md): Understand the components that make up the OpenShell runtime. - [Get Started](../index.md): Install the CLI and create your first sandbox. -- [Security Model](../safety-and-privacy/security-model.md): Learn how OpenShell enforces isolation across all protection layers. +- [Safety and Privacy](../safety-and-privacy/index.md): Learn how OpenShell enforces isolation across all protection layers. diff --git a/docs/conf.py b/docs/conf.py index 9ade62e9..ba369693 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -6,6 +6,7 @@ from pathlib import Path sys.path.insert(0, str(Path(__file__).parent.parent)) +sys.path.insert(0, str(Path(__file__).parent / "_ext")) project = "NVIDIA OpenShell Developer Guide" this_year = date.today().year @@ -23,6 +24,7 @@ "sphinx_copybutton", "sphinx_design", "sphinxcontrib.mermaid", + "policy_table", ] autodoc_default_options = { diff --git a/docs/get-started/run-claude.md b/docs/get-started/run-claude.md index 3abd231d..6d217d8a 100644 --- a/docs/get-started/run-claude.md +++ b/docs/get-started/run-claude.md @@ -115,5 +115,5 @@ This is useful when you plan to reconnect later or iterate on the policy while t - {doc}`../sandboxes/create-and-manage`: Learn the isolation model and sandbox lifecycle. - {doc}`../sandboxes/providers`: Understand how credentials are injected without exposing them to agent code. -- {doc}`../safety-and-privacy/policies`: Customize the default policy or write your own. -- {doc}`../safety-and-privacy/policies`: Explore network policies and per-endpoint rules. +- {doc}`../safety-and-privacy/default-policies`: What the built-in default policy allows and denies. +- {doc}`../safety-and-privacy/policies`: Write custom policies and configure network rules. diff --git a/docs/get-started/run-opencode.md b/docs/get-started/run-opencode.md index e088ee4a..b31c30a2 100644 --- a/docs/get-started/run-opencode.md +++ b/docs/get-started/run-opencode.md @@ -255,4 +255,4 @@ $ nemoclaw sandbox delete opencode-sandbox - {doc}`../safety-and-privacy/policies`: How the proxy evaluates network rules and policy enforcement. - {doc}`../inference/index`: Inference route configuration, protocol detection, and transparent rerouting. - {doc}`../sandboxes/providers`: Provider types, credential discovery, and manual and automatic creation. -- {doc}`../safety-and-privacy/security-model`: The four protection layers and how they interact. +- {doc}`../safety-and-privacy/index`: The four protection layers and how they interact. diff --git a/docs/index.md b/docs/index.md index bb094a90..5ded2565 100644 --- a/docs/index.md +++ b/docs/index.md @@ -212,7 +212,7 @@ sandboxes/community-sandboxes :hidden: safety-and-privacy/index -safety-and-privacy/security-model +safety-and-privacy/default-policies safety-and-privacy/policies ``` diff --git a/docs/safety-and-privacy/default-policies.md b/docs/safety-and-privacy/default-policies.md new file mode 100644 index 00000000..3247ff62 --- /dev/null +++ b/docs/safety-and-privacy/default-policies.md @@ -0,0 +1,37 @@ + + +# Built-in Default Policy + +NVIDIA OpenShell ships a built-in policy that covers common agent workflows out of the box. When you create a sandbox without `--policy`, this policy is applied automatically. + +## Agent Compatibility + +| Agent | Coverage | Action Required | +|---|---|---| +| Claude Code | Full | None. Works out of the box. | +| OpenCode | Partial | Add `opencode.ai` endpoint and OpenCode binary paths. See [Run OpenCode with NVIDIA Inference](../get-started/run-opencode.md). | +| Codex | None | Provide a complete custom policy with OpenAI endpoints and Codex binary paths. | + +:::{important} +If you run a non-Claude agent without a custom policy, the agent's API calls are denied by the proxy. You must provide a policy that declares the agent's endpoints and binaries. +::: + +## What the Default Policy Allows + +The default policy defines six network policy blocks, filesystem isolation, Landlock enforcement, and process identity. To view the default policy, check the [`deploy/docker/sandbox/dev-sandbox-policy.yaml`](https://github.com/NVIDIA/NemoClaw/blob/main/deploy/docker/sandbox/dev-sandbox-policy.yaml) file. + + + +## Next Steps + +- {doc}`policies`: Write custom policies, configure network rules, and iterate on a running sandbox. +- [Policy Schema Reference](../reference/policy-schema.md): Complete field reference for the policy YAML. diff --git a/docs/safety-and-privacy/index.md b/docs/safety-and-privacy/index.md index 68cc20e2..f647b49e 100644 --- a/docs/safety-and-privacy/index.md +++ b/docs/safety-and-privacy/index.md @@ -63,9 +63,28 @@ Filesystem and process restrictions are locked at creation time. Network and inference rules are hot-reloadable on a running sandbox, so you can iterate on access rules without recreating the sandbox. +## Threat Scenarios + +When an AI agent runs with unrestricted access, it can read any file, reach any +network host, call any API with your credentials, and install arbitrary software. +The table below shows how the four layers address concrete threats. + +| Threat | Without Protection | With OpenShell | +|---|---|---| +| **Data exfiltration** | Agent uploads source code to an external server via `curl`. | Network policy blocks all outbound connections except explicitly approved hosts. | +| **Credential theft** | Agent reads `~/.ssh/id_rsa` or `~/.aws/credentials` and exfiltrates them. | [Landlock](https://docs.kernel.org/security/landlock.html) limits the agent to declared paths (`/sandbox`, `/tmp`, read-only system dirs). | +| **Unauthorized API calls** | Agent calls `api.openai.com` with your key, sending data to a third-party provider. | Privacy router intercepts and reroutes calls to a backend you control. | +| **Privilege escalation** | Agent runs `sudo apt install`, modifies `/etc/passwd`, or scans the internal network. | Agent runs as an unprivileged user with seccomp filters; no `sudo`, no `setuid`. | + +:::{important} +All four layers work together. No single layer is sufficient on its own. +Filesystem restrictions do not prevent network exfiltration. Network policies do +not prevent local privilege escalation. Process restrictions do not control +where inference traffic goes. Defense in depth means every layer covers gaps +that the others cannot. +::: + ## Next Steps -- {doc}`security-model`: Threat scenarios and how each protection layer - addresses them. -- {doc}`policies`: Policy structure, evaluation order, and how to iterate on - rules. +- {doc}`default-policies`: The built-in policy that ships with OpenShell and what each block allows. +- {doc}`policies`: Write custom policies, configure network rules, and iterate on a running sandbox. diff --git a/docs/safety-and-privacy/policies.md b/docs/safety-and-privacy/policies.md index d98f7b79..e491b2f1 100644 --- a/docs/safety-and-privacy/policies.md +++ b/docs/safety-and-privacy/policies.md @@ -3,21 +3,13 @@ SPDX-License-Identifier: Apache-2.0 --> -# Write Sandbox Policies +# Policy Configuration -This guide covers how to author, iterate, and manage sandbox policies that control what an agent can do inside a OpenShell sandbox. You will learn to create sandboxes with custom policies, monitor denied traffic to discover missing rules, and push policy updates without restarting the sandbox. +A policy is a single YAML document that controls what a sandbox can do: filesystem access, process identity, network access, and inference routing. You attach it when creating a sandbox; the network and inference sections can be updated on a running sandbox without restarting. -## What is a policy +## Policy Structure -A policy is a single YAML document that controls what a sandbox can do: filesystem access, process identity, network access, and inference routing. You attach it when creating a sandbox; the network and inference parts can be updated on a running sandbox without restarting. OpenShell's four protection layers (filesystem, network, process, inference) are all configured through this one policy. - -## How is it evaluated - -For **network** traffic, the proxy matches destination (host and port) and calling binary to a policy block and optionally applies per-endpoint rules; see [Network access rules](#network-access-rules) below. For filesystem and process, the policy is applied at sandbox start (and for static fields, at creation only). For the full endpoint schema and binary matching, see the [Policy Schema Reference](../reference/policy-schema.md). - -## How is it structured - -A policy is a YAML document with five top-level sections: `version`, `filesystem_policy`, `landlock`, `process`, `network_policies`, and `inference`. Static fields (`filesystem_policy`, `landlock`, `process`) are locked at sandbox creation and require recreation to change. Dynamic fields (`network_policies`, `inference`) are hot-reloadable on a running sandbox. The `landlock` section configures [Landlock LSM](https://docs.kernel.org/security/landlock.html) enforcement at the kernel level. +A policy has five top-level sections: `version`, `filesystem_policy`, `landlock`, `process`, `network_policies`, and `inference`. Static sections (`filesystem_policy`, `landlock`, `process`) are locked at sandbox creation and require recreation to change. Dynamic sections (`network_policies`, `inference`) are hot-reloadable on a running sandbox. The `landlock` section configures [Landlock LSM](https://docs.kernel.org/security/landlock.html) enforcement at the kernel level. ```yaml version: 1 @@ -55,21 +47,24 @@ inference: allowed_routes: [local] ``` -For the complete structure and every field, see the [Policy Schema Reference](../reference/policy-schema.md). +For the complete field reference, see the [Policy Schema Reference](../reference/policy-schema.md). -## Network access rules +## Network Policy Evaluation -Network access is controlled by policy blocks under `network_policies`. Each block has a **name**, a list of **endpoints** (host, port, protocol, and optional rules), and a list of **binaries** that are allowed to use those endpoints. The example below shows a full policy block. +Every outbound connection from the sandbox goes through the proxy. The proxy matches the destination such as host and port and the calling binary to an endpoint in one of the `network_policies` blocks. -### How network access is evaluated +- If an endpoint matches the destination and the binary is listed in that block's `binaries`, the connection is allowed. +- For endpoints with `protocol: rest` and `tls: terminate`, each HTTP request is also checked against that endpoint's `rules` (method and path). +- If no endpoint matches and inference routes are configured, the request may be rerouted for inference. +- Otherwise the connection is denied. -Every outbound connection from the sandbox goes through the proxy. The proxy matches the **destination** (host and port) and the **calling binary** to an endpoint in one of your policy blocks. If an endpoint matches the destination and the binary is listed in that block's `binaries`, the connection is **allowed**. For endpoints with `protocol: rest` and `tls: terminate`, each HTTP request is also checked against that endpoint's `rules` (method and path). If no endpoint matches and inference routes are configured, the request may be **rerouted for inference**. Otherwise the connection is **denied**. Endpoints without `protocol` or `tls` (L4-only) allow the TCP stream through without inspecting payloads. For the full endpoint schema, access presets, and binary matching, see the [Policy Schema Reference](../reference/policy-schema.md). +Endpoints without `protocol` or `tls` (L4-only) allow the TCP stream through without inspecting payloads. For the full endpoint schema, access presets, and binary matching, see the [Policy Schema Reference](../reference/policy-schema.md). -### Enable GitHub push +## Example Policy for GitHub Repository Access -The following policy block allows the listed binaries (Claude and the GitHub CLI) to reach `api.github.com` with the given rules: read-only (GET, HEAD, OPTIONS) and GraphQL (POST) for all paths; full write access for `alpha-repo`; and create/edit issues only for `bravo-repo`. Replace `` with your GitHub org or username. +The following policy block allows Claude and the GitHub CLI to reach `api.github.com` with granular per-endpoint rules: read-only (GET, HEAD, OPTIONS) and GraphQL (POST) for all paths; full write access for `alpha-repo`; and create/edit issues only for `bravo-repo`. Replace `` with your GitHub org or username. -Add this to your existing sandbox policy if you want to apply this GitHub permission set. +Add this block to the `network_policies` section of your sandbox policy. ```yaml github_repos: @@ -111,27 +106,13 @@ Add this to your existing sandbox policy if you want to apply this GitHub permis - { path: /usr/bin/gh } ``` -Then run `openshell policy set --policy --wait` to apply. - -### If something is blocked - -Check `openshell logs --tail --source sandbox` for the denied host, path, and binary. Add or adjust the matching endpoint or rules in the relevant policy block (e.g. add a new `allow` rule for the method and path, or add the binary to that block's `binaries` list). See [How do I edit it](#how-do-i-edit-it) for the full iteration workflow. - -## Default Policy - -OpenShell ships a built-in default policy designed for Claude Code. It covers Claude's API endpoints, telemetry hosts, GitHub access, and VS Code marketplace traffic out of the box. +Apply with `openshell policy set --policy --wait`. -| Agent | Default policy coverage | What you need to do | -|---|---|---| -| Claude Code | Full | Nothing: works out of the box | -| OpenCode | Partial | Add `opencode.ai` endpoint and OpenCode binary paths. | -| Codex | None | Provide a complete custom policy with OpenAI endpoints and Codex binary paths. | +## Debug Denied Requests -:::{important} -If you run a non-Claude agent without a custom policy, the agent's API calls will be denied by the proxy. You must provide a policy that declares the agent's endpoints and binaries. -::: +Check `openshell logs --tail --source sandbox` for the denied host, path, and binary. Add or adjust the matching endpoint or rules in the relevant policy block (for example, add a new `allow` rule for the method and path, or add the binary to that block's `binaries` list). See [Hot-Reload Policy Updates](#hot-reload-policy-updates) for the full iteration workflow. -## Create a Sandbox with a Custom Policy +## Apply a Custom Policy Pass a policy YAML file when creating the sandbox: @@ -150,9 +131,9 @@ $ openshell sandbox create --keep -- claude The CLI uses the policy from `OPENSHELL_SANDBOX_POLICY` whenever `--policy` is not explicitly provided. -## How do I edit it +## Hot-Reload Policy Updates -To change what the sandbox can access, you pull the current policy, edit the YAML, and push the update. The workflow is iterative: create the sandbox, monitor logs for denied actions, pull the policy, modify it, push, and verify. +To change what the sandbox can access, pull the current policy, edit the YAML, and push the update. The workflow is iterative: create the sandbox, monitor logs for denied actions, pull the policy, modify it, push, and verify. ```{mermaid} flowchart TD @@ -173,35 +154,35 @@ flowchart TD linkStyle default stroke:#76b900,stroke-width:2px ``` -**Steps** +The following steps outline the hot-reload policy update workflow. -1. **Create** the sandbox with your initial policy (or set `OPENSHELL_SANDBOX_POLICY`). +1. Create the sandbox with your initial policy (or set `OPENSHELL_SANDBOX_POLICY`). ```console $ openshell sandbox create --policy ./my-policy.yaml --keep -- claude ``` -2. **Monitor** denials — each log entry shows host, port, binary, and reason. Alternatively use `openshell term` for a live dashboard. +2. Monitor denials — each log entry shows host, port, binary, and reason. Alternatively use `openshell term` for a live dashboard. ```console $ openshell logs --tail --source sandbox ``` -3. **Pull** the current policy. Strip the metadata header (Version, Hash, Status) before reusing the file. +3. Pull the current policy. Strip the metadata header (Version, Hash, Status) before reusing the file. ```console $ openshell policy get --full > current-policy.yaml ``` -4. **Edit** the YAML: add or adjust `network_policies` entries, binaries, `access` or `rules`, or `inference.allowed_routes`. +4. Edit the YAML: add or adjust `network_policies` entries, binaries, `access` or `rules`, or `inference.allowed_routes`. -5. **Push** the updated policy. Exit codes: 0 = loaded, 1 = validation failed, 124 = timeout. +5. Push the updated policy. Exit codes: 0 = loaded, 1 = validation failed, 124 = timeout. ```console $ openshell policy set --policy current-policy.yaml --wait ``` -6. **Verify** the new revision. If status is `loaded`, repeat from step 2 as needed; if `failed`, fix the policy and repeat from step 4. +6. Verify the new revision. If status is `loaded`, repeat from step 2 as needed; if `failed`, fix the policy and repeat from step 4. ```console $ openshell policy list @@ -209,5 +190,6 @@ flowchart TD ## Next Steps +- {doc}`default-policies`: The built-in policy that ships with OpenShell and what each block allows. - [Policy Schema Reference](../reference/policy-schema.md): Complete field reference for the policy YAML. -- [Security Model](security-model.md): Threat scenarios and protection layers. +- [Safety and Privacy](index.md): Threat scenarios and protection layers. diff --git a/docs/safety-and-privacy/security-model.md b/docs/safety-and-privacy/security-model.md deleted file mode 100644 index 8108b564..00000000 --- a/docs/safety-and-privacy/security-model.md +++ /dev/null @@ -1,86 +0,0 @@ - - -# The Security Model - -When an AI agent runs with unrestricted access to your system, it can read any -file, reach any network host, call any API with your credentials, and install -arbitrary software. OpenShell's security model exists to prevent all of that. - -:::{note} -OpenShell uses defense in depth. Four independent protection layers: filesystem, -network, process, and inference. They work together so that no single point of -failure can compromise your environment. -::: - -## What Happens Without Protection - -Autonomous agents are powerful, but power without boundaries is risk. Here are -four concrete threat scenarios and how OpenShell addresses each one. - -### Data Exfiltration - -**Without protection:** -The agent writes a script that reads your source code and uploads it to an -external server using `curl`. - -**With OpenShell:** -The network policy blocks all outbound connections except to hosts you have -explicitly approved. The `curl` command to an unapproved destination is denied -at the proxy before the request ever leaves the sandbox. - ---- - -### Credential Theft - -**Without protection:** -The agent reads `~/.ssh/id_rsa`, `~/.aws/credentials`, or other sensitive files -from your home directory and exfiltrates them. - -**With OpenShell:** -[Landlock](https://docs.kernel.org/security/landlock.html) filesystem restrictions limit the agent to declared paths. The agent -can access `/sandbox`, `/tmp`, and read-only system directories, but not your -home directory, SSH keys, cloud credentials, or anything else outside the -policy. - ---- - -### Unauthorized API Calls - -**Without protection:** -The agent code calls `api.openai.com` with your API key, sending proprietary -data to a third-party inference provider you did not approve. - -**With OpenShell:** -The privacy router intercepts outbound API calls and reroutes them to a -backend you control: a local model, an NVIDIA endpoint, or your own -deployment. The agent's code does not need to change; the rerouting is -transparent. Your data never reaches an unauthorized provider. - ---- - -### Privilege Escalation - -**Without protection:** -The agent runs `sudo apt install` to install packages, modifies `/etc/passwd`, -or uses raw sockets to scan your internal network. - -**With OpenShell:** -The agent runs as an unprivileged user with seccomp filters that block -dangerous system calls. [Landlock](https://docs.kernel.org/security/landlock.html) prevents writes outside allowed paths. There -is no `sudo`, no `setuid`, and no path to elevated privileges. - -:::{important} -All four layers work together. No single layer is sufficient on its own. -Filesystem restrictions do not prevent network exfiltration. Network policies do -not prevent local privilege escalation. Process restrictions do not control -where inference traffic goes. Defense in depth means every layer covers gaps -that the others cannot. -::: - -## Next Steps - -- {doc}`policies`: Write and iterate on the policy YAML that configures all four layers (including network rules, binary matching, and TLS inspection). -- {doc}`../inference/index`: Set up private inference backends diff --git a/docs/sandboxes/providers.md b/docs/sandboxes/providers.md index 4427ae5b..f1c3b508 100644 --- a/docs/sandboxes/providers.md +++ b/docs/sandboxes/providers.md @@ -125,4 +125,4 @@ environment variable names and values yourself with `--credential`. ## Next Steps - {doc}`create-and-manage`: Full sandbox lifecycle management -- {doc}`../safety-and-privacy/security-model`: Why credential isolation matters \ No newline at end of file +- {doc}`../safety-and-privacy/index`: Why credential isolation matters \ No newline at end of file From bbbbbad8ab83e0eddf67008afe2826d2f32aadcb Mon Sep 17 00:00:00 2001 From: Miyoung Choi Date: Mon, 9 Mar 2026 19:01:11 -0700 Subject: [PATCH 2/4] put the table autogeneration back --- docs/safety-and-privacy/default-policies.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/safety-and-privacy/default-policies.md b/docs/safety-and-privacy/default-policies.md index 3247ff62..bf697fab 100644 --- a/docs/safety-and-privacy/default-policies.md +++ b/docs/safety-and-privacy/default-policies.md @@ -21,11 +21,9 @@ If you run a non-Claude agent without a custom policy, the agent's API calls are ## What the Default Policy Allows -The default policy defines six network policy blocks, filesystem isolation, Landlock enforcement, and process identity. To view the default policy, check the [`deploy/docker/sandbox/dev-sandbox-policy.yaml`](https://github.com/NVIDIA/NemoClaw/blob/main/deploy/docker/sandbox/dev-sandbox-policy.yaml) file. +The default policy defines six network policy blocks, filesystem isolation, Landlock enforcement, and process identity. - +The default policy defines six network policy blocks, plus filesystem isolation, Landlock enforcement, and process identity. For the full breakdown of each block, see {doc}`../reference/default-policy`. ## Next Steps From e85527a54244fdf4e2f2604f3d1df46060969699 Mon Sep 17 00:00:00 2001 From: Miyoung Choi Date: Mon, 9 Mar 2026 19:40:55 -0700 Subject: [PATCH 4/4] typo --- docs/safety-and-privacy/default-policies.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/safety-and-privacy/default-policies.md b/docs/safety-and-privacy/default-policies.md index a1d5799b..f32ef919 100644 --- a/docs/safety-and-privacy/default-policies.md +++ b/docs/safety-and-privacy/default-policies.md @@ -6,7 +6,7 @@ # Built-in Default Policy NVIDIA OpenShell ships a built-in policy that covers common agent workflows out of the box. -When you create a sandbox without `--policy`, OpenShell applies a built-in default policy. This policy controls three things: +When you create a sandbox without `--policy`, OpenShell applies the default policy. This policy controls three things: - What the agent can access on disk. Filesystem paths are split into read-only and read-write sets. [Landlock LSM](https://docs.kernel.org/security/landlock.html) enforces these restrictions at the kernel level. - What the agent can reach on the network. Each network policy block pairs a set of allowed destinations (host and port) with a set of allowed binaries (executable paths inside the sandbox). The proxy resolves every outbound connection to the binary that opened it. A connection is allowed only when both the destination and the calling binary match an entry in the same block. Everything else is denied.