diff --git a/docs/about/architecture.md b/docs/about/architecture.md new file mode 100644 index 00000000..706124d7 --- /dev/null +++ b/docs/about/architecture.md @@ -0,0 +1,81 @@ +--- +title: + page: "Architecture Overview" + nav: "Architecture" +description: "High-level overview of the OpenShell architecture: gateway, sandboxes, policy engine, and privacy router." +keywords: ["nemoclaw architecture", "sandbox architecture", "agent isolation", "k3s", "policy engine"] +topics: ["generative_ai", "cybersecurity"] +tags: ["ai_agents", "sandboxing", "security", "architecture"] +content: + type: concept + difficulty: technical_beginner + audience: [engineer, data_scientist] +--- + + + +# How OpenShell Works + +NemoClaw runs as a [k3s](https://k3s.io/) Kubernetes cluster inside a Docker container. +Each sandbox is an isolated Kubernetes pod managed by the OpenShell control plane. +Four components work together to keep agents secure. + +```{mermaid} +flowchart LR + CLI["CLI"] -->|gRPC| GW["Gateway"] + GW --> SBX["Sandbox"] + + subgraph SBX["Sandbox"] + direction TB + AGENT["Agent Process"] -->|All traffic| PROXY["Network Proxy"] + PROXY -->|Evaluate| OPA["Policy Engine"] + end + + PROXY -->|Allowed traffic| EXT["External Services"] + + style CLI fill:#ffffff,stroke:#000000,color:#000000 + style GW fill:#76b900,stroke:#000000,color:#000000 + style SBX fill:#f5f5f5,stroke:#000000,color:#000000 + style AGENT fill:#ffffff,stroke:#000000,color:#000000 + style PROXY fill:#76b900,stroke:#000000,color:#000000 + style OPA fill:#76b900,stroke:#000000,color:#000000 + style EXT fill:#ffffff,stroke:#000000,color:#000000 + + linkStyle default stroke:#76b900,stroke-width:2px +``` + +## Components + +NemoClaw consists of the following components. + +Gateway +: The control-plane API that manages sandbox lifecycle, stores encrypted credentials, distributes policies, and terminates SSH tunnels. The CLI communicates exclusively with the gatewayβ€”it never talks to sandbox pods directly. + +Sandbox +: An isolated pod that runs your agent. Each sandbox contains a **supervisor** (sets up isolation and starts the agent), an **L7 proxy** (intercepts and evaluates every outbound connection), and the agent process itself. + +Policy Engine +: Evaluates declarative YAML policies that define filesystem, network, and process constraints. The proxy queries the engine on every outbound connection. Policies can be hot-reloaded without restarting the agent. + +Privacy Router +: Intercepts LLM API calls and routes them to local or self-hosted backends based on your routing policy. Sensitive prompts and completions stay on infrastructure you control. + +## How a Request Flows + +NemoClaw works in the following way: + +1. The agent makes an outbound connection (for example, an API call). +2. The L7 proxy intercepts the connection and identifies the calling process. +3. The proxy queries the policy engine with the destination and process identity. +4. Based on the policy decision, the proxy either allows the connection, routes it through the privacy router for inference, or denies it. + +## Remote Deployment + +NemoClaw can also run on a remote host. Deploy with `nemoclaw gateway start --remote user@host`, then set up a tunnel with `nemoclaw gateway tunnel`. The architecture is identicalβ€”only the Docker container location changes. + +--- + +For detailed component internals, refer to the [Architecture Reference](../reference/architecture.md). diff --git a/docs/about/index.md b/docs/about/index.md deleted file mode 100644 index 73a57815..00000000 --- a/docs/about/index.md +++ /dev/null @@ -1,6 +0,0 @@ - - -# About NemoClaw diff --git a/docs/about/overview.md b/docs/about/overview.md new file mode 100644 index 00000000..30040d94 --- /dev/null +++ b/docs/about/overview.md @@ -0,0 +1,80 @@ +--- +title: + page: "Overview of NVIDIA OpenShell" + nav: "Overview" +description: "NemoClaw is the safe, private runtime for autonomous AI agents. Run agents in sandboxed environments that protect your data, credentials, and infrastructure." +keywords: ["nemoclaw", "ai agent sandbox", "agent security", "agent isolation", "inference routing"] +topics: ["generative_ai", "cybersecurity"] +tags: ["ai_agents", "sandboxing", "security", "privacy", "inference_routing"] +content: + type: concept + difficulty: technical_beginner + audience: [engineer, data_scientist] +--- + + + +# Overview of NVIDIA OpenShell + +NVIDIA OpenShell is an open-source runtime that executes autonomous AI agents inside sandboxed environments with kernel-level isolation. It prevents agents from accessing unauthorized files, exfiltrating data, leaking credentials, or making uncontrolled network requests. A single declarative YAML policy governs filesystem, network, process, and inference protections across all sandboxes and is hot-reloadable without restarting running agents. + +## Common Challenges with AI Agents + +AI agents are most useful when they have broad access to reading files, installing packages, calling APIs, and using credentials. However, this same access makes them dangerous. An unrestricted agent can leak your API keys, send proprietary code to unauthorized endpoints, or reach infrastructure it was never meant to touch. + +Conventional containers do not solve this. They isolate processes from the host, but they do not control what an agent does *inside* the container β€” which files it reads, which hosts it contacts, or where it sends your prompts. + +## Benefits of Using OpenShell + +NemoClaw addresses these risks through defense-in-depth enforcement across four policy domains: filesystem, network, process, and inference. + +:::{dropdown} πŸ›‘οΈ Kernel-Level Isolation +NemoClaw enforces isolation at the Linux kernel level using Landlock for filesystem restrictions, seccomp for system call filtering, and network namespaces for traffic control. These mechanisms operate below the application layer, so agents cannot bypass them regardless of the tools or languages they use. +::: + +:::{dropdown} πŸ“œ Declarative Policy Enforcement +A single YAML policy file defines all security boundaries for a sandbox: allowed filesystem paths, permitted network destinations, restricted processes, and inference routing rules. Policies are hot-reloadable, so you can tighten or relax rules on a running sandbox without restarting the agent. +::: + +:::{dropdown} πŸ” Credential Containment +Credentials are injected into sandboxes as environment variables at startup and are scoped to the sandbox's isolated namespace. They cannot be read by processes outside the sandbox, and network policies prevent agents from transmitting them to unauthorized endpoints. +::: + +:::{dropdown} πŸ”€ Private Inference Routing +The built-in inference router intercepts LLM API calls and redirects them to local or self-hosted backends based on your routing policy. Sensitive prompts and completions stay on infrastructure you control. Routes are configurable per sandbox and can be updated without restarting agents. +::: + +:::{dropdown} πŸ” Full L7 Traffic Inspection +Every outbound TCP connection from a sandbox passes through an L7 proxy that resolves the calling process, evaluates the destination against the active policy, and either allows, denies, or reroutes the request. For REST endpoints, the proxy decrypts TLS, inspects HTTP method and path, and applies fine-grained access rules. +::: + +## Use Cases + +The following are common use cases for OpenShell. + +:::{dropdown} πŸ’» Secure Coding Agents +Run AI coding assistants such as Claude Code, OpenCode, or OpenClaw inside a sandbox where they can read and modify project files but cannot access SSH keys, cloud credentials, or files outside the project directory. Network policies restrict which package registries and APIs the agent can reach. +::: + +:::{dropdown} 🏒 Private Enterprise Development +Route all LLM inference through self-hosted NVIDIA NIM endpoints or private API backends. Proprietary source code and internal documentation stay on your infrastructure and are never sent to third-party LLM providers. +::: + +:::{dropdown} βœ… Compliance and Audit +Declarative policies serve as auditable security controls. Each sandbox runs under a well-defined policy that specifies exactly what the agent can access. Policy files can be version-controlled and reviewed as part of your security and compliance processes. +::: + +:::{dropdown} πŸ“¦ Community and Custom Sandbox Images +Use pre-built sandbox images from the [NemoClaw Community](https://github.com/NVIDIA/NemoClaw-Community) catalog or bring your own container. Community sandboxes bundle domain-specific tools, policies, and skills, while custom containers let you package any environment your agents need. +::: + +--- + +## Next Steps + +- [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. diff --git a/docs/about/release-notes.md b/docs/about/release-notes.md new file mode 100644 index 00000000..f85907c4 --- /dev/null +++ b/docs/about/release-notes.md @@ -0,0 +1,13 @@ + + +# Release Notes + +This page covers the highlights of each OpenShell release. +For more details, refer to the [NemoClaw GitHub Releases](https://github.com/NVIDIA/NemoClaw/releases). + +## 0.1.0 + +This is the first release of NVIDIA OpenShell. It introduces sandboxed AI agent execution with kernel-level isolation, policy enforcement, and credential management. diff --git a/docs/conf.py b/docs/conf.py index f1c2ff2f..9ade62e9 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -7,7 +7,7 @@ sys.path.insert(0, str(Path(__file__).parent.parent)) -project = "NVIDIA NemoClaw Developer Guide" +project = "NVIDIA OpenShell Developer Guide" this_year = date.today().year copyright = f"2025-{this_year}, NVIDIA Corporation" author = "NVIDIA Corporation" @@ -38,6 +38,7 @@ exclude_patterns = [ "README.md", + "SETUP.md", "_build/**", ] @@ -63,6 +64,23 @@ html_show_sourcelink = False html_show_sphinx = False +mermaid_init_js = ( + "mermaid.initialize({" + " startOnLoad: true," + " theme: 'base'," + " themeVariables: {" + " background: '#ffffff'," + " primaryColor: '#76b900'," + " primaryTextColor: '#000000'," + " primaryBorderColor: '#000000'," + " lineColor: '#000000'," + " textColor: '#000000'," + " mainBkg: '#ffffff'," + " nodeBorder: '#000000'" + " }" + "});" +) + html_domain_indices = False html_use_index = False highlight_language = "console" @@ -75,5 +93,11 @@ "icon": "fa-brands fa-github", "type": "fontawesome", }, + { + "name": "PyPI", + "url": "https://pypi.org/project/nemoclaw/", + "icon": "fa-brands fa-python", + "type": "fontawesome", + }, ], } diff --git a/docs/feature1/index.md b/docs/feature1/index.md deleted file mode 100644 index debf3ae8..00000000 --- a/docs/feature1/index.md +++ /dev/null @@ -1,6 +0,0 @@ - - -# Feature 1 diff --git a/docs/feature2/index.md b/docs/feature2/index.md deleted file mode 100644 index 9b14022e..00000000 --- a/docs/feature2/index.md +++ /dev/null @@ -1,6 +0,0 @@ - - -# Feature 2 diff --git a/docs/get-started/index.md b/docs/get-started/index.md deleted file mode 100644 index 6452a1e9..00000000 --- a/docs/get-started/index.md +++ /dev/null @@ -1,6 +0,0 @@ - - -# Get Started diff --git a/docs/get-started/quickstart.md b/docs/get-started/quickstart.md new file mode 100644 index 00000000..ce4c907f --- /dev/null +++ b/docs/get-started/quickstart.md @@ -0,0 +1,93 @@ +--- +title: + page: "Quickstart" + nav: "Quickstart" +description: "Install the OpenShell CLI and create your first sandboxed AI agent in two commands." +keywords: ["nemoclaw install", "quickstart", "sandbox create", "getting started"] +topics: ["generative_ai", "cybersecurity"] +tags: ["ai_agents", "sandboxing", "installation", "quickstart"] +content: + type: get_started + difficulty: technical_beginner + audience: [engineer, data_scientist] +--- + + + +# Quickstart + +This page gets you from zero to a running, policy-enforced sandbox in two commands. + +## Prerequisites + +Before you begin, make sure you have: + +- Python 3.12 or later +- Docker Desktop running on your machine + +## Install the OpenShell CLI + +```bash +pip install nemoclaw +``` + +## Create Your First OpenShell Sandbox + +Choose the tab that matches your agent: + +::::{tab-set} + +:::{tab-item} Claude Code +```console +$ nemoclaw sandbox create -- claude +``` + +```text +βœ“ Runtime ready +βœ“ Discovered Claude credentials (ANTHROPIC_API_KEY) +βœ“ Created sandbox: keen-fox +βœ“ Policy loaded (4 protection layers active) + +Connecting to keen-fox... +``` + +The CLI detects your `ANTHROPIC_API_KEY`, creates a provider, builds the sandbox, applies a default policy, and drops you into an interactive session. No additional configuration is required. +::: + +:::{tab-item} Community Sandbox +```console +$ nemoclaw sandbox create --from openclaw +``` + +The `--from` flag pulls a pre-built sandbox definition from the [NemoClaw Community](https://github.com/NVIDIA/NemoClaw-Community) catalog. Each definition bundles a container image, a tailored policy, and optional skills into a single package. +::: + +:::: + +## What Happens Behind the Scenes + +When you create a sandbox, OpenShell activates the following protection layers. + +| Protection Layer | Description | +|------------------------|-----------------------------------------------------------------------------------------------| +| Filesystem isolation | The agent can only read and write paths that the policy explicitly permits. | +| Network enforcement | Outbound connections are denied by default. The policy allowlists specific hosts, ports, and binaries. | +| Process restrictions | The agent runs as a non-root user inside the container. | +| Inference privacy | LLM API traffic is routed through a privacy-aware proxy. Credentials never leak outside the sandbox. | + +A single YAML policy file controls all four layers. You can hot-reload network and inference rules on a running sandbox without restarting it. + +:::{note} +For OpenCode or Codex, the default policy does not cover the required endpoints. Follow the [Run OpenCode with NVIDIA Inference](run-opencode.md) tutorial for agent-specific setup. +::: + +## Next Steps + +You now have a working sandbox. From here, you can: + +- Follow the [Tutorials](tutorials.md) for step-by-step walkthroughs with Claude Code, OpenClaw, and OpenCode. +- Learn how sandboxes work in [Sandboxes](../sandboxes/create-and-manage.md). +- Write your own policies in [Safety and Privacy](../safety-and-privacy/index.md). diff --git a/docs/get-started/run-claude.md b/docs/get-started/run-claude.md new file mode 100644 index 00000000..d09a2371 --- /dev/null +++ b/docs/get-started/run-claude.md @@ -0,0 +1,119 @@ + + +# Run Claude Code Safely + +This tutorial walks you through the path to running Claude Code inside a OpenShell sandbox. By the end of this tutorial, you will have an isolated environment with credentials securely injected and a default policy controlling what the agent can access. + +## What You Will Learn + +- Create a sandbox with a single command. +- Understand how OpenShell auto-discovers provider credentials. +- Inspect what the default policy allows and denies. +- Connect to a running sandbox and work inside it. + +## Prerequisites + +- Met the prerequisites and installed the OpenShell CLI as described in the {doc}`quickstart` guide. +- `ANTHROPIC_API_KEY` environment variable set on your host machine. + +## Create the Sandbox + +Run the following command: + +```console +$ nemoclaw sandbox create -- claude +``` + +This single command performs four actions: + +1. Bootstraps the runtime. On first use, the CLI provisions a local k3s cluster inside Docker and deploys the OpenShell control plane. This happens once. Subsequent commands reuse the existing cluster. +2. Auto-discovers credentials. The CLI detects that `claude` is a recognized tool and reads the `ANTHROPIC_API_KEY` environment variable from your shell. It creates a provider automatically. +3. Creates the sandbox. The CLI provisions an isolated container and applies the default policy. This policy allows Claude Code to reach `api.anthropic.com` and a small set of supporting endpoints while blocking everything else. +4. Drops you into the sandbox. You land in an interactive SSH session, ready to work. + +:::{note} +The first bootstrap takes a few minutes depending on your network speed. The CLI prints progress as each component starts. Subsequent sandbox creations are much faster. +::: + +## Work Inside the Sandbox + +You are now inside the sandbox. Start Claude Code: + +```console +$ claude +``` + +Your credentials are available as environment variables. Verify this with: + +```console +$ echo $ANTHROPIC_API_KEY +sk-ant-... +``` + +The sandbox provides a working directory at `/sandbox` where you can create and edit files. Standard development tools β€” git, common language runtimes, and package managers β€” are available within the boundaries set by the policy. + +## Check Sandbox Status + +Open a second terminal on your host machine to inspect the sandbox from outside. + +1. In the second terminal, list all sandboxes: + + ```console + $ nemoclaw sandbox list + ``` + +2. Launch the OpenShell Terminal for a live dashboard that shows sandbox status, active network connections, and policy decisions in real time: + + ```console + $ nemoclaw term + ``` + +## Connect from VS Code (Optional) + +If you prefer a graphical editor, connect to the sandbox with VS Code Remote-SSH. + +Export the sandbox SSH configuration: + +```console +$ nemoclaw sandbox ssh-config >> ~/.ssh/config +``` + +Then open VS Code, install the Remote - SSH extension if needed, and connect to the host named after your sandbox. VS Code opens a full editor session inside the isolated environment. + +:::{tip} +Replace `` with your sandbox name. Run `nemoclaw sandbox list` to find it if you did not specify one at creation time. +::: + +## Clean Up + +Exit the sandbox shell: + +```console +$ exit +``` + +Delete the sandbox: + +```console +$ nemoclaw sandbox delete +``` + +:::{tip} +If you want the sandbox to persist after you disconnect, add the `--keep` flag at creation time: + +```console +$ nemoclaw sandbox create --keep -- claude +``` + +This is useful when you plan to reconnect later or iterate on the policy while the sandbox runs. +::: + +## Next Steps + +- {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/network-access-rules`: Explore the network proxy and per-endpoint rules. diff --git a/docs/get-started/run-openclaw.md b/docs/get-started/run-openclaw.md new file mode 100644 index 00000000..97b50a34 --- /dev/null +++ b/docs/get-started/run-openclaw.md @@ -0,0 +1,101 @@ + + +# Run OpenClaw Safely + +This tutorial shows you how to launch a sandbox with OpenClaw from the [NemoClaw Community catalog](https://github.com/NVIDIA/NemoClaw-Community) using the `--from` flag. This is a pre-built sandbox configuration that includes a container image, a tailored policy, and optional skills. + +## What You Will Learn + +- Understand what community sandboxes are and how they differ from default sandboxes. +- Use the `--from` flag to pull and build a complete sandbox configuration. +- Inspect the bundled policy that ships with a community sandbox. + +## Prerequisites + +- Met the prerequisites and installed the OpenShell CLI as described in the {doc}`quickstart` guide. +- NVIDIA GPU with drivers installed. OpenClaw requires GPU acceleration. + +## Create the Sandbox + +Run the following command: + +```console +$ nemoclaw sandbox create --from openclaw --keep +``` + +The `--from` flag tells the CLI to pull a sandbox definition from the OpenShell Community catalog. Here is what happens behind the scenes: + +1. Fetches the definition. The CLI downloads the OpenClaw sandbox definition from the OpenShell-Community repository. This includes a Dockerfile, a policy YAML, and any bundled skills. +2. Builds the image. The CLI builds the Dockerfile locally using Docker. The resulting image includes all tools and dependencies that OpenClaw needs. +3. Applies the bundled policy. Instead of the generic default policy, the sandbox starts with a policy written specifically for the OpenClaw workload. It allows the endpoints and binaries that OpenClaw requires. +4. Creates and keeps the sandbox. The `--keep` flag ensures the sandbox stays running after creation so you can connect and disconnect freely. + +:::{note} +The first build takes longer because Docker needs to pull base layers and install dependencies. Subsequent creates reuse the cached image. +::: + +## Connect to the Sandbox + +After creation completes, connect to the running sandbox: + +```console +$ nemoclaw sandbox connect +``` + +Replace `` with the sandbox name shown in the creation output. If you did not specify a name with `--name`, the CLI assigns one automatically. Run `nemoclaw sandbox list` to find it. + +## Explore the Environment + +The sandbox comes pre-configured for the OpenClaw workload. The tools, runtimes, and libraries that OpenClaw needs are already installed in the container image. The policy is tuned to allow the specific network endpoints and operations that OpenClaw uses, so you can start working immediately without policy adjustments. + +## Inspect the Bundled Policy + +To see exactly what the sandbox is allowed to do, pull the full policy: + +```console +$ nemoclaw policy get --full +``` + +This outputs the complete policy YAML. Review it to understand the sandbox's permissions: + +- Network policies, which hosts and ports the sandbox can reach, and which binaries are allowed to initiate those connections. +- Filesystem policy, which paths are read-only and which are read-write. +- Process restrictions, which user and group the sandbox runs as. +- Inference rules, which inference routing hints are allowed. + +Reviewing the bundled policy is a good practice before you use a community sandbox for sensitive work. + +:::{tip} +Save the policy to a file for reference or as a starting point for customization: + +```console +$ nemoclaw policy get --full > openclaw-policy.yaml +``` +::: + +## Clean Up + +Exit the sandbox if you are connected: + +```console +$ exit +``` + +Delete the sandbox: + +```console +$ nemoclaw sandbox delete +``` + +:::{note} +The OpenShell Community repository accepts contributions. If you build a sandbox configuration that would be useful to others, submit it to the [NemoClaw-Community](https://github.com/NVIDIA/NemoClaw-Community) repository. +::: + +## Next Steps + +- {doc}`../sandboxes/community-sandboxes`: Full reference on community sandbox definitions, available images, and how to contribute your own. +- {doc}`../safety-and-privacy/policies`: Learn the policy format and how to customize what a sandbox can do. +- {doc}`../sandboxes/create-and-manage`: Understand the isolation model and lifecycle behind every sandbox. diff --git a/docs/get-started/run-opencode.md b/docs/get-started/run-opencode.md new file mode 100644 index 00000000..00a85bb9 --- /dev/null +++ b/docs/get-started/run-opencode.md @@ -0,0 +1,258 @@ + + +# Run OpenCode with NVIDIA Inference + +This tutorial walks you through a realistic setup where you run [OpenCode](https://opencode.ai) inside a OpenShell sandbox with inference routed to NVIDIA API endpoints. Along the way, you will hit a policy denial, diagnose it from logs, write a custom policy, and configure inference routing. This is the full policy iteration loop that you will use whenever you onboard a new tool. + +## What You Will Learn + +- Create a provider manually using the `--from-existing` flag. +- Write a custom policy to replace the default policy. +- Read sandbox logs to diagnose denied actions. +- Distinguish between agent traffic and userland inference. +- Set up inference routes for code running inside the sandbox. + +## Prerequisites + +- Met the prerequisites and installed the OpenShell CLI as described in the {doc}`quickstart` guide. +- `NVIDIA_API_KEY` environment variable set on your host machine with a valid NVIDIA API key. + +## Create the Provider + +In the Claude Code tutorial, the CLI auto-discovered credentials. Here you create a provider explicitly, which gives you control over the provider name and type. + +```console +$ nemoclaw provider create --name nvidia --type nvidia --from-existing +``` + +The `--from-existing` flag tells the CLI to discover credentials from your local environment. It finds `NVIDIA_API_KEY` and stores it securely. The provider is now available to attach to any sandbox. + +Verify the provider exists: + +```console +$ nemoclaw provider list +``` + +## Create the Sandbox + +Create a sandbox with the NVIDIA provider attached and OpenCode as the startup command: + +```console +$ nemoclaw sandbox create --name opencode-sandbox --provider nvidia --keep -- opencode +``` + +The `--keep` flag keeps the sandbox alive after you exit, which you need for the iteration steps ahead. The CLI creates the sandbox with the default policy, injects the NVIDIA credentials, and starts OpenCode. + +## Hit a Policy Denial + +Try using OpenCode inside the sandbox. You will find that calls to NVIDIA inference endpoints fail. The default policy is designed around Claude Code, not OpenCode, so the required endpoints are not allowlisted. + +Open a second terminal and check the logs: + +```console +$ nemoclaw logs opencode-sandbox --tail +``` + +Alternatively, launch the OpenShell Terminal for a live view: + +```console +$ nemoclaw term +``` + +Look for lines like these: + +``` +action=deny host=integrate.api.nvidia.com binary=/usr/local/bin/opencode reason="no matching network policy" +action=deny host=opencode.ai binary=/usr/bin/node reason="no matching network policy" +action=inspect_for_inference host=integrate.api.nvidia.com binary=/bin/bash +``` + +Each log entry tells you the exact host, binary, and reason for the denial. + +## Understand the Denial + +The default policy contains a `nvidia_inference` network policy entry, but it is configured for a narrow set of binaries β€” typically `/usr/local/bin/claude` and `/usr/bin/node`. When OpenCode makes HTTP calls through its own binary, `curl`, or a shell subprocess, those connections do not match any policy rule and get denied. + +Two separate problems are at play: + +- OpenCode's own traffic. OpenCode contacts `opencode.ai` for its API and `integrate.api.nvidia.com` for inference. Neither endpoint has a matching rule for the binaries OpenCode uses. +- Missing endpoint. The default policy has no entry for `opencode.ai` at all. Even if the binary matched, the destination is not listed. + +This is expected behavior. OpenShell denies everything by default. You need to write a policy that explicitly allows what OpenCode needs. + +## Write a Custom Policy + +Create a file called `opencode-policy.yaml` with the following content: + +```yaml +version: 1 +inference: + allowed_routes: + - nvidia +filesystem_policy: + include_workdir: true + read_only: + - /usr + - /lib + - /proc + - /dev/urandom + - /app + - /etc + - /var/log + read_write: + - /sandbox + - /tmp + - /dev/null +landlock: + compatibility: best_effort +process: + run_as_user: sandbox + run_as_group: sandbox +network_policies: + opencode_api: + name: opencode-api + endpoints: + - host: opencode.ai + port: 443 + protocol: rest + tls: terminate + enforcement: enforce + access: full + binaries: + - path: /usr/local/bin/opencode + - path: /usr/bin/node + nvidia_inference: + name: nvidia-inference + endpoints: + - host: integrate.api.nvidia.com + port: 443 + protocol: rest + tls: terminate + enforcement: enforce + access: full + binaries: + - path: /usr/local/bin/opencode + - path: /usr/bin/node + - path: /usr/bin/curl + - path: /bin/bash + npm_registry: + name: npm-registry + endpoints: + - host: registry.npmjs.org + port: 443 + binaries: + - path: /usr/bin/npm + - path: /usr/bin/node + - path: /usr/local/bin/npm + - path: /usr/local/bin/node + github_rest_api: + name: github-rest-api + endpoints: + - host: api.github.com + port: 443 + protocol: rest + tls: terminate + enforcement: enforce + access: read-only + binaries: + - path: /usr/local/bin/opencode + - path: /usr/bin/node + - path: /usr/bin/gh + github_ssh_over_https: + name: github-ssh-over-https + endpoints: + - host: github.com + port: 443 + protocol: rest + tls: terminate + enforcement: enforce + rules: + - allow: + method: GET + path: "/**/info/refs*" + - allow: + method: POST + path: "/**/git-upload-pack" + binaries: + - path: /usr/bin/git +``` + +This policy differs from the default in four key ways: + +- `opencode_api`: Allows OpenCode and Node.js to reach `opencode.ai:443`. +- Broader `nvidia_inference` binaries: Adds `/usr/local/bin/opencode`, `/usr/bin/curl`, and `/bin/bash` so OpenCode's subprocesses can reach the NVIDIA endpoint. +- `inference.allowed_routes`: Includes `nvidia` so inference routing works for userland code. +- GitHub access: Scoped to support OpenCode's git operations. + +:::{warning} +The `filesystem_policy`, `landlock`, and `process` sections are static. They are set at sandbox creation time and cannot be changed on a running sandbox. To modify these, delete and recreate the sandbox. The `network_policies` and `inference` sections are dynamic and can be hot-reloaded. +::: + +## Apply the Policy + +Push your custom policy to the running sandbox: + +```console +$ nemoclaw policy set opencode-sandbox --policy opencode-policy.yaml --wait +``` + +The `--wait` flag blocks until the sandbox confirms the policy is loaded. + +Verify the policy revision was accepted: + +```console +$ nemoclaw policy list opencode-sandbox +``` + +The latest revision should show status `loaded`. + +## Set Up Inference Routing + +So far, you have allowed the OpenCode *agent* to reach `integrate.api.nvidia.com` directly through network policy. But code that OpenCode writes and runs inside the sandbox β€” scripts, notebooks, applications β€” uses a separate mechanism called the privacy router. + +Create an inference route so userland code can access NVIDIA models: + +```console +$ nemoclaw inference create \ + --routing-hint nvidia \ + --base-url https://integrate.api.nvidia.com \ + --model-id z-ai/glm5 \ + --api-key $NVIDIA_API_KEY +``` + +The policy you wrote earlier already includes `nvidia` in `inference.allowed_routes`, so no policy update is needed. If you had omitted it, you would add the route to the policy and push again. + +:::{note} +*Network policies* and *inference routes* are two separate enforcement points. Network policies control which hosts the agent binary can reach directly. Inference routes control where LLM API calls from userland code get routed through the privacy proxy. +::: + +## Verify the Policy + +Tail the logs again: + +```console +$ nemoclaw logs opencode-sandbox --tail +``` + +You should no longer see `action=deny` lines for the endpoints you added. Connections to `opencode.ai`, `integrate.api.nvidia.com`, and GitHub should show `action=allow`. + +If you still see denials, read the log line carefully. It tells you the exact host, port, and binary that was blocked. Add the missing entry to your policy and push again with `nemoclaw policy set`. This observe-modify-push cycle is the normal workflow for onboarding any new tool in OpenShell. + +## Clean Up + +When you are finished, delete the sandbox: + +```console +$ nemoclaw sandbox delete opencode-sandbox +``` + +## Next Steps + +- {doc}`../safety-and-privacy/policies`: Full reference on policy YAML structure, static and dynamic fields, and enforcement modes. +- {doc}`../safety-and-privacy/network-access-rules`: How the proxy evaluates network rules, L4 and L7 inspection, and TLS termination. +- {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. diff --git a/docs/get-started/tutorials.md b/docs/get-started/tutorials.md new file mode 100644 index 00000000..c95cae8a --- /dev/null +++ b/docs/get-started/tutorials.md @@ -0,0 +1,66 @@ +--- +title: + page: "NemoClaw Tutorials" + nav: "Tutorials" +description: "Step-by-step tutorials for running AI agents inside OpenShell sandboxes." +keywords: ["nemoclaw tutorials", "claude code sandbox", "opencode sandbox", "openclaw sandbox"] +topics: ["generative_ai", "cybersecurity"] +tags: ["ai_agents", "sandboxing", "tutorial"] +content: + type: tutorial + difficulty: technical_beginner + audience: [engineer, data_scientist] +--- + + + +# Tutorials + +Each tutorial below walks you through running a specific AI agent inside a OpenShell sandbox. Choose the tutorial that matches your agent. + +::::{grid} 1 1 2 2 +:gutter: 3 + +:::{grid-item-card} Run Claude Code Safely +:link: run-claude +:link-type: doc + +Create a sandbox with Claude Code. + ++++ +{bdg-secondary}`Tutorial` +::: + +:::{grid-item-card} Run OpenClaw Safely +:link: run-openclaw +:link-type: doc + +Launch a sandbox with OpenClaw from the OpenShell Community catalog using the `--from` flag. + ++++ +{bdg-secondary}`Tutorial` +::: + +:::{grid-item-card} Run OpenCode with NVIDIA Inference +:link: run-opencode +:link-type: doc + +Launch a sandbox with OpenCode with NVIDIA inference routed to NVIDIA API endpoints. + ++++ +{bdg-secondary}`Tutorial` +::: + +:::: + +```{toctree} +:hidden: +:maxdepth: 2 + +Run Claude Code Safely +Run OpenClaw Safely +Run OpenCode with NVIDIA Inference +``` diff --git a/docs/index.md b/docs/index.md index 96144b97..7c9584fd 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,64 +1,251 @@ +--- +title: + page: "NVIDIA OpenShell Developer Guide" + nav: "Get Started" + card: "NVIDIA OpenShell" +description: "OpenShell is the safe, private runtime for autonomous AI agents. Run agents in sandboxed environments that protect your data, credentials, and infrastructure." +topics: +- Generative AI +- Cybersecurity +tags: +- AI Agents +- Sandboxing +- Security +- Privacy +- Inference Routing +content: + type: index +--- + -# NVIDIA NemoClaw Developer Guide +# NVIDIA OpenShell + +[![GitHub](https://img.shields.io/badge/github-repo-green?logo=github)](https://github.com/NVIDIA/NemoClaw) +[![License](https://img.shields.io/badge/License-Apache_2.0-blue)](https://github.com/NVIDIA/NemoClaw/blob/main/LICENSE) +[![PyPI](https://img.shields.io/badge/PyPI-nemoclaw-orange?logo=pypi)](https://pypi.org/project/nemoclaw/) + +OpenShell is the safe, private runtime for autonomous AI agents. It provides sandboxed execution environments +that protect your data, credentials, and infrastructure. Agents run with exactly the permissions they need and +nothing more, governed by declarative policies that prevent unauthorized file access, data exfiltration, and +uncontrolled network activity. + +## Get Started + +Install the CLI and create your first sandbox in two commands. + +```{raw} html + +
+
+ + + +
+
+
$ pip install nemoclaw
+
$ nemoclaw sandbox create -- claude--from openclaw
+
+
+``` + +Refer to the [Quickstart](get-started/quickstart.md) for more details. + +--- + +## Explore + +::::{grid} 2 2 3 3 +:gutter: 3 + +:::{grid-item-card} About OpenShell +:link: about/overview +:link-type: doc + +Learn about OpenShell and its capabilities. + ++++ +{bdg-secondary}`Concept` +::: + +:::{grid-item-card} Get Started +:link: get-started/quickstart +:link-type: doc + +Quickstart guide and tutorials for creating a OpenShell sandbox with Claude Code, OpenClaw, and OpenCode. + ++++ +{bdg-secondary}`Tutorial` +::: + +:::{grid-item-card} Sandboxes +:link: sandboxes/index +:link-type: doc + +Create, manage, and customize sandboxes. Use community images or bring your own container. + ++++ +{bdg-secondary}`Concept` +::: -NemoClaw is the runtime environment for autonomous agents. It provides secure sandboxed execution, cluster management, and infrastructure for running AI agent workloads. +:::{grid-item-card} Safety and Privacy +:link: safety-and-privacy/index +:link-type: doc + +Write policies that control what agents can access. Iterate on network rules in real time. + ++++ +{bdg-secondary}`Concept` +::: + +:::{grid-item-card} Inference Routing +:link: inference/index +:link-type: doc + +Keep inference traffic private by routing API calls to local or self-hosted backends. + ++++ +{bdg-secondary}`Concept` +::: + +:::{grid-item-card} Reference +:link: reference/cli +:link-type: doc + +CLI commands, policy schema, environment variables, and system architecture. + ++++ +{bdg-secondary}`Reference` +::: + +:::: + +```{toctree} +:hidden: + +Home +``` ```{toctree} -:caption: About +:caption: About NVIDIA OpenShell :hidden: -about/index +Overview +How It Works +Release Notes ``` ```{toctree} :caption: Get Started :hidden: -get-started/index +get-started/quickstart +get-started/tutorials ``` ```{toctree} -:caption: Feature 1 +:caption: Sandboxes :hidden: -feature1/index +sandboxes/index +sandboxes/create-and-manage +sandboxes/providers +sandboxes/custom-containers +sandboxes/community-sandboxes +sandboxes/terminal ``` ```{toctree} -:caption: Feature 2 +:caption: Safety and Privacy :hidden: -feature2/index +safety-and-privacy/index +safety-and-privacy/security-model +safety-and-privacy/policies +safety-and-privacy/network-access-rules ``` ```{toctree} -:caption: Observability +:caption: Inference Routing :hidden: -observability/index +inference/index +inference/configure-routes ``` ```{toctree} :caption: Reference :hidden: -reference/index +reference/cli +reference/policy-schema +reference/architecture ``` ```{toctree} :caption: Troubleshooting :hidden: -troubleshooting/index +troubleshooting ``` ```{toctree} :caption: Resources :hidden: -resources/index +resources/eula ``` diff --git a/docs/inference/configure-routes.md b/docs/inference/configure-routes.md new file mode 100644 index 00000000..a76f904a --- /dev/null +++ b/docs/inference/configure-routes.md @@ -0,0 +1,92 @@ + + +# Configure Inference Routes + +This guide covers how to create and manage inference routes so that sandboxes can route AI API calls from userland code to policy-controlled backends. You will learn to create routes, connect them to sandboxes through policy, and manage routes across a cluster. + +:::{note} +Inference routes are for *userland code*, which are scripts and programs that the agent writes and executes inside the sandbox. The agent's own API traffic flows directly through network policies, not through inference routing. Refer to {doc}`../safety-and-privacy/network-access-rules` for the distinction between agent traffic and userland traffic. +::: + +## Create a Route + +Use `nemoclaw inference create` to register a new inference backend: + +```console +$ nemoclaw inference create \ + --routing-hint local \ + --base-url https://my-llm.example.com \ + --model-id my-model-v1 \ + --api-key sk-abc123 +``` + +This creates a route named after the routing hint. Any sandbox whose policy includes `local` in its `inference.allowed_routes` list can use this route. If you omit `--protocol`, the CLI probes the endpoint and auto-detects the supported protocol (refer to [Supported API Patterns](index.md#supported-api-patterns)). Refer to the [CLI Reference](../reference/cli.md#inference-create-flags) for all flags. + +## Manage Routes + +### List all routes + +```console +$ nemoclaw inference list +``` + +### Update a route + +Change any field on an existing route: + +```console +$ nemoclaw inference update --base-url https://new-backend.example.com +``` + +```console +$ nemoclaw inference update --model-id updated-model-v2 --api-key sk-new-key +``` + +### Delete a route + +```console +$ nemoclaw inference delete +``` + +Deleting a route that is referenced by running sandboxes does not interrupt those sandboxes immediately. Future inference requests that would have matched the deleted route will be denied. + +## Connect a Sandbox to Routes + +Inference routes take effect only when a sandbox policy references the route's `routing_hint` in its `inference.allowed_routes` list. + +### Step 1: Add the routing hint to your policy + +```yaml +inference: + allowed_routes: + - local +``` + +### Step 2: Create or update the sandbox with that policy + +```console +$ nemoclaw sandbox create --policy ./my-policy.yaml --keep -- claude +``` + +Or, if the sandbox is already running, push an updated policy: + +```console +$ nemoclaw policy set --policy ./my-policy.yaml --wait +``` + +The `inference` section is a dynamic field, so you can add or remove routing hints on a running sandbox without recreating it. + +## Good to Know + +- Cluster-level: routes are shared across all sandboxes in the cluster, not scoped to one sandbox. +- Per-model: each route maps to one model. Create multiple routes with the same `--routing-hint` but different `--model-id` values to expose multiple models. +- Hot-reloadable: routes can be created, updated, or deleted at any time without restarting sandboxes. + +## Next Steps + +- {doc}`index`: understand the inference routing architecture, interception sequence, and routing hints. +- {doc}`../safety-and-privacy/network-access-rules`: configure the network policies that control agent traffic (as opposed to userland inference traffic). +- {doc}`../safety-and-privacy/policies`: the full policy iteration workflow. diff --git a/docs/inference/index.md b/docs/inference/index.md new file mode 100644 index 00000000..67f47a30 --- /dev/null +++ b/docs/inference/index.md @@ -0,0 +1,83 @@ + + +# About Inference Routing + +The inference routing system keeps your AI inference traffic private by +transparently intercepting API calls from sandboxed agents and rerouting them +to backends you control. + +:::{note} +Inference routing applies to userland traffic: code that the agent writes +or runs, not the agent itself. The agent's own API calls (for example, Claude calling +`api.anthropic.com`) go directly through network policy. Refer to +{doc}`/safety-and-privacy/network-access-rules` for the distinction. +::: + +## How It Works + +When userland code inside a sandbox makes an API call (for example, using the OpenAI +or Anthropic SDK), the request flows through the sandbox proxy. If the +destination does not match any explicit network policy but the sandbox has +inference routes configured, the proxy: + +1. TLS-terminates the connection using the sandbox's ephemeral CA. +2. Detects the inference API pattern (for example, `POST /v1/chat/completions`). +3. Strips authorization headers and forwards to a matching backend. +4. Rewrites the authorization with the route's API key and model ID. +5. Returns the response to the agent's code. The agent sees a normal HTTP + response as if it came from the original API. + +The agent's code needs zero changes. Standard OpenAI/Anthropic SDK calls work +transparently. + +```{mermaid} +sequenceDiagram + participant Code as Userland Code + participant Proxy as Sandbox Proxy + participant OPA as Policy Engine + participant Router as Privacy Router + participant Backend as Your Backend + + Code->>Proxy: CONNECT api.openai.com:443 + Proxy->>OPA: evaluate policy + OPA-->>Proxy: InspectForInference + Proxy-->>Code: 200 Connection Established + Proxy->>Proxy: TLS terminate + Code->>Proxy: POST /v1/chat/completions + Proxy->>Router: route to matching backend + Router->>Backend: forwarded request + Backend-->>Router: response + Router-->>Proxy: response + Proxy-->>Code: HTTP 200 OK +``` + +## Supported API Patterns + +The proxy detects these inference patterns: + +| Pattern | Method | Path | +|---|---|---| +| OpenAI Chat Completions | POST | `/v1/chat/completions` | +| OpenAI Completions | POST | `/v1/completions` | +| Anthropic Messages | POST | `/v1/messages` | + +If an intercepted request does not match any known pattern, it is denied. + +## Key Properties + +- Zero code changes: standard SDK calls work transparently. +- Inference privacy: prompts and responses stay on your infrastructure. +- Credential isolation: the agent's code never sees your backend API key. +- Policy-controlled: `inference.allowed_routes` determines which routes a + sandbox can use. +- Hot-reloadable: update `allowed_routes` on a running sandbox without + restarting. + +## Next Steps + +- {doc}`configure-routes`: Create and manage inference routes. +- {doc}`/safety-and-privacy/network-access-rules`: Understand agent traffic versus + userland traffic and how network rules interact with inference routing. diff --git a/docs/observability/index.md b/docs/observability/index.md deleted file mode 100644 index 31d92605..00000000 --- a/docs/observability/index.md +++ /dev/null @@ -1,6 +0,0 @@ - - -# Observability diff --git a/docs/reference/architecture.md b/docs/reference/architecture.md new file mode 100644 index 00000000..d36af1d0 --- /dev/null +++ b/docs/reference/architecture.md @@ -0,0 +1,208 @@ +--- +title: + page: "Architecture Reference" + nav: "Architecture" +description: "Detailed reference for OpenShell architecture: gateway, sandbox internals, policy engine, privacy router, and remote deployment." +keywords: ["nemoclaw architecture", "sandbox architecture", "agent isolation", "k3s", "policy engine"] +topics: ["generative_ai", "cybersecurity"] +tags: ["ai_agents", "sandboxing", "security", "architecture"] +content: + type: reference + difficulty: technical_advanced + audience: [engineer, data_scientist] +--- + + + +# Architecture Reference + +This page provides detailed technical information about each OpenShell component. +For a high-level summary, refer to the [Architecture Overview](../about/architecture.md). + +## Component Diagram + +```{mermaid} +graph TB + subgraph docker["Docker Container"] + subgraph k3s["k3s Cluster"] + gw["Gateway"] + pr["Privacy Router"] + + subgraph pod1["Sandbox"] + sup1["Supervisor"] + proxy1["L7 Proxy"] + pe1["Policy Engine"] + agent1["Agent"] + + sup1 --> proxy1 + sup1 --> agent1 + proxy1 --> pe1 + end + + subgraph pod2["Sandbox"] + sup2["Supervisor"] + proxy2["L7 Proxy"] + pe2["Policy Engine"] + agent2["Agent"] + + sup2 --> proxy2 + sup2 --> agent2 + proxy2 --> pe2 + end + + gw -- "credentials,
policies" --> sup1 + gw -- "credentials,
policies" --> sup2 + end + end + + cli["nemoclaw CLI"] -- "gRPC" --> gw + agent1 -- "all outbound
traffic" --> proxy1 + agent2 -- "all outbound
traffic" --> proxy2 + proxy1 -- "policy-approved
traffic" --> internet["External Services"] + proxy2 -- "policy-approved
traffic" --> internet + proxy1 -- "inference traffic" --> pr + proxy2 -- "inference traffic" --> pr + pr -- "routed requests" --> backend["LLM Backend"] + + style cli fill:#ffffff,stroke:#000000,color:#000000 + style gw fill:#76b900,stroke:#000000,color:#000000 + style pr fill:#76b900,stroke:#000000,color:#000000 + style sup1 fill:#76b900,stroke:#000000,color:#000000 + style proxy1 fill:#76b900,stroke:#000000,color:#000000 + style pe1 fill:#76b900,stroke:#000000,color:#000000 + style agent1 fill:#ffffff,stroke:#000000,color:#000000 + style sup2 fill:#76b900,stroke:#000000,color:#000000 + style proxy2 fill:#76b900,stroke:#000000,color:#000000 + style pe2 fill:#76b900,stroke:#000000,color:#000000 + style agent2 fill:#ffffff,stroke:#000000,color:#000000 + style internet fill:#ffffff,stroke:#000000,color:#000000 + style backend fill:#ffffff,stroke:#000000,color:#000000 + style docker fill:#f5f5f5,stroke:#000000,color:#000000 + style k3s fill:#e8e8e8,stroke:#000000,color:#000000 + style pod1 fill:#f5f5f5,stroke:#000000,color:#000000 + style pod2 fill:#f5f5f5,stroke:#000000,color:#000000 + + linkStyle default stroke:#76b900,stroke-width:2px +``` + +## Gateway + +The gateway is the central control-plane API. It coordinates sandbox lifecycle +and state, acts as the auth boundary, and brokers all requests across the +platform. It exposes a gRPC API consumed by the CLI and handles: + +- Sandbox lifecycle: creates, monitors, and deletes sandbox pods. +- Provider storage: stores encrypted provider credentials. +- Policy distribution: delivers policy YAML to sandboxes at startup and on + hot-reload. +- SSH termination: terminates SSH tunnels from the CLI and routes them to + the correct sandbox. + +The CLI never talks to sandbox pods directly. All commands go through the +gateway. + +## Sandbox + +Each sandbox is an isolated runtime that includes container supervision and +general L7 egress routing. It runs as a Kubernetes pod containing a supervisor +process, an L7 proxy, and the agent. + +### Supervisor + +The supervisor is the sandbox's init process. It establishes all isolation +boundaries before starting the agent: + +1. Fetch credentials from the gateway for all attached providers. +2. Set up the network namespace. The sandbox gets its own network stack + with no default route. All outbound traffic is redirected through the proxy. +3. Apply Landlock filesystem restrictions based on the policy. +4. Apply seccomp filters to restrict available system calls. +5. Start the L7 proxy in the sandbox's network namespace. +6. Start the SSH server for interactive access. +7. Start the agent as a child process with credentials injected as + environment variables. + +### L7 Proxy + +Every outbound TCP connection from any process in the sandbox is routed through +the proxy. For each connection, the proxy: + +1. Resolves the calling binary through `/proc//exe`, ancestor process + walking, and `/proc//cmdline`. +2. Queries the policy engine with the destination host, port, and resolved + binary path. +3. Acts on the decision: allow the connection directly, hand it to the + privacy router for inference routing, or deny it. Refer to + [How the Proxy Evaluates Connections](../safety-and-privacy/network-access-rules.md#how-the-proxy-evaluates-connections) + for the full decision model. + +For endpoints configured with `protocol: rest` and `tls: terminate`, the proxy +performs full L7 inspection: it decrypts TLS, reads the HTTP method and path, +evaluates access rules, then re-encrypts and forwards the request. + +## Policy Engine + +The policy engine is the definition and enforcement layer for filesystem, +network, and process constraints. Defense in depth enforces policies from the +application layer down to infrastructure and kernel layers. + +The engine evaluates policies compiled from the sandbox's policy YAML. It is +queried synchronously by the proxy on every outbound connection. Policy updates +delivered through hot-reload are compiled and loaded without restarting the proxy. + +## Privacy Router + +The privacy router is a privacy-aware LLM routing layer that keeps sensitive +context on sandbox compute and routes based on cost/privacy policy. + +When the policy engine determines that a connection should be inspected for +inference, the privacy router: + +1. Reads the intercepted HTTP request. +2. Checks whether the method and path match a recognized inference API pattern + (`/v1/chat/completions`, `/v1/completions`, `/v1/messages`). +3. Selects a route whose `routing_hint` appears in the sandbox policy's + `allowed_routes`. +4. Strips the original authorization header. +5. Injects the route's API key and model ID. +6. Forwards the request to the route's backend URL. + +The router refreshes its route list periodically from the gateway, so routes +created with `nemoclaw inference create` become available without restarting +sandboxes. + +## Remote Deployment + +NemoClaw can deploy the cluster to a remote host via SSH. This is useful for +shared team environments or running sandboxes on machines with more resources. + +### Deploy + +```console +$ nemoclaw gateway start --remote user@host --ssh-key ~/.ssh/id_rsa +``` + +The CLI connects to the remote machine over SSH, installs k3s, deploys the +NemoClaw control plane, and registers the cluster locally. The remote machine +needs Docker installed. + +### Tunnel + +After deploying to a remote host, set up a tunnel for CLI access: + +```console +$ nemoclaw gateway tunnel +``` + +This establishes an SSH tunnel from your local machine to the remote cluster's +API server. All subsequent CLI commands route through this tunnel transparently. + +### Remote Architecture + +The architecture is identical to a local deployment. The only difference is +that the Docker container runs on the remote host instead of your workstation. +The CLI communicates with the gateway over the SSH tunnel. Sandbox SSH +connections are also tunneled through the gateway. diff --git a/docs/reference/cli.md b/docs/reference/cli.md new file mode 100644 index 00000000..9856ee5f --- /dev/null +++ b/docs/reference/cli.md @@ -0,0 +1,204 @@ + + +# CLI Reference + +Complete command reference for the `nemoclaw` CLI. Every subcommand, flag, and option is documented here. + +## Command Tree + +```text +nemoclaw +β”œβ”€β”€ status +β”œβ”€β”€ logs [name] +β”œβ”€β”€ forward +β”‚ β”œβ”€β”€ start +β”‚ β”œβ”€β”€ stop +β”‚ └── list +β”œβ”€β”€ policy +β”‚ β”œβ”€β”€ set +β”‚ β”œβ”€β”€ get +β”‚ └── list +β”œβ”€β”€ gateway +β”‚ β”œβ”€β”€ start +β”‚ β”œβ”€β”€ stop +β”‚ β”œβ”€β”€ destroy +β”‚ β”œβ”€β”€ info +β”‚ β”œβ”€β”€ tunnel +β”‚ └── select [name] +β”œβ”€β”€ sandbox +β”‚ β”œβ”€β”€ create +β”‚ β”œβ”€β”€ get [name] +β”‚ β”œβ”€β”€ list +β”‚ β”œβ”€β”€ delete +β”‚ β”œβ”€β”€ connect [name] +β”‚ β”œβ”€β”€ upload [name] +β”‚ β”œβ”€β”€ download [name] +β”‚ └── ssh-config +β”œβ”€β”€ provider +β”‚ β”œβ”€β”€ create +β”‚ β”œβ”€β”€ get +β”‚ β”œβ”€β”€ list +β”‚ β”œβ”€β”€ update +β”‚ └── delete +β”œβ”€β”€ inference +β”‚ β”œβ”€β”€ create +β”‚ β”œβ”€β”€ update +β”‚ β”œβ”€β”€ delete +β”‚ └── list +β”œβ”€β”€ term +└── completions +``` + +## Top-Level Commands + +Commands available directly under `nemoclaw` for common operations. + +| Command | Description | +|---|---| +| `nemoclaw status` | Show the health and status of the active gateway. | +| `nemoclaw logs [name]` | View sandbox logs. Use `--tail` for streaming, `--source` and `--level` to filter. When name is omitted, uses the last-used sandbox. | +| `nemoclaw forward start ` | Forward a sandbox port to the host. Add `-d` for background mode. | +| `nemoclaw forward stop ` | Stop an active port forward. | +| `nemoclaw forward list` | List all active port forwards. | +| `nemoclaw policy set ` | Apply or update a policy on a running sandbox. Pass `--policy `. | +| `nemoclaw policy get ` | Show the active policy for a sandbox. Add `--full` for the complete policy with metadata. | +| `nemoclaw policy list ` | List all policy versions applied to a sandbox, with status. | + +## Gateway Commands + +Manage the OpenShell runtime cluster. + +| Command | Description | +|---|---| +| `nemoclaw gateway start` | Deploy a new cluster. Add `--remote user@host` for remote deployment. | +| `nemoclaw gateway stop` | Stop the active cluster, preserving state. | +| `nemoclaw gateway destroy` | Permanently remove the cluster and all its data. | +| `nemoclaw gateway info` | Show detailed information about the cluster. | +| `nemoclaw gateway tunnel` | Set up a kubectl tunnel to a remote cluster. | +| `nemoclaw gateway select ` | Set the active cluster. All subsequent commands target this cluster. | +| `nemoclaw gateway select` | List all registered clusters (when called without a name). | + +## Sandbox Commands + +Create and manage isolated agent execution environments. + +| Command | Description | +|---|---| +| `nemoclaw sandbox create` | Create a new sandbox. See flag reference below. | +| `nemoclaw sandbox get [name]` | Show detailed information about a sandbox. When name is omitted, uses the last-used sandbox. | +| `nemoclaw sandbox list` | List all sandboxes in the active cluster. | +| `nemoclaw sandbox delete ` | Delete one or more sandboxes by name. | +| `nemoclaw sandbox connect [name]` | Open an interactive SSH session into a running sandbox. When name is omitted, reconnects to the last-used sandbox. | +| `nemoclaw sandbox upload [name]` | Upload files from the host into a sandbox. When name is omitted, uses the last-used sandbox. | +| `nemoclaw sandbox download [name]` | Download files from a sandbox to the host. When name is omitted, uses the last-used sandbox. | +| `nemoclaw sandbox ssh-config ` | Print SSH config for a sandbox. Append to `~/.ssh/config` for VS Code Remote-SSH. | + +### Sandbox Create Flags + +| Flag | Description | +|---|---| +| `--name` | Assign a human-readable name to the sandbox. Auto-generated if omitted. | +| `--provider` | Attach a credential provider. Repeatable for multiple providers. | +| `--policy` | Path to a policy YAML file to apply at creation time. | +| `--upload` | Upload local files into the sandbox before running. | +| `--keep` | Keep the sandbox alive after the trailing command exits. | +| `--forward` | Forward a local port into the sandbox at startup. | +| `--from` | Build from a community sandbox name, local Dockerfile directory, or container image reference. | +| `-- ` | The command to run inside the sandbox. Everything after `--` is passed as the agent command. | + +## Provider Commands + +Manage credential providers that inject secrets into sandboxes. + +| Command | Description | +|---|---| +| `nemoclaw provider create` | Create a new credential provider. See flag reference below. | +| `nemoclaw provider get ` | Show details of a provider. | +| `nemoclaw provider list` | List all providers in the active cluster. | +| `nemoclaw provider update ` | Update a provider's credentials or configuration. | +| `nemoclaw provider delete ` | Delete a provider. | + +### Provider Create Flags + +| Flag | Description | +|---|---| +| `--name` | Name for the provider. | +| `--type` | Provider type: `claude`, `codex`, `opencode`, `github`, `gitlab`, `nvidia`, `generic`, `outlook`. | +| `--from-existing` | Discover credentials from your current shell environment variables. | +| `--credential` | Set a credential explicitly. Format: `KEY=VALUE` or bare `KEY` to read from env. Repeatable. | +| `--config` | Set a configuration value. Format: `KEY=VALUE`. Repeatable. | + +## Inference Commands + +Manage inference routes that intercept and reroute LLM API calls from userland code. + +| Command | Description | +|---|---| +| `nemoclaw inference create` | Create a new inference route. See flag reference below. | +| `nemoclaw inference update ` | Update an existing route's configuration. | +| `nemoclaw inference delete ` | Delete an inference route. | +| `nemoclaw inference list` | List all inference routes in the active cluster. | + +### Inference Create Flags + +| Flag | Description | +|---|---| +| `--routing-hint` | Short label that identifies this route (for example, `local`, `nvidia`, `staging`). Referenced by `allowed_routes` in sandbox policies. | +| `--base-url` | Base URL of the inference backend (for example, `https://vllm.internal:8000`). | +| `--model-id` | Model identifier to send to the backend (for example, `meta/llama-3.1-8b`). | +| `--api-key` | API key for authenticating with the backend. | +| `--protocol` | API protocol: `openai` or `anthropic`. Defaults to `openai`. | +| `--disabled` | Create the route in a disabled state. | + +## OpenShell Terminal + +`nemoclaw term` launches the OpenShell Terminal, a dashboard that shows sandbox +status, live logs, and policy decisions in a single view. Navigate with `j`/`k`, +press `f` to follow live output, `s` to filter by source, and `q` to quit. + +Refer to {doc}`/sandboxes/terminal` for the full guide, including how to read log +entries, diagnose blocked connections, and interpret inference interception. + +## Sandbox Name Fallback + +Commands that accept an optional `[name]` argument, such as `get`, `connect`, `upload`, `download`, and `logs`, fall back to the last-used sandbox when the name is omitted. The CLI records the sandbox name each time you create or connect to a sandbox. When falling back, the CLI prints a hint showing which sandbox was selected. + +If no sandbox has been used yet and no name is provided, the command exits with an error prompting you to specify a name. + +## Environment Variables + +| Variable | Description | +|---|---| +| `NEMOCLAW_CLUSTER` | Name of the cluster to operate on. Overrides the active cluster set by `nemoclaw gateway select`. | +| `NEMOCLAW_SANDBOX_POLICY` | Default path to a policy YAML file. When set, `nemoclaw sandbox create` uses this policy if no `--policy` flag is provided. | + +## Shell Completions + +Generate shell completion scripts for tab completion: + +```console +$ nemoclaw completions bash +$ nemoclaw completions zsh +$ nemoclaw completions fish +``` + +Pipe the output to your shell's config file: + +```console +$ nemoclaw completions zsh >> ~/.zshrc +$ source ~/.zshrc +``` + +## Self-Teaching + +Every command and subcommand includes built-in help. Use `--help` at any level to see available subcommands, flags, and usage examples: + +```console +$ nemoclaw --help +$ nemoclaw sandbox --help +$ nemoclaw sandbox create --help +$ nemoclaw gateway --help +``` diff --git a/docs/reference/policy-schema.md b/docs/reference/policy-schema.md new file mode 100644 index 00000000..033d1b09 --- /dev/null +++ b/docs/reference/policy-schema.md @@ -0,0 +1,224 @@ + + +# Policy Schema Reference + +Complete field reference for the sandbox policy YAML. Each field is documented with its type, whether it is required, and whether it is static (locked at sandbox creation) or dynamic (hot-reloadable on a running sandbox). + +## Top-Level Structure + +```yaml +version: 1 +filesystem_policy: { ... } +landlock: { ... } +process: { ... } +network_policies: { ... } +inference: { ... } +``` + +| Field | Type | Required | Category | Description | +|---|---|---|---|---| +| `version` | integer | Yes | -- | Policy schema version. Must be `1`. | +| `filesystem_policy` | object | No | Static | Controls which directories the agent can read and write. | +| `landlock` | object | No | Static | Configures Landlock LSM enforcement behavior. | +| `process` | object | No | Static | Sets the user and group the agent process runs as. | +| `network_policies` | map | No | Dynamic | Declares which binaries can reach which network endpoints. | +| `inference` | object | No | Dynamic | Controls which inference routing backends are available. | + +Static fields are set at sandbox creation time. Changing them requires destroying and recreating the sandbox. Dynamic fields can be updated on a running sandbox with `nemoclaw policy set` and take effect without restarting. + +## Version + +| Field | Type | Required | Description | +|---|---|---|---| +| `version` | integer | Yes | Schema version number. Currently must be `1`. | + +## Filesystem Policy + +**Category:** Static + +Controls filesystem access inside the sandbox. Paths not listed in either `read_only` or `read_write` are inaccessible. + +| Field | Type | Required | Description | +|---|---|---|---| +| `include_workdir` | bool | No | When `true`, automatically adds the agent's working directory to `read_write`. | +| `read_only` | list of strings | No | Paths the agent can read but not modify. Typically system directories like `/usr`, `/lib`, `/etc`. | +| `read_write` | list of strings | No | Paths the agent can read and write. Typically `/sandbox` (working directory) and `/tmp`. | + +**Validation constraints:** + +- Every path must be absolute (start with `/`). +- Paths must not contain `..` traversal components. The server normalizes paths before storage, but rejects policies where traversal would escape the intended scope. +- Read-write paths must not be overly broad (for example, `/` alone is rejected). +- Each individual path must not exceed 4096 characters. +- The combined total of `read_only` and `read_write` paths must not exceed 256. + +Policies that violate these constraints are rejected with `INVALID_ARGUMENT` at creation or update time. Disk-loaded YAML policies that fail validation fall back to a restrictive default. + +Example: + +```yaml +filesystem_policy: + include_workdir: true + read_only: + - /usr + - /lib + - /proc + - /dev/urandom + - /etc + read_write: + - /sandbox + - /tmp + - /dev/null +``` + +## Landlock + +**Category:** Static + +Configures [Landlock LSM](https://docs.kernel.org/security/landlock.html) enforcement at the kernel level. Landlock provides mandatory filesystem access control below what UNIX permissions allow. + +| Field | Type | Required | Values | Description | +|---|---|---|---|---| +| `compatibility` | string | No | `best_effort`, `hard_requirement` | How OpenShell handles kernel ABI differences. `best_effort` uses the highest Landlock ABI the host kernel supports. `hard_requirement` fails if the required ABI is unavailable. | + +Example: + +```yaml +landlock: + compatibility: best_effort +``` + +## Process + +**Category:** Static + +Sets the OS-level identity for the agent process inside the sandbox. + +| Field | Type | Required | Description | +|---|---|---|---| +| `run_as_user` | string | No | The user name or UID the agent process runs as. Default: `sandbox`. | +| `run_as_group` | string | No | The group name or GID the agent process runs as. Default: `sandbox`. | + +**Validation constraint:** Neither `run_as_user` nor `run_as_group` may be set to `root` or `0`. Policies that request root process identity are rejected at creation or update time. + +Example: + +```yaml +process: + run_as_user: sandbox + run_as_group: sandbox +``` + +## Network Policies + +**Category:** Dynamic + +A map of named network policy entries. Each entry declares a set of endpoints and a set of binaries. Only the listed binaries are permitted to connect to the listed endpoints. The map key is a logical identifier. The `name` field inside the entry is the display name used in logs. + +### Network Policy Entry + +| Field | Type | Required | Description | +|---|---|---|---| +| `name` | string | No | Display name for the policy entry. Used in log output. Defaults to the map key. | +| `endpoints` | list of endpoint objects | Yes | Hosts and ports this entry permits. | +| `binaries` | list of binary objects | Yes | Executables allowed to connect to these endpoints. | + +### Endpoint Object + +Each endpoint defines a reachable destination and optional inspection rules. + +| Field | Type | Required | Description | +|---|---|---|---| +| `host` | string | Yes | Hostname or IP address. Supports wildcards: `*.example.com` matches any subdomain. | +| `port` | integer | Yes | TCP port number. | +| `protocol` | string | No | Set to `rest` to enable L7 (HTTP) inspection. Omit for L4-only (TCP passthrough). | +| `tls` | string | No | TLS handling mode. `terminate` decrypts TLS at the proxy for inspection. `passthrough` forwards encrypted traffic without inspection. Only relevant when `protocol` is `rest`. | +| `enforcement` | string | No | `enforce` actively blocks disallowed requests. `audit` logs violations but allows traffic through. | +| `access` | string | No | HTTP access level. One of `read-only`, `read-write`, or `full`. Refer to table below. Mutually exclusive with `rules`. | +| `rules` | list of rule objects | No | Fine-grained per-method, per-path allow rules. Mutually exclusive with `access`. | + +#### Access Levels + +| Value | Allowed HTTP Methods | +|---|---| +| `full` | All methods and paths. | +| `read-only` | `GET`, `HEAD`, `OPTIONS`. | +| `read-write` | `GET`, `HEAD`, `OPTIONS`, `POST`, `PUT`, `PATCH`. | + +#### Rule Object + +Used when `access` is not set. Each rule explicitly allows a method and path combination. + +| Field | Type | Required | Description | +|---|---|---|---| +| `allow.method` | string | Yes | HTTP method to allow (for example, `GET`, `POST`). | +| `allow.path` | string | Yes | URL path pattern. Supports `*` and `**` glob syntax. | + +Example with rules: + +```yaml +rules: + - allow: + method: GET + path: /**/info/refs* + - allow: + method: POST + path: /**/git-upload-pack +``` + +### Binary Object + +Identifies an executable that is permitted to use the associated endpoints. + +| Field | Type | Required | Description | +|---|---|---|---| +| `path` | string | Yes | Filesystem path to the executable. Supports glob patterns with `*` and `**`. For example, `/sandbox/.vscode-server/**` matches any executable under that directory tree. | + +### Full Example + +```yaml +network_policies: + github_rest_api: + name: github-rest-api + endpoints: + - host: api.github.com + port: 443 + protocol: rest + tls: terminate + enforcement: enforce + access: read-only + binaries: + - path: /usr/local/bin/claude + - path: /usr/bin/node + - path: /usr/bin/gh + npm_registry: + name: npm-registry + endpoints: + - host: registry.npmjs.org + port: 443 + binaries: + - path: /usr/bin/npm + - path: /usr/bin/node +``` + +## Inference + +**Category:** Dynamic + +Controls which inference routing backends userland code can access. The `allowed_routes` list names route types that the privacy router will accept. Traffic matching an inference API pattern that targets a route type not in this list is denied. + +| Field | Type | Required | Description | +|---|---|---|---| +| `allowed_routes` | list of strings | No | Routing hint labels (e.g., `local`, `nvidia`, `staging`) that this sandbox can use. Must match the `routing_hint` of inference routes created with `nemoclaw inference create`. | + +Example: + +```yaml +inference: + allowed_routes: + - local + - nvidia +``` diff --git a/docs/reference/index.md b/docs/resources/eula.md similarity index 93% rename from docs/reference/index.md rename to docs/resources/eula.md index d15068d3..a1d6f1b9 100644 --- a/docs/reference/index.md +++ b/docs/resources/eula.md @@ -3,4 +3,4 @@ SPDX-License-Identifier: Apache-2.0 --> -# Reference \ No newline at end of file +# License diff --git a/docs/resources/index.md b/docs/resources/index.md deleted file mode 100644 index eb696ad1..00000000 --- a/docs/resources/index.md +++ /dev/null @@ -1,6 +0,0 @@ - - -# Resources diff --git a/docs/safety-and-privacy/index.md b/docs/safety-and-privacy/index.md new file mode 100644 index 00000000..68cc20e2 --- /dev/null +++ b/docs/safety-and-privacy/index.md @@ -0,0 +1,71 @@ + + +# About Safety and Privacy + +OpenShell wraps every sandbox in four independent protection layers. No single +point of failure can compromise your environment. Each layer covers gaps the +others cannot. + +```{mermaid} +graph TB + subgraph runtime["OpenShell Runtime"] + direction TB + + subgraph layers["Protection Layers"] + direction TB + + fs["Filesystem β€” Landlock LSM"] + net["Network β€” Proxy + Policy Engine"] + proc["Process β€” seccomp + Unprivileged User"] + inf["Inference β€” Privacy Router"] + + subgraph sandbox["Sandbox"] + agent(["AI Agent"]) + end + end + end + + agent -- "read /sandbox βœ”" --> fs + agent -- "read /etc/shadow ✘" --> fs + agent -- "curl approved.com βœ”" --> net + agent -- "curl evil.com ✘" --> net + agent -- "sudo install pkg ✘" --> proc + agent -- "call api.openai.com" --> inf + inf -- "reroute β†’ your backend βœ”" --> net + + style runtime fill:#f5f5f5,stroke:#000000,color:#000000 + style layers fill:#e8e8e8,stroke:#000000,color:#000000 + style sandbox fill:#f5f5f5,stroke:#000000,color:#000000 + style agent fill:#ffffff,stroke:#000000,color:#000000 + style fs fill:#76b900,stroke:#000000,color:#000000 + style net fill:#76b900,stroke:#000000,color:#000000 + style proc fill:#76b900,stroke:#000000,color:#000000 + style inf fill:#76b900,stroke:#000000,color:#000000 + + linkStyle default stroke:#76b900,stroke-width:2px +``` + +## How the Layers Work Together + +You control all four layers through a single YAML policy. + +| Layer | What It Protects | When It Applies | +|---|---|---| +| **Filesystem** (Landlock LSM) | Prevents reads/writes outside allowed paths. | Locked at sandbox creation. | +| **Network** (Proxy + Policy Engine) | Blocks unauthorized outbound connections. | Hot-reloadable at runtime. | +| **Process** (seccomp + unprivileged user) | Blocks privilege escalation and dangerous syscalls. | Locked at sandbox creation. | +| **Inference** (Privacy Router) | Reroutes API calls to backends you control. | Hot-reloadable at runtime. | + +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. + +## 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. diff --git a/docs/safety-and-privacy/network-access-rules.md b/docs/safety-and-privacy/network-access-rules.md new file mode 100644 index 00000000..4ef9bd65 --- /dev/null +++ b/docs/safety-and-privacy/network-access-rules.md @@ -0,0 +1,290 @@ + + +# Network Access Rules + +Every outbound connection from a sandbox passes through OpenShell's transparent +proxy. Nothing leaves the sandbox directly. The proxy identifies which binary +initiated the connection, evaluates the active policy, and decides what happens +next. + +## How the Proxy Evaluates Connections + +Each outbound connection resolves to one of three outcomes: + +| Outcome | When it applies | What happens | +|------------------------|-----------------------------------------------------------------------------|-----------------------------------------------------------------------------| +| Allow | A `network_policies` entry matches the destination host *and* the calling binary. | Traffic flows directly to the destination. The agent's own API key is used. | +| InspectForInference| No network policy matches, but `inference.allowed_routes` is configured. | The proxy intercepts the connection and hands it to the privacy router, which reroutes it to a configured backend. The route's API key is used. | +| Deny | No network policy matches and no inference route applies. | The connection is blocked. The calling process receives a 403 or connection reset. | + +:::{note} +This is the most important distinction in OpenShell's network model. + +*Agent traffic* is the coding agent (Claude, OpenCode, Codex) calling its own API to get completions. This traffic matches a `network_policies` entry because the policy declares both the endpoint (for example, `api.anthropic.com:443`) and the binary (for example, `/usr/local/bin/claude`). The proxy allows it through directly. The agent's own API key (injected by the provider) is used as-is. + +*Userland traffic* is code that the agent *writes* making inference calls. A Python script calling the OpenAI-compatible API, a data pipeline hitting an LLM endpoint, a test harness querying a model. This traffic does *not* match any network policy because the calling binary (`/usr/bin/python3`) is not listed in the agent's policy entry. The proxy intercepts it, the privacy router reroutes it to a configured backend, and the route's API key is substituted in. The agent's code never touches your real API key. +::: + +```{mermaid} +flowchart TD + A["Outbound connection from sandbox"] --> B["Proxy resolves calling binary\n(/proc/pid/exe, ancestors, cmdline)"] + B --> C{"Network policy matches\n(endpoint + binary)?"} + C -- Yes --> D["Allow: forward directly\nto destination"] + C -- No --> E{"Inference routes\nconfigured?"} + E -- Yes --> F["InspectForInference:\nTLS terminate, check for\ninference pattern"] + F --> G{"Matches inference\npattern?"} + G -- Yes --> H["Route to configured\ninference backend"] + G -- No --> I["Deny: 403"] + E -- No --> I + + style A fill:#ffffff,stroke:#000000,color:#000000 + style B fill:#76b900,stroke:#000000,color:#000000 + style C fill:#76b900,stroke:#000000,color:#000000 + style D fill:#76b900,stroke:#000000,color:#000000 + style E fill:#76b900,stroke:#000000,color:#000000 + style F fill:#76b900,stroke:#000000,color:#000000 + style G fill:#76b900,stroke:#000000,color:#000000 + style H fill:#76b900,stroke:#000000,color:#000000 + style I fill:#ff4444,stroke:#000000,color:#ffffff + + linkStyle default stroke:#76b900,stroke-width:2px +``` + +## Structure of a Network Policy Entry + +Each entry in the `network_policies` section pairs a set of endpoints with a set of binaries. Only the listed binaries can connect to the listed endpoints: + +```yaml +network_policies: + my_rule: + name: my-rule + endpoints: + - host: api.example.com + port: 443 + protocol: rest + tls: terminate + enforcement: enforce + access: full + binaries: + - path: /usr/local/bin/my-agent +``` + +The key (`my_rule`) is a logical name for reference. The `name` field is the human-readable label that appears in logs and the OpenShell Terminal. + +## Endpoints + +Each endpoint entry controls access to a single host-port combination. Refer to [Endpoint Object](../reference/policy-schema.md#endpoint-object) for the full field reference. + +The `access` field provides presets: `full` (all methods), `read-only` (`GET`, `HEAD`, `OPTIONS`), or `read-write` (`GET`, `HEAD`, `OPTIONS`, `POST`, `PUT`, `PATCH`). + +### Custom Rules + +When access presets are not granular enough, define explicit allow rules. Each rule specifies a method and a path pattern: + +```yaml +endpoints: + - host: github.com + port: 443 + protocol: rest + tls: terminate + enforcement: enforce + rules: + - allow: + method: GET + path: /**/info/refs* + - allow: + method: POST + path: /**/git-upload-pack +``` + +This example allows Git fetch operations (read-only clone and pull) while blocking push operations. Path patterns use glob syntax. + +If a request does not match any rule, it is denied (in `enforce` mode) or logged (in `audit` mode). + +## Binaries + +The `binaries` list specifies which executables are permitted to use the endpoint. Each entry has a `path` field that the proxy matches against the calling process. + +The proxy resolves the calling binary through several mechanisms, evaluated in order: + +| Match type | Example | Description | +|-------------------|---------|-------------| +| Exact path | `/usr/local/bin/claude` | Matches the binary at exactly this path. | +| Ancestor process | `/usr/local/bin/claude` | Matches if any ancestor in the process tree has this path. A Node.js subprocess spawned by Claude matches a `claude` entry. | +| Cmdline path | `/usr/local/bin/opencode` | Matches against `/proc/pid/cmdline` for interpreted languages where the `exe` link points to the interpreter. | +| Glob pattern | `/sandbox/.vscode-server/**` | Matches any executable under the directory tree. | + +```yaml +binaries: + - path: /usr/local/bin/claude + - path: /usr/bin/node + - path: /sandbox/.vscode-server/** +``` + +## L7 Inspection + +To inspect HTTPS traffic at the HTTP level, you need both `protocol: rest` and `tls: terminate` on the endpoint: + +```yaml +endpoints: + - host: api.example.com + port: 443 + protocol: rest + tls: terminate + enforcement: enforce + access: full +``` + +With both fields set, the proxy terminates the TLS connection, decrypts the HTTP request, evaluates it against the `access` preset or custom `rules`, and re-encrypts before forwarding to the destination. + +:::{warning} +Without `tls: terminate` on port 443, the proxy cannot decrypt the traffic. L7 rules (`access`, `rules`) are not evaluated because the HTTP payload is encrypted. The connection is handled at L4 only: allowed or denied based on host and port, with no HTTP-level access control. +::: + +### Bare Endpoints (L4-Only) + +Endpoints declared without `protocol` or `tls` are L4-only: + +```yaml +endpoints: + - host: registry.npmjs.org + port: 443 +``` + +The proxy allows the TCP connection through to the destination without decrypting or inspecting the payload. Use L4-only entries for non-HTTP traffic, package registries where you need connectivity but not method-level control, or endpoints where TLS termination is not desired. + +:::{warning} +With L4-only rules, you have no control over *what* is sent to the endpoint: only *whether* the connection is allowed. Any binary listed in the entry can send any data to the host. If you need to restrict HTTP methods or paths, add `protocol: rest` and `tls: terminate`. +::: + +## Forward Proxy Mode + +The sandbox proxy supports two request modes: CONNECT tunnels (the default for +HTTPS) and forward proxy (for plain HTTP to private endpoints). + +When a client inside the sandbox sets `HTTP_PROXY` and makes a plain `http://` +request, standard HTTP libraries send a forward proxy request instead of a +CONNECT tunnel. The proxy handles these transparently. + +### Security Constraints + +Forward proxy mode is restricted to private IP endpoints that are explicitly +allowed by policy. Plain HTTP traffic never reaches the public internet. All +three conditions must be true: + +1. The network policy explicitly allows the destination. +2. The matched endpoint has `allowed_ips` configured. +3. All resolved IP addresses are RFC 1918 private (`10/8`, `172.16/12`, `192.168/16`). + +If any condition fails, the proxy returns 403. + +| Condition | Forward proxy | CONNECT | +|---|---|---| +| Public IP, no `allowed_ips` | 403 | Allowed (standard SSRF check) | +| Public IP, with `allowed_ips` | 403 (private-IP gate) | Allowed if IP in allowlist | +| Private IP, no `allowed_ips` | 403 | 403 (SSRF block) | +| Private IP, with `allowed_ips` | Allowed | Allowed | +| `https://` scheme | 403 (must use CONNECT) | N/A | + +### Policy Configuration + +No new policy fields are needed. The same `network_policies` entry that enables +CONNECT access to a private endpoint also enables forward proxy access. Add +`allowed_ips` to the endpoint: + +```yaml +network_policies: + internal_service: + name: internal-service + endpoints: + - host: 10.86.8.223 + port: 8000 + allowed_ips: + - "10.86.8.223/32" + binaries: + - path: /usr/local/bin/python3.12 +``` + +With this policy, both CONNECT and forward proxy requests to the endpoint work: + +```python +import httpx +resp = httpx.get("http://10.86.8.223:8000/api/", + proxy="http://10.200.0.1:3128") +``` + +### Limitations + +- Forward proxy v1 injects `Connection: close` (no keep-alive). Each connection handles exactly one request-response exchange. +- L7 inspection is not performed on forwarded traffic. +- `https://` URLs must use the CONNECT tunnel, not the forward proxy path. + +## Enforcement Modes + +Each endpoint can operate in one of two enforcement modes: + +| Mode | Behavior on violation | +|-----------|-----------------------| +| `enforce` | Blocks the request and returns HTTP 403 to the calling process. The violation is logged. | +| `audit` | Logs the violation but forwards the traffic to the destination. The agent is not interrupted. | + +:::{tip} +Start with `enforcement: audit` when developing a new policy. Audit mode shows you what *would* be blocked without actually breaking the agent. After the policy is correct and you have confirmed that no legitimate traffic is flagged, switch to `enforcement: enforce`. +::: + +## Putting It Together + +Here is a realistic policy snippet for an agent that needs to reach its own API, clone Git repositories (read-only), and install npm packages: + +```yaml +network_policies: + agent_api: + name: agent-api + endpoints: + - host: api.anthropic.com + port: 443 + protocol: rest + tls: terminate + enforcement: enforce + access: full + binaries: + - path: /usr/local/bin/claude + - path: /usr/bin/node + + github: + name: github + endpoints: + - host: github.com + port: 443 + protocol: rest + tls: terminate + enforcement: enforce + rules: + - allow: + method: GET + path: /**/info/refs* + - allow: + method: POST + path: /**/git-upload-pack + binaries: + - path: /usr/bin/git + + npm_registry: + name: npm-registry + endpoints: + - host: registry.npmjs.org + port: 443 + binaries: + - path: /usr/bin/npm + - path: /usr/bin/node + - path: /usr/local/bin/npm + - path: /usr/local/bin/node +``` + +## Next Steps + +- [Write Sandbox Policies](policies.md): The full iterative workflow for authoring, testing, and updating policies. +- {doc}`security-model`: Threat scenarios and how the four protection layers work together. diff --git a/docs/safety-and-privacy/policies.md b/docs/safety-and-privacy/policies.md new file mode 100644 index 00000000..d98f7b79 --- /dev/null +++ b/docs/safety-and-privacy/policies.md @@ -0,0 +1,213 @@ + + +# Write Sandbox Policies + +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. + +## What is a policy + +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. + +```yaml +version: 1 + +# Static: locked at sandbox creation. Paths the agent can read vs read/write. +filesystem_policy: + read_only: [/usr, /lib, /etc] + read_write: [/sandbox, /tmp] + +# Static: Landlock LSM kernel enforcement. best_effort uses highest ABI the host supports. +landlock: + compatibility: best_effort + +# Static: Unprivileged user/group the agent process runs as. +process: + run_as_user: sandbox + run_as_group: sandbox + +# Dynamic: hot-reloadable. Named blocks of endpoints + binaries allowed to reach them. +network_policies: + my_api: + name: my-api + endpoints: + - host: api.example.com + port: 443 + protocol: rest + tls: terminate + enforcement: enforce + access: full + binaries: + - path: /usr/bin/curl + +# Dynamic: hot-reloadable. Routing hints this sandbox can use for inference (e.g. local, nvidia). +inference: + allowed_routes: [local] +``` + +For the complete structure and every field, see the [Policy Schema Reference](../reference/policy-schema.md). + +## Network access rules + +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. + +### How network access is evaluated + +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). + +### Enable GitHub push + +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. + +Add this to your existing sandbox policy if you want to apply this GitHub permission set. + +```yaml + github_repos: + name: github_repos + endpoints: + - host: api.github.com + port: 443 + protocol: rest + tls: terminate + enforcement: enforce + rules: + # Read-only access to all GitHub API paths + - allow: + method: GET + path: "/**" + - allow: + method: HEAD + path: "/**" + - allow: + method: OPTIONS + path: "/**" + # GraphQL API (used by gh CLI for most operations) + - allow: + method: POST + path: "/graphql" + # alpha-repo: full write access + - allow: + method: "*" + path: "/repos//alpha-repo/**" + # bravo-repo: create + edit issues + - allow: + method: POST + path: "/repos//bravo-repo/issues" + - allow: + method: PATCH + path: "/repos//bravo-repo/issues/*" + binaries: + - { path: /usr/local/bin/claude } + - { 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. + +| 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. | + +:::{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. +::: + +## Create a Sandbox with a Custom Policy + +Pass a policy YAML file when creating the sandbox: + +```console +$ openshell sandbox create --policy ./my-policy.yaml --keep -- claude +``` + +The `--keep` flag keeps the sandbox running after the initial command exits, which is useful when you plan to iterate on the policy. + +To avoid passing `--policy` every time, set a default policy with an environment variable: + +```console +$ export OPENSHELL_SANDBOX_POLICY=./my-policy.yaml +$ 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 + +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. + +```{mermaid} +flowchart TD + A["1. Create sandbox with initial policy"] --> B["2. Monitor logs for denied actions"] + B --> C["3. Pull current policy"] + C --> D["4. Modify the policy YAML"] + D --> E["5. Push updated policy"] + E --> F["6. Verify the new revision loaded"] + F --> B + + style A fill:#76b900,stroke:#000000,color:#000000 + style B fill:#76b900,stroke:#000000,color:#000000 + style C fill:#76b900,stroke:#000000,color:#000000 + style D fill:#ffffff,stroke:#000000,color:#000000 + style E fill:#76b900,stroke:#000000,color:#000000 + style F fill:#76b900,stroke:#000000,color:#000000 + + linkStyle default stroke:#76b900,stroke-width:2px +``` + +**Steps** + +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. + + ```console + $ openshell logs --tail --source sandbox + ``` + +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`. + +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. + + ```console + $ openshell policy list + ``` + +## Next Steps + +- [Policy Schema Reference](../reference/policy-schema.md): Complete field reference for the policy YAML. +- [Security Model](security-model.md): Threat scenarios and protection layers. diff --git a/docs/safety-and-privacy/security-model.md b/docs/safety-and-privacy/security-model.md new file mode 100644 index 00000000..8108b564 --- /dev/null +++ b/docs/safety-and-privacy/security-model.md @@ -0,0 +1,86 @@ + + +# 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/community-sandboxes.md b/docs/sandboxes/community-sandboxes.md new file mode 100644 index 00000000..f176abdf --- /dev/null +++ b/docs/sandboxes/community-sandboxes.md @@ -0,0 +1,95 @@ + + +# Community Sandboxes + +Use pre-built sandboxes from the OpenShell Community catalog, or contribute your +own. + +## What Are Community Sandboxes + +Community sandboxes are ready-to-use environments published in the +[NemoClaw Community](https://github.com/NVIDIA/NemoClaw-Community) repository. +Each sandbox bundles a Dockerfile, policy, optional skills, and startup scripts +into a single package that you can launch with one command. + +## Current Catalog + +The following community sandboxes are available in the catalog. + +| Sandbox | Description | +|---|---| +| `base` | Foundational image with system tools and dev environment | +| `openclaw` | Open agent manipulation and control | +| `sdg` | Synthetic data generation workflows | +| `simulation` | General-purpose simulation sandboxes | + +## Use a Community Sandbox + +Launch a community sandbox by name with the `--from` flag: + +```console +$ nemoclaw sandbox create --from openclaw +``` + +When you pass `--from` with a community sandbox name, the CLI: + +1. Resolves the name against the + [NemoClaw Community](https://github.com/NVIDIA/NemoClaw-Community) repository. +2. Pulls the Dockerfile, policy, skills, and any startup scripts. +3. Builds the container image locally. +4. Creates the sandbox with the bundled configuration applied. + +You end up with a running sandbox whose image, policy, and tooling are all +preconfigured by the community package. + +### Other Sources + +The `--from` flag also accepts: + +- Local directory paths: Point to a directory on disk that contains a + Dockerfile and optional policy/skills: + + ```console + $ nemoclaw sandbox create --from ./my-sandbox-dir + ``` + +- Container image references: Use an existing container image directly: + + ```console + $ nemoclaw sandbox create --from my-registry.example.com/my-image:latest + ``` + +## Contribute a Community Sandbox + +Each community sandbox is a directory under `sandboxes/` in the +[NemoClaw Community](https://github.com/NVIDIA/NemoClaw-Community) repository. +At minimum, a sandbox directory must contain: + +- `Dockerfile`: Defines the container image +- `README.md`: Describes the sandbox and how to use it + +Optional files: + +- `policy.yaml`: Default policy applied when the sandbox launches +- `skills/`: Agent skill definitions bundled with the sandbox +- Startup scripts: Any scripts the Dockerfile or entrypoint invokes + +To contribute, fork the repository, add your sandbox directory, and open a pull +request. Refer to the repository's +[CONTRIBUTING.md](https://github.com/NVIDIA/NemoClaw-Community/blob/main/CONTRIBUTING.md) +for submission guidelines. + +:::{note} +The community catalog is designed to grow. If you have built a sandbox that +supports a particular workflow (data processing, simulation, code review, +or anything else), consider contributing it back so others can use it. +::: + +## Next Steps + +- {doc}`create-and-manage`: Full sandbox lifecycle management +- {doc}`custom-containers`: Build a fully custom container with BYOC +- {doc}`../safety-and-privacy/policies`: Customize the policy applied to any sandbox diff --git a/docs/sandboxes/create-and-manage.md b/docs/sandboxes/create-and-manage.md new file mode 100644 index 00000000..f9653d1b --- /dev/null +++ b/docs/sandboxes/create-and-manage.md @@ -0,0 +1,195 @@ + + +# Create and Manage Sandboxes + +This page walks you through the full sandbox lifecycle: creating, inspecting, connecting to, monitoring, and deleting sandboxes. For background on what sandboxes are and how the runtime works, refer to [About Sandboxes](index.md). + +## Prerequisites + +Ensure the following are installed before creating sandboxes. + +- OpenShell CLI installed (`pip install nemoclaw`) +- Docker running on your machine + +## Create a Sandbox + +The simplest way to create a sandbox is to specify a trailing command: + +```console +$ nemoclaw sandbox create -- claude +``` + +The CLI bootstraps the runtime (if this is your first run), discovers your +credentials, applies the default policy, and drops you into the sandbox. + +You can customize creation with flags like `--name`, `--provider`, `--policy`, +`--upload`, `--keep`, `--forward`, and `--from`. Refer to the +[CLI Reference](../reference/cli.md) for the full flag list. + +A fully specified creation command might look like: + +```console +$ nemoclaw sandbox create \ + --name dev \ + --provider my-claude \ + --policy policy.yaml \ + --upload \ + --keep \ + -- claude +``` + +:::{tip} +Use `--keep` to keep the sandbox running after the trailing command exits. +This is especially useful when you are iterating on a policy or want to +reconnect later from another terminal or VS Code. +::: + +## List and Inspect Sandboxes + +Check the status of your sandboxes and retrieve detailed information about individual ones. + +List all sandboxes: + +```console +$ nemoclaw sandbox list +``` + +Get detailed information about a specific sandbox: + +```console +$ nemoclaw sandbox get my-sandbox +``` + +## Connect to a Sandbox + +Access a running sandbox through an interactive SSH session or VS Code Remote-SSH. + +### Interactive SSH + +Open an SSH session into a running sandbox: + +```console +$ nemoclaw sandbox connect my-sandbox +``` + +### VS Code Remote-SSH + +Export the sandbox SSH configuration and append it to your SSH config: + +```console +$ nemoclaw sandbox ssh-config my-sandbox >> ~/.ssh/config +``` + +Then open VS Code, install the Remote - SSH extension if you have not +already, and connect to the host named `my-sandbox`. + +## View Logs + +Stream and filter sandbox logs to monitor agent activity and diagnose policy decisions. + +Stream sandbox logs: + +```console +$ nemoclaw logs my-sandbox +``` + +Use flags to filter and follow output: + +| Flag | Purpose | Example | +|---|---|---| +| `--tail` | Stream logs in real time | `nemoclaw logs my-sandbox --tail` | +| `--source` | Filter by log source | `--source sandbox` | +| `--level` | Filter by severity | `--level warn` | +| `--since` | Show logs from a time window | `--since 5m` | + +Combine flags to narrow in on what you need: + +```console +$ nemoclaw logs my-sandbox --tail --source sandbox --level warn --since 5m +``` + +:::{tip} +For a real-time dashboard that combines sandbox status and logs in one view, +run `nemoclaw term`. Refer to {doc}`terminal` for details on reading log entries and +diagnosing blocked connections. +::: + +## Transfer Files + +Transfer files between your host machine and a running sandbox. + +Upload files from your host into the sandbox: + +```console +$ nemoclaw sandbox upload my-sandbox ./src /sandbox/src +``` + +Download files from the sandbox to your host: + +```console +$ nemoclaw sandbox download my-sandbox /sandbox/output ./local +``` + +:::{note} +You can also upload files at creation time with the `--upload` flag on +`nemoclaw sandbox create`. +::: + +## Port Forwarding + +Forward a port from the sandbox to your host machine. This runs in the +foreground by default: + +```console +$ nemoclaw forward start 8080 my-sandbox +``` + +Add `-d` to run the forward in the background: + +```console +$ nemoclaw forward start 8080 my-sandbox -d +``` + +List active port forwards: + +```console +$ nemoclaw forward list +``` + +Stop a port forward: + +```console +$ nemoclaw forward stop 8080 my-sandbox +``` + +:::{note} +You can set up port forwarding at creation time with the `--forward` flag on +`nemoclaw sandbox create`, which is convenient when you know upfront that +your workload exposes a service. +::: + +## Delete Sandboxes + +Remove sandboxes when they are no longer needed. Deleting a sandbox stops all processes, releases cluster resources, and purges injected credentials. + +Delete a sandbox by name: + +```console +$ nemoclaw sandbox delete my-sandbox +``` + +You can delete multiple sandboxes in a single command: + +```console +$ nemoclaw sandbox delete sandbox-a sandbox-b sandbox-c +``` + +## Next Steps + +- {doc}`community-sandboxes`: Use pre-built sandboxes from the community catalog +- {doc}`providers`: Create and attach credential providers +- {doc}`custom-containers`: Build and run your own container image +- {doc}`../safety-and-privacy/policies`: Control what the agent can access \ No newline at end of file diff --git a/docs/sandboxes/custom-containers.md b/docs/sandboxes/custom-containers.md new file mode 100644 index 00000000..6eb1583a --- /dev/null +++ b/docs/sandboxes/custom-containers.md @@ -0,0 +1,80 @@ + + +# Custom Containers + +Build a custom container image and run it as a OpenShell sandbox. + +## Prerequisites + +Ensure the following are installed before building custom container sandboxes. + +- OpenShell CLI installed (`pip install nemoclaw`) +- Docker running on your machine +- A Dockerfile for your workload + +## Step 1: Create a Sandbox from Your Dockerfile + +Point `--from` at the directory containing your Dockerfile: + +```console +$ nemoclaw sandbox create --from ./my-app --keep --name my-app +``` + +The CLI builds the image locally using Docker, pushes it into the cluster, and +creates the sandbox, all in one step. No external container registry is +needed. + +You can also pass a full container image reference if the image is already +built: + +```console +$ nemoclaw sandbox create --from my-registry.example.com/my-image:latest --keep --name my-app +``` + +## Step 2: Forward Ports + +If your container runs a service, forward the port to your host: + +```console +$ nemoclaw forward start 8080 my-app -d +``` + +The `-d` flag runs the forward in the background so you can continue using +your terminal. + +## Step 3: Iterate + +When you change your Dockerfile, delete the sandbox and recreate: + +```console +$ nemoclaw sandbox delete my-app && \ + nemoclaw sandbox create --from ./my-app --keep --name my-app +``` + +## Shortcut: Create with Forwarding and a Startup Command + +You can combine port forwarding and a startup command in a single step: + +```console +$ nemoclaw sandbox create --from ./my-app --forward 8080 --keep -- ./start-server.sh +``` + +This creates the sandbox, sets up port forwarding on port 8080, and runs +`./start-server.sh` as the sandbox command. + +:::{warning} +NemoClaw does not support distroless and `FROM scratch` images. The +supervisor requires glibc, `/proc`, and a shell to operate. Images missing +`iproute2` or required Linux capabilities will fail to start in proxy mode. +Ensure your base image includes these dependencies. +::: + +## Next Steps + +- {doc}`create-and-manage`: Full sandbox lifecycle commands +- {doc}`providers`: Attach credentials to your custom container +- {doc}`/safety-and-privacy/policies`: Write a policy tailored to your workload +- {doc}`/safety-and-privacy/security-model`: Understand the isolation layers applied to custom images \ No newline at end of file diff --git a/docs/sandboxes/index.md b/docs/sandboxes/index.md new file mode 100644 index 00000000..18a20d33 --- /dev/null +++ b/docs/sandboxes/index.md @@ -0,0 +1,55 @@ + + +# About Sandboxes + +A OpenShell sandbox is a safe, private execution environment for an AI agent. Each sandbox runs inside a Kubernetes pod with multiple layers of protection that prevent unauthorized data access, credential exposure, and network exfiltration. Protection layers include filesystem restrictions (Landlock), system call filtering (seccomp), network namespace isolation, and a privacy-enforcing HTTP CONNECT proxy. + +## Sandbox Lifecycle + +Every sandbox moves through a defined set of phases: + +| Phase | Description | +|---|---| +| Provisioning | The runtime is setting up the sandbox environment, injecting credentials, and applying your policy. | +| Ready | The sandbox is running. The agent process is active and all isolation layers are enforced. You can connect, sync files, and view logs. | +| Error | Something went wrong during provisioning or execution. Check logs with `nemoclaw logs` for details. | +| Deleting | The sandbox is being torn down. The system releases resources and purges credentials. | + +## The OpenShell Runtime + +Sandboxes run inside a lightweight runtime cluster that OpenShell manages for +you. The cluster runs as a [k3s](https://k3s.io/) Kubernetes distribution +inside a Docker container on your machine. + +You do not need to set this up manually. The first time you run a command +that needs a cluster (such as `nemoclaw sandbox create`), the CLI provisions +one automatically. This is the "Runtime ready" line you see in the output. +Subsequent commands reuse the existing cluster. + +For teams or when you need more resources, you can deploy the cluster to a +remote host instead of your local machine: + +```console +$ nemoclaw gateway start --remote user@host +``` + +Refer to [Remote Deployment](../about/architecture.md) for +details. If you have multiple clusters (local and remote), switch between them +with `nemoclaw gateway select `. Refer to the +[CLI Reference](../reference/cli.md#gateway-commands) for the full command set. + +## Next Steps + +- {doc}`create-and-manage`: Create, inspect, connect, monitor, and delete + sandboxes. +- {doc}`providers`: Create and attach credential providers so agents can + authenticate with external services. +- {doc}`custom-containers`: Build and run your own container image inside a + sandbox. +- {doc}`community-sandboxes`: Use pre-built sandboxes from the community + catalog. +- {doc}`terminal`: Monitor sandbox status and live activity in a terminal + dashboard. diff --git a/docs/sandboxes/providers.md b/docs/sandboxes/providers.md new file mode 100644 index 00000000..1dc7d26f --- /dev/null +++ b/docs/sandboxes/providers.md @@ -0,0 +1,165 @@ + + +# Providers + +AI agents typically need credentials to access external services: an API key for the AI model provider, a token for GitHub or GitLab, and so on. OpenShell manages these credentials as first-class entities called *providers*. + +Create and manage providers that supply credentials to sandboxes. + +## Create a Provider + +Providers can be created from local environment variables or with explicit credential values. + +### From Local Credentials + +The fastest way to create a provider is to let the CLI discover credentials from +your shell environment: + +```console +$ nemoclaw provider create --name my-claude --type claude --from-existing +``` + +This reads `ANTHROPIC_API_KEY` or `CLAUDE_API_KEY` from your current environment +and stores them in the provider. + +### With Explicit Credentials + +Supply a credential value directly: + +```console +$ nemoclaw provider create --name my-api --type generic --credential API_KEY=sk-abc123 +``` + +### Bare Key Form + +Pass a key name without a value to read the value from the environment variable +of that name: + +```console +$ nemoclaw provider create --name my-api --type generic --credential API_KEY +``` + +This looks up the current value of `$API_KEY` in your shell and stores it. + +## Manage Providers + +List, inspect, update, and delete providers from the active cluster. + +List all providers: + +```console +$ nemoclaw provider list +``` + +Inspect a provider: + +```console +$ nemoclaw provider get my-claude +``` + +Update a provider's credentials: + +```console +$ nemoclaw provider update my-claude --type claude --from-existing +``` + +Delete a provider: + +```console +$ nemoclaw provider delete my-claude +``` + +## Attach Providers to Sandboxes + +Pass one or more `--provider` flags when creating a sandbox: + +```console +$ nemoclaw sandbox create --provider my-claude --provider my-github -- claude +``` + +Each `--provider` flag attaches one provider. The sandbox receives all +credentials from every attached provider at runtime. + +:::{warning} +Providers cannot be added to a running sandbox. If you need to attach an +additional provider, delete the sandbox and recreate it with all required +providers specified. +::: + +### Auto-Discovery Shortcut + +When the trailing command in `nemoclaw sandbox create` is a recognized tool name (`claude`, `codex`, or `opencode`), the CLI auto-creates the required +provider from your local credentials if one does not already exist. You do not +need to create the provider separately: + +```console +$ nemoclaw sandbox create -- claude +``` + +This detects `claude` as a known tool, finds your `ANTHROPIC_API_KEY`, creates +a provider, attaches it to the sandbox, and launches Claude Code. + +## How Credentials Flow + +Credentials follow a secure path from your machine into the agent process. + +```{mermaid} +flowchart LR + A["You create a provider"] --> B["Attach provider\nto sandbox at creation"] + B --> C["Sandbox starts"] + C --> D["Supervisor fetches\ncredentials from gateway"] + D --> E["Credentials injected into\nagent process + SSH sessions"] + + style A fill:#ffffff,stroke:#000000,color:#000000 + style B fill:#ffffff,stroke:#000000,color:#000000 + style C fill:#76b900,stroke:#000000,color:#000000 + style D fill:#76b900,stroke:#000000,color:#000000 + style E fill:#76b900,stroke:#000000,color:#000000 + + linkStyle default stroke:#76b900,stroke-width:2px +``` + +1. You create a provider with credentials from your environment or + specified explicitly. +2. You attach the provider to a sandbox at creation time using the + `--provider` flag (one or more providers can be attached). +3. The sandbox starts. The supervisor process initializes. +4. The supervisor fetches credentials from the OpenShell gateway at runtime. + The system does not store credentials in the sandbox specification. It retrieves them on demand. +5. Credentials are injected into the agent process as environment variables. + They are also available in SSH sessions when you connect to the sandbox. + +:::{warning} +The system does not store credentials in the sandbox container specification. The supervisor fetches them at runtime and holds them only in process memory. This +means you cannot find credentials in container inspection, image layers, or +environment dumps of the container spec. +::: + +## Supported Provider Types + +The following provider types are supported. + +| Type | Environment Variables Injected | Typical Use | +|---|---|---| +| `claude` | `ANTHROPIC_API_KEY`, `CLAUDE_API_KEY` | Claude Code, Anthropic API | +| `codex` | `OPENAI_API_KEY` | OpenAI Codex | +| `opencode` | `OPENCODE_API_KEY`, `OPENROUTER_API_KEY`, `OPENAI_API_KEY` | opencode tool | +| `github` | `GITHUB_TOKEN`, `GH_TOKEN` | GitHub API, `gh` CLI | +| `gitlab` | `GITLAB_TOKEN`, `GLAB_TOKEN`, `CI_JOB_TOKEN` | GitLab API, `glab` CLI | +| `nvidia` | `NVIDIA_API_KEY` | NVIDIA API Catalog | +| `generic` | User-defined | Any service with custom credentials | +| `outlook` | *(none: no auto-discovery)* | Microsoft Outlook integration | + +:::{tip} +Use the `generic` type for any service not listed above. You define the +environment variable names and values yourself with `--credential`. +::: + +## Next Steps + +- {doc}`create-and-manage`: Full sandbox lifecycle management +- {doc}`custom-containers`: Use providers with custom container images +- {doc}`../safety-and-privacy/security-model`: Why credential isolation matters \ No newline at end of file diff --git a/docs/sandboxes/terminal.md b/docs/sandboxes/terminal.md new file mode 100644 index 00000000..43888e76 --- /dev/null +++ b/docs/sandboxes/terminal.md @@ -0,0 +1,110 @@ + + +# Terminal + +NemoClaw Terminal is a terminal dashboard that displays sandbox status and live activity in a single view. Use it to monitor agent behavior, diagnose blocked connections, and observe inference interception in real time. + +```console +$ nemoclaw term +``` + +## Sandbox Status + +The status pane at the top of the dashboard displays the following sandbox metadata: + +- Name and phase (`Provisioning`, `Ready`, `Error`) +- Image running in the sandbox +- Providers attached and their available credentials +- Age since creation +- Port forwards currently active + +A phase other than `Ready` indicates the sandbox is still initializing or has encountered an error. Inspect the logs pane for details. + +## Live Log Stream + +The logs pane streams activity in real time. Outbound connections, policy decisions, and inference interceptions appear as they occur. + +Log entries originate from two sources: + +- sandbox: The sandbox supervisor (proxy decisions, policy enforcement, SSH connections, process lifecycle). +- gateway: The control plane (sandbox creation, phase changes, policy distribution). + +Press `f` to enable follow mode and auto-scroll to new entries. + +## Diagnosing Blocked Connections + +Entries with `action=deny` indicate connections blocked by policy: + +``` +22:35:19 sandbox INFO CONNECT action=deny dst_host=registry.npmjs.org dst_port=443 +``` + +Each deny entry contains the following fields: + +| Field | Description | +|---|---| +| `action=deny` | Connection was blocked by the network policy. | +| `dst_host` | Destination host the process attempted to reach. | +| `dst_port` | Destination port (typically 443 for HTTPS). | +| `src_addr` | Source address inside the sandbox. | +| `policy` | Policy rule that was evaluated, or `-` if no rule matched. | + +To resolve a blocked connection: + +1. Add the host to the network policy if the connection is legitimate. Refer to {doc}`../safety-and-privacy/policies` for the iteration workflow. +2. Leave it blocked if the connection is unauthorized. + +## Diagnosing Inference Interception + +Entries with `action=inspect_for_inference` indicate intercepted API calls: + +``` +22:35:37 sandbox INFO CONNECT action=inspect_for_inference dst_host=integrate.api.nvidia.com dst_port=443 +22:35:37 sandbox INFO Intercepted inference request, routing locally kind=chat_completion +``` + +This sequence indicates: + +- No network policy matched the connection (the endpoint and binary combination is not in the policy). +- Inference routing is configured (`allowed_routes` is non-empty), so the proxy intercepted the call instead of denying it. +- The proxy TLS-terminated the connection, detected an inference API pattern, and routed the request through the privacy router. + +:::{note} +If these calls should go directly to the destination rather than through inference routing, the most likely cause is a binary path mismatch. The process making the HTTP call does not match any binary listed in the network policy. + +Check the log entry for the binary path, then update the `binaries` list in the policy. Refer to {doc}`../safety-and-privacy/network-access-rules` for details on binary matching. +::: + +## Filtering and Navigation + +The dashboard provides filtering and navigation controls: + +- Press `s` to filter logs by source. Display only `sandbox` logs (policy decisions) or only `gateway` logs (lifecycle events). +- Press `f` to toggle follow mode. Auto-scroll to the latest entries. +- Press `Enter` on a log entry to open the detail view with the full message. +- Use `j` / `k` to navigate up and down the log list. + +## Keyboard Shortcuts + +The following keyboard shortcuts are available in the terminal dashboard. + +| Key | Action | +|---|---| +| `j` / `k` | Navigate down / up in the log list. | +| `Enter` | Open detail view for the selected entry. | +| `g` / `G` | Jump to top / bottom. | +| `f` | Toggle follow mode (auto-scroll to new entries). | +| `s` | Open source filter (sandbox, gateway, or all). | +| `Esc` | Return to the main view / close detail view. | +| `q` | Quit. | + +## Related Topics + +For deeper dives into topics covered by the terminal dashboard, refer to the following guides. + +- Blocked connections: Follow {doc}`../safety-and-privacy/policies` to pull the current policy, add the missing endpoint, and push an update without restarting the sandbox. +- Inference interception: Refer to {doc}`../safety-and-privacy/network-access-rules` for the distinction between agent traffic (routed directly) and userland traffic (routed through inference routing). +- Troubleshooting: Refer to {doc}`../troubleshooting` for troubleshooting tips and diagnostics. diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md new file mode 100644 index 00000000..87d885f8 --- /dev/null +++ b/docs/troubleshooting.md @@ -0,0 +1,135 @@ + + +# Troubleshooting + +Use this guide to troubleshoot problems with OpenShell. + +## Cluster Issues + +Troubleshoot problems with deploying, connecting to, and running OpenShell clusters. + +### Cluster Deploy Fails + +**Symptom:** `nemoclaw gateway start` exits with an error. + +**Check:** +1. Is Docker running? The cluster requires Docker to be active. +2. Is the port already in use? Try a different port: `--port 8081`. +3. Does a stale container exist? Destroy and redeploy: `nemoclaw gateway destroy && nemoclaw gateway start`. + +### Cluster Not Reachable + +**Symptom:** `nemoclaw status` fails to connect. + +**Check:** +1. Is the cluster container running? `docker ps | grep nemoclaw`. +2. Was the cluster stopped? Redeploy: `nemoclaw gateway start`. +3. For remote clusters, is the SSH connection working? + +### Health Check Fails During Deploy + +**Symptom:** Deploy hangs or times out waiting for health checks. + +**Check:** +1. View container logs: `docker logs nemoclaw-cluster`. +2. Check if k3s started: the bootstrap process waits up to 180 attempts (six minutes) for cluster readiness. +3. Look for resource constraints. k3s needs sufficient memory and disk. + +## Sandbox Issues + +Troubleshoot problems with creating, connecting to, and configuring sandboxes. + +### Sandbox Stuck in Provisioning + +**Symptom:** Sandbox shows `Provisioning` status and does not become `Ready`. + +**Check:** +1. View sandbox logs: `nemoclaw logs --source gateway`. +2. Check if the container image can be pulled. +3. For custom images, verify the image was pushed: `nemoclaw sandbox image push`. + +### Cannot Connect to Sandbox + +**Symptom:** `nemoclaw sandbox connect ` fails. + +**Check:** +1. Is the sandbox in `Ready` state? `nemoclaw sandbox get `. +2. Is SSH accessible? The tunnel goes through the gateway. Verify cluster connectivity first. + +### Network Requests Denied + +**Symptom:** The agent cannot reach a remote host. + +**Check:** +1. Stream sandbox logs: `nemoclaw logs --tail --source sandbox`. +2. Look for `deny` actions. They include the destination, binary, and reason. +3. Update the policy to allow the blocked endpoint. Refer to [](safety-and-privacy/policies.md). + +### Policy Update Fails + +**Symptom:** `nemoclaw policy set` returns an error or the status shows `failed`. + +**Check:** +1. Are you changing a static field? `filesystem_policy`, `landlock`, and `process` cannot change after creation. +2. Are you adding/removing `network_policies` to change the network mode? This is not allowed. The mode is fixed at creation. +3. Check the error message in `nemoclaw policy list `. + +## Provider Issues + +Troubleshoot problems with provider credential discovery and injection into sandboxes. + +### Provider Discovery Finds No Credentials + +**Symptom:** `--from-existing` creates a provider with no credentials. + +**Check:** +1. Are the expected environment variables set? (for example, `ANTHROPIC_API_KEY` for Claude). +2. Do the expected config files exist? (for example, `~/.claude.json`). +3. Try explicit credentials: `--credential ANTHROPIC_API_KEY=sk-...`. + +### Sandbox Missing Credentials + +**Symptom:** Environment variables for a provider are not set inside the sandbox. + +**Check:** +1. Was the provider attached? `nemoclaw sandbox get `. Check the providers list. +2. Does the provider have credentials? `nemoclaw provider get `. +3. Are the credential keys valid env var names? Keys with dots, dashes, or spaces are silently skipped. + +## Custom Container Issues + +Troubleshoot problems with building and running custom container images in sandboxes. + +### Custom Image Fails to Start + +**Symptom:** Sandbox with `--from ` goes to `Error` state. + +**Check:** +1. Is the image pushed to the cluster? `nemoclaw sandbox image push --dockerfile ./Dockerfile --tag my-image`. +2. Does the image have glibc and `/proc`? Distroless / `FROM scratch` images are not supported. +3. For proxy mode, does the image have `iproute2`? Network namespace setup requires it. + +## Port Forwarding Issues + +Troubleshoot problems with forwarding local ports into sandbox services. + +### Port Forward Not Working + +**Symptom:** `localhost:` does not connect to the sandbox service. + +**Check:** +1. Is the forward running? `nemoclaw forward list`. +2. Is the service listening on that port inside the sandbox? +3. Is the sandbox still in `Ready` state? +4. Try stopping and restarting: `nemoclaw forward stop && nemoclaw forward start -d`. + +## Getting More Information + +Use these techniques to gather additional diagnostic detail when troubleshooting. + +- Increase CLI verbosity: `nemoclaw -vvv ` for trace-level output. +- View gateway-side logs: `nemoclaw logs --source gateway`. +- View sandbox-side logs: `nemoclaw logs --source sandbox --level debug`. diff --git a/docs/troubleshooting/index.md b/docs/troubleshooting/index.md deleted file mode 100644 index 093695e1..00000000 --- a/docs/troubleshooting/index.md +++ /dev/null @@ -1,6 +0,0 @@ - - -# Troubleshooting