OpenShell deep dive: NVIDIA's runtime for autonomous AI agents — not approval, not a container, a policy-bounded execution environment
I. At a glance
★★★★☆ (4/5). A Rust project from NVIDIA, Apache-2.0, 8.7k stars / 1.3k forks (as of 2026-09-22). It fills the gap left when you strip away agentgate-style approval workflows and SkillSpector-style skill auditing: a policy-bounded runtime for autonomous AI agents. The agent is not waiting for approval — it runs inside an isolated container wrapped in four layers of declarative policy, and every outbound request passes through a three-state gate (allow / bind credentials / deny). One star off because 0.1.0 has not shipped yet (the repo is alpha and the very first line of the README says “OpenShell 0.1.0 is coming soon”), and the 403 open issues show this is a project still moving fast — don’t put it in production, but research / internal / controlled environments can absolutely try it.
| Main language | Rust (core + Gateway), with SDKs in Python / TypeScript / Go / Rust |
| License | Apache-2.0 |
| Stars / Forks | 8,746 / 1,269 |
| First release | 2026-02-24 (less than six months ago) |
| Last commit | 2026-09-22 (still iterating at high frequency) |
| Open issues | 403 (typical for an early-stage project; the README itself expects many before 0.1.0) |
| Compute backends | Docker / Podman / MicroVM / Kubernetes |
| Network policy granularity | L7 (HTTP method + path) |
| Repo | https://github.com/NVIDIA/OpenShell |
Who is it for (per-audience score, 5-point scale):
| Audience | Score | Why |
|---|---|---|
| Enterprise security operations / CISO | 5 | One of the few “gate before letting an agent touch a production system” products out there. Endpoint-bound credentials and method/path-level network controls are dramatically safer than handing an agent an API key and hoping |
| AI security researcher | 5 | The four-layer policy model, endpoint-bound credentials, and L7 policy enforcement are all great research material. The repo ships a ready-made target range at examples/sandbox-policy-quickstart/ |
| Red team / pentester | 3 | Not an offensive tool, but it can host Claude Code / Codex / OpenCode as opponents — useful for adversarial testing like “can I push the agent past its policy from inside the sandbox” |
| CTF player | 2 | Not directly relevant to CTFs, but the walkthroughs under examples/ are good practice for tuning L7 policy by feel |
| Hobbyist / self-hoster | 3 | Easy to install (openshell sandbox create is one command), but the alpha status means the official guidance is still to run it on a dev cluster, not against a production database |
Forks (Apache-2.0, three difficulty tiers):
| Tier | What you can do |
|---|---|
| Tweak config | Add a new provider profile (openshell provider profile import); write a sandbox policy YAML for your use case; ship a custom image for a supported agent |
| Tweak integrations | Write a new compute driver (the crates/ directory has reference implementations for container / MicroVM / K8s); add a new SDK language |
| Tweak the kernel | Modify the policy engine (it spans filesystem / network / process / provider and is the most interesting part to read); modify the Gateway control plane |
II. What it is, what it isn’t
Before you open https://github.com/NVIDIA/OpenShell, three clarifications, because OpenShell is not several things that share the same neighborhood:
Not an approval workflow. agentgate — same broad category of “AI Agent control” — takes the approach: agent wants to act, dashboard / Slack / Discord approves. OpenShell goes the opposite way: deny by default, allow by policy. The agent is not applying for permission; it is running inside a container already surrounded by policy. Want to hit the GitHub API? Check the YAML first.
Not an agent vulnerability scanner. NVIDIA/SkillSpector scans agent skills for prompt injection or dangerous commands — entry point is the skill file. OpenShell doesn’t scan; it manages runtime. Assume your skills are clean and your agent is well-behaved; OpenShell adds the “what if it goes off the rails” safety net.
Not a CALDERA-style BAS. CALDERA is MITRE’s red-team automation platform (adversary emulation). OpenShell is a blue-team tool for “letting the agent act inside a smaller, trusted boundary.” They face opposite directions — OpenShell assumes you are defending production and the agent is a “trusted-but-needs-constraints” subject, not an attacker you are simulating.
So what is OpenShell? The official one-liner:
OpenShell is the safe, private runtime for autonomous AI agents. It provides sandboxed execution environments that protect your data, credentials, and infrastructure — governed by declarative YAML policies that prevent unauthorized file access, data exfiltration, and uncontrolled network activity.
Three keywords: sandboxed execution / declarative YAML policies / protect data credentials infrastructure.
III. Four protection layers × four components
OpenShell’s architecture splits cleanly into “four components managing four protection layers,” but the interaction sequence and responsibility boundaries matter more than the table itself.
Four components (README):
| Component | Role |
|---|---|
| Gateway | Control-plane API, owns sandbox lifecycle, acts as the auth boundary |
| Sandbox | Isolated runtime with container supervision and policy-enforced egress routing |
| Policy Engine | Enforces filesystem, network, and process constraints from application layer down to kernel |
| Provider Access | Profile-defined endpoints, binary policy, and endpoint-bound credential injection for model APIs and other services |
The Gateway is the control plane, not the policy enforcement point. The architecture docs are explicit: the Gateway exposes gRPC APIs (lifecycle, provider management, policy updates, logs, watch streams, relay forwarding) and HTTP endpoints (health, WebSocket tunnels, edge-auth), but it does not make per-request network policy decisions — those happen inside the sandbox, in the supervisor + proxy. The Gateway only stores and delivers policy; actual enforcement occurs at the kernel layer. This design lets the Gateway scale horizontally without becoming a performance bottleneck, and it avoids the “policy is outside, agent is inside” routing problem.
Sandbox Readiness state machine. The Gateway composes driver-reported status with supervisor session state into the externally visible SandboxPhase:
1 | backend_phase = derive_phase(driver_status) |
For supervisor-controlled drivers (Docker, Podman, VM), the driver saying Ready is not enough — the supervisor gRPC session must also be established before the sandbox is Ready. For Kubernetes (a driver-reports-runtime-readiness driver), the driver’s Ready is authoritative. This distinction determines how long you wait after openshell sandbox create before you can connect — local Docker is usually seconds; Kubernetes may wait for Pod scheduling + image pull.
Policy Engine’s 6-step network decision flow. All outbound traffic inside the sandbox is forced through the proxy, which decides in this order (architecture/security-policy.md):
- Force proxy — namespace + seccomp controls ensure traffic can only go through the proxy;
- Binary identity — identify the requesting process binary path and compare against the
binarieswhitelist in policy; - Hard-block — reject dangerous internal IP ranges (e.g., 169.254.x.x, 10.x.x.x) unless explicitly allowed;
- Policy match — match destination + port + binary against network policy blocks;
- L7 rules — for endpoints with protocol inspection enabled, check HTTP method + path;
- Action — allow / deny / audit / log. Explicit deny and hardening checks win over allow; no match → deny.
Four protection layers (defense in depth):
| Layer | What it protects | When it locks |
|---|---|---|
| Filesystem | Prevents reads/writes outside allowed paths | Locked at sandbox creation |
| Network | Blocks unauthorized outbound connections | Hot-reloadable at runtime |
| Process | Blocks privilege escalation and dangerous syscalls | Locked at sandbox creation |
| Providers | Grants endpoint-bound credentials and network access | Hot-reloadable at runtime |
Why are Filesystem and Process locked at creation while Network and Providers can hot-reload? Because the first two are hard to walk back once granted (a file may have been copied somewhere else, an escalated process can spawn any child process), while the latter two are more like temporary passes — adding GitHub API to the network whitelist, attaching a new model profile for a different provider — both can be loaded live.
IV. Network policy and credential model (L7 + endpoint-bound)
The two pieces worth expanding on are network policy and credential model, because these are the biggest gap between OpenShell and “just another Docker sandbox.”
Network: L7 granularity, minimal egress by default
The Quickstart in the README shows a full “deny, then allow” cycle:
1 | # 1. Create a sandbox (starts with minimal outbound access) |
Three details worth noting:
- The 403 comes from the proxy, not from GitHub — the agent cannot get out at the network layer, regardless of DNS / IP / HTTPS tricks;
- GET is allowed, POST is blocked — the policy is not a domain allow/deny list, it is “GET to https://api.github.com is allowed, POST is not.” That is L7 granularity, a big step up from most “egress allowlist” products that stop at L4 (IP + port);
- Hot-reload does not require restarting the sandbox — the sandbox keeps running while
openshell policy set ... --waitapplies the new policy.
Policy YAML field structure
The full structure of examples/sandbox-policy-quickstart/policy.yaml is the starting point for understanding L7 policy:
1 | version: 1 |
Key fields:
version: 1— the only supported version; the parser rejects unknown fields with no permissive parsing mode;filesystem_policy— static filesystem rules;read_only/read_writelists determine what paths the agent can touch;landlock— Linux Landlock LSM compatibility setting;best_effortmeans the sandbox starts even if the kernel does not support Landlock (with a warning logged);network_policies.<key>— named policy blocks, each containingendpoints(target host/port/protocol/access) andbinaries(process whitelist).
Host wildcard rules. endpoints[].host supports * wildcards, but with two hard constraints: only in the first DNS label, or as an entire middle label. The OPA runtime matches with a . label boundary, so *.github.com matches api.github.com, but api.*.com does not. The validator enforces this boundary at policy load time, preventing silent mismatches at the proxy.
Landlock implementation details
OpenShell uses Linux Landlock LSM for filesystem enforcement, not just chroot:
- Inode-type-distinguished rights. Landlock rules assign permissions based on the inode type reported by the already-opened path descriptor: directories keep directory + file rights; regular files, device nodes, and sockets keep only file-compatible rights. This lets mixed-path policies (e.g.,
/devcontaining both directories and device nodes) succeed without weakeninghard_requirement. - Baseline path enrichment. Before applying Landlock, the supervisor auto-supplements baseline paths the runtime needs (e.g.,
/proc,/dev/urandom). Missing baseline paths are skipped rather than invalidating the entire ruleset. - GPU special handling. When GPU is enabled, the supervisor adds existing GPU device nodes as read-write paths and promotes
/procto read-write because CUDA workloads write thread metadata under/proc/<pid>/task/<tid>/comm.
Credentials: never touch disk, bound to endpoints
OpenShell abstracts all API keys / tokens / service accounts as Providers (named credential bundles). Provider declarations come from provider profiles. Here is providers/anthropic.yaml:
1 | id: anthropic |
And providers/claude-code.yaml, showing the complexity of an agent-type profile:
1 | id: claude-code |
Key design choices:
| Property | Meaning |
|---|---|
| Credentials never land on the sandbox filesystem | The agent cannot cat ~/.aws/credentials to exfiltrate them |
| Credentials are injected as environment variables, bound to the profile’s authorized endpoints | Even if the agent reads the env var, it only works against the authorized target — stealing a key to send to another service does not work |
| Profiles are import-only; the gateway ships none of its own | Before deploying, you must have reviewed and imported every profile; no “OpenShell bundles a sample credential” surprise |
| The CLI can auto-discover credentials from your shell environment | You don’t have to type every key in, but you do need to have already exported them in your shell |
binaries is the least-privilege control |
Even if endpoint and credential match, a requesting process whose path is not in the binaries list is rejected |
“Endpoint-bound” is the biggest difference from a regular secret manager: a secret manager hands the agent the full key and lets it go, OpenShell only gives the agent a temporary use of the key for the specific request the policy admits, and the moment the request ends, the binding is gone. The binaries field adds a second layer: even if the key is read, only specific processes can use it.
V. SDKs and ecosystem
Four official SDKs
| Language | Package | Install |
|---|---|---|
| Python | openshell (PyPI) |
uv add openshell |
| TypeScript | @nvidia/openshell-sdk (GitHub Packages) |
npm install @nvidia/openshell-sdk (configure the @nvidia scope to point at npm.pkg.github.com first) |
| Go | github.com/NVIDIA/OpenShell/sdk/go |
go get github.com/NVIDIA/OpenShell/sdk/go@latest |
| Rust | openshell-sdk (currently source-only) |
cargo add openshell-sdk --git ... --tag <release-tag> |
Note the Rust SDK is still source-only — the README explicitly says “pin the Git dependency to the same OpenShell release as the gateway.” With 0.1.0 unreleased, the crate has not landed on crates.io.
Officially supported agents
| Agent | Integration |
|---|---|
| Claude Code | Package into a workload image and attach a claude-code provider or another endpoint-bearing model profile |
| OpenCode | Package into an image and attach its model provider and policy |
| Codex | Same idea, with an OpenAI provider |
| GitHub Copilot CLI | Package into an image and attach GitHub credentials and policy |
| OpenClaw | Use the NemoClaw blueprint |
| Hermes Agent | Use the NemoClaw blueprint |
These are not “OpenShell takes over the agent” — they are “OpenShell knows the agent’s binary paths, required endpoints, and credential types, and ships profiles pre-tuned for them.” Any other agent (custom, in-house SDK) can also run — you write the image, write the profile, declare the endpoint.
Agent Skills dual-layer system
OpenShell splits skills into two layers, embodying its “agent-first” design philosophy:
Public skills (skills/ directory, user-installable):
openshell-cli— CLI workflow assistancedebug-openshell-cluster— Gateway cluster troubleshootingdebug-inference— Inference endpoint troubleshootinggenerate-sandbox-policy— Auto-generate sandbox policy
Install:
1 | npx skills add NVIDIA/OpenShell |
Contributor / Maintainer skills (.agents/skills/ directory, not distributed with public skills):
create-spike→state:accepted→ optionalagent:*planning and implementation workflowtriage-issue— agent assesses technical validity and impact; humans decide whether to schedulereview-security-issue/fix-security-issue— security assessment and remediationsync-agent-infra,update-docs-from-commits— repository maintenance
The README states the project is “built agent-first” — not only does it provide agent tools to users, its own development workflow is agent-driven. Contributor skills are not for end users, but they signal NVIDIA’s bet on agent-driven development.
How to put an agent inside
1 | # The default image contains only Ubuntu 24.04 — no agent CLI. |
“Installing OpenShell” and “having an agent run inside OpenShell” are two different things. OpenShell provides isolation and policy; you are responsible for putting the agent into the image. That is a deliberate responsibility split: the runtime does not take the blame for an agent that misbehaves.
Terminal UI
openshell term launches a k9s-style real-time terminal dashboard that auto-refreshes every 2 seconds:
Tabswitches panelsj/kmoves up/downEnterselects:enters command mode
The TUI is not cosmetic — it is a terminal frontend for the Gateway gRPC watch stream, showing real-time sandbox status, provider attachments, and policy decision logs (action=allow in green / action=deny in red).
VI. Model freedom / compute backends / GPU
Four compute drivers compared
OpenShell’s compute runtimes do not evaluate policy themselves; they only start and stop workloads per the Gateway’s spec. But the four drivers differ significantly (architecture/compute-runtimes.md):
| Driver | Isolation | Characteristics |
|---|---|---|
| Docker | Container | Easiest for local demos; supervisor and workload share Docker network, but the proxy forces all outbound |
| Podman | Container (rootless) | Paired container design: workload runs with network=none, supervisor joins the configured network separately; they communicate via gRPC over a Unix socket on a named volume; both run non-root with all capabilities dropped |
| MicroVM | Virtual machine | Strongest isolation (container-escape protection); uses host virtualization, supervisor outside the VM; early MicroVM implementations only — Firecracker / gVisor coverage is incomplete |
| Kubernetes | Pod | Deploy Gateway via Helm chart; supervisor.sandboxRuntime.networkPolicyEnforced=true enables network policy; fits existing k8s clusters |
The Podman driver deserves its own paragraph. Unlike Docker, Podman uses the libpod API to start two containers simultaneously — one workload (the agent runs inside) and one supervisor (runs outside). The workload container has no network at all (network=none); all outbound must go through the supervisor container’s proxy. The two containers share only a user namespace (for volume ownership), not PID, mount, or network namespace. This means even if the workload container is compromised, the attacker has no network access — because the workload container has no network interface.
The 7 requirements of the Driver Contract (from the architecture docs):
- Select the sandbox image;
- Resolve an immutable non-root sandbox identity before workload creation;
- Supply separate bootstrap material for workload and supervisor;
- Deliver
openshell-sandboxonly to the workload,openshell-supervisoronly to the supervisor; - Provide a protected control channel (Unix socket / TLS TCP / vsock);
- Forward the exact main-process argv and TTY mode without shell reconstruction;
- Report lifecycle events and clean up runtime-owned resources.
What you can and cannot change
| Dimension | What you can change | What you cannot |
|---|---|---|
| Compute backend | Switch between Docker / Podman / MicroVM / Kubernetes | No Firecracker / gVisor beyond the early MicroVM integration |
| Workload image | Bring your own container (BYOC), pull from a registry, ship custom images | The image must contain the agent — the default Ubuntu image is bare-bones |
| GPU | --gpu flag enables GPU passthrough (experimental) |
Requires NVIDIA drivers + NVIDIA Container Toolkit on the host, plus GPU libraries inside the image |
| Policy | Network and Providers can be hot-reloaded | Filesystem and Process are immutable after creation (this is intentional) |
| Credential injection | Profiles can declare endpoints and binaries freely | Credentials cannot be injected into endpoints not registered in the profile |
| Telemetry | Can be compiled out at build time | The official support matrix does not cover builds with telemetry disabled |
GPU passthrough deserves its own paragraph. openshell sandbox create --gpu ... will attempt CDI (Container Device Interface) first and fall back to Docker’s --gpus all if CDI is not available. The documentation is refreshingly honest: “Expect rough edges and breaking changes.” It is genuinely experimental — do not stake production on it.
Telemetry compilation options
Telemetry is not a single on/off switch; OpenShell offers three levels of control:
1. Runtime disable (gateway level, propagated to sandboxes):
1 | OPENSHELL_TELEMETRY_ENABLED=false openshell gateway ... |
2. Compile-time removal (produces binaries with no telemetry endpoint, HTTP client, or emission code):
1 | cargo build --release -p openshell-gateway \ |
Note: --no-default-features must be paired with defaults-without-telemetry; passing --features defaults-without-telemetry alone fails because Cargo does not support “subtract a single default feature.”
3. Per-driver trimming. Gateway Cargo features also include compute-driver-kubernetes, compute-driver-docker, compute-driver-podman, compute-driver-vm, and compute-driver-mxc (Windows). Compile only the drivers you need:
1 | # Docker only, with telemetry |
Telemetry data scope is also documented precisely: only anonymous operational categories and counts (sandbox lifecycle outcomes, provider profile buckets, policy decision counts, network denial categories). It never collects sandbox names/IDs, hostnames, file paths, binary paths, prompts, credentials, provider names, model names, or user content.
VII. Boundaries and risks
Before you push OpenShell toward production, a few facts you need to know:
1. 0.1.0 has not shipped. The first line of the README is > [!IMPORTANT] OpenShell 0.1.0 is coming soon. The repo is alpha, which means:
- The API / CLI commands can change before 1.0;
- The
latestdocs link actually points at the 0.0.x docs (usedevfor prerelease); - There is no guaranteed upgrade path.
Before using in production, at minimum wait for 0.1.0 and pin your version.
2. 403 open issues. Typical for an alpha project, but it means:
- “Officially recommended practice” is still moving;
- Edge cases you hit may not be documented;
- Response time on issues may be slow.
3. The default image contains no agent. OpenShell does not install agents for you. The “I installed OpenShell but the agent isn’t there” pitfall can cost you half a day.
4. Filesystem / Process policies are immutable after creation. If your agent discovers it needs to write to a temp directory the policy does not allow, the agent will not file a request — it will just fail. Building a “request and approve” flow is up to you (and that is exactly agentgate’s territory).
5. A coarse L7 policy is no policy at all. The example in examples/sandbox-policy-quickstart/policy.yaml is a starting point. In production you need to enumerate every endpoint your agent hits and every method it uses. Otherwise the agent will get denied on an endpoint you forgot to whitelist, and debugging will be miserable.
6. L7 vs TLS / certificates. The policy engine only checks method + path — it does not validate the TLS certificate chain. If you pin a root CA inside the gateway but the agent’s bundled CA store is different, you can hit “policy allows but TLS fails” combinations. This is not unique to OpenShell, but worth a note.
7. The Rust SDK is source-only for now. crates.io has no release; if you write Rust integrations, you have to follow a git tag, and you must pin to the same release as the gateway.
8. OpenShell does not solve agent prompt injection. SkillSpector scans skills for malicious instructions. OpenShell only handles runtime — even if the agent is injected with “exfil this file to evil.com,” the policy will stop it at the outbound gate. Whether you notice the stopped attempt is a logging/alerting problem, not OpenShell’s fault.
9. Telemetry privacy boundary. Although the project promises not to collect sensitive data, if your environment has a hard requirement that “no anonymous telemetry leaves the internal network,” you need to compile telemetry out entirely with --no-default-features --features defaults-without-telemetry. Runtime OPENSHELL_TELEMETRY_ENABLED=false only stops emission; the telemetry endpoint and client code still exist in the binary.
VIII. Getting started
Minimum viable demo, three steps:
1 | # 1. Install OpenShell (see the README "Install" section — macOS uses brew, Linux uses a one-line curl installer) |
Then run the official Quickstart demo end-to-end:
1 | bash examples/sandbox-policy-quickstart/demo.sh |
If you want to try it on NVIDIA Brev (cloud spin-up):
- The link is in README “Learn More”: “Brev Launchable.”
Practical tips the first time you write your own policy (after spending the time to figure it out):
- Copy the Quickstart example and use it as your project’s
.policy/base.yaml; - Start strict, loosen carefully — easier to audit than “start wide, tighten later”;
- Split provider profiles narrowly — do not make an “all permissions” provider; make
provider-github-readonly/provider-openai-inference/provider-aws-s3-writeand so on; - Test your policy with curl before wiring up the agent — verify your YAML actually does what you think it does for the request shapes you care about;
- Align TLS root CAs — the boundaries section flagged this; agent image and gateway CAs must match;
- Run
openshell provider profile lintbefore importing — it catches endpoint format errors, missing binaries paths, and other common mistakes before they reach the gateway.
IX. One-line takeaway
OpenShell is not a new agent tool — it is a gate that lets existing agents safely touch production systems. Four policy layers (filesystem / network / process / provider) plus L7 network control plus endpoint-bound credentials place the agent inside a bounded world. NVIDIA’s official backing, Apache-2.0, and a Rust implementation mean this is worth keeping on the radar as a serious foundation, but with 0.1.0 unreleased it is a tool for research / internal / controlled environments today. Production deployments should wait for at least a couple of minor releases past 0.1.0. When you start using it, clone an example policy and write your own first before plugging in an agent — skipping that step will cost you an hour on TLS / endpoint matching, just like it did me.