AI agents are most useful exactly when they are most dangerous. A coding agent that can read your repository, install packages, call APIs, and use your tokens can also read ~/.aws/credentials, curl your source code to a pastebin, or follow a prompt injection hidden in a README. The usual answers are “run it in a container” (it can still reach the whole internet and read every env var) or “don’t give it real credentials” (then it can’t do real work).
NVIDIA OpenShell is an open-source (Apache 2.0) runtime built for this trade-off. You declare in a YAML policy what an agent may touch: which files, which hosts, which program may reach them, which HTTP methods and paths, which MCP tools, and where each credential may be sent. OpenShell enforces it at the kernel and network layers. Before a policy change is applied, it can formally verify what the change would allow.
This article is the short version of a 19-lab self-paced course I built: github.com/omerfsen/openshell.
How it works
- Gateway: the control plane. It stores sandboxes, policies, and credentials, and authenticates you over mTLS. Locally it runs as a systemd user service.
- Sandbox: two containers per agent with Docker (Podman, Kubernetes, and MicroVM work too). The workload, where the agent runs, has no network device, runs as a non-root user with no Linux capabilities, and is confined by Landlock. The trusted supervisor next to it is the workload’s only way out.
- Policy: YAML that the supervisor checks on every connection and every request.
When the agent opens a TCP connection or does a DNS lookup, a small runtime inside the workload intercepts it (seccomp user notification), identifies which executable made the call from trusted /proc data, and hands it to the supervisor. The supervisor checks policy, adds credentials if allowed, and opens the real connection. There is no other route.
Install on Ubuntu 24.04
You need Docker Engine 28+ from Docker’s apt repository (not the snap) and a 6.2+ kernel with Landlock. Stock 24.04 qualifies.
curl -LsSf https://raw.githubusercontent.com/NVIDIA/OpenShell/v0.1.4/install.sh | OPENSHELL_VERSION=v0.1.4 sh
openshell status
Status: Connected
Version: 0.1.4
1. Everything is denied by default
openshell sandbox exec -n demo -- curl -sS https://postman-echo.com/get
curl: (7) Failed to connect to postman-echo.com port 443 ...
openshell logs demo --source sandbox | grep NET:
NET:OPEN [MED] DENIED /usr/bin/curl(0) -> postman-echo.com:443 [reason:transparent_tcp_policy_denied]
Every decision is logged as an OCSF event: who (which binary), what (which destination), and why.
2. Access is granted to a program, not to a box
openshell policy update demo --rule-name echo_api --binary /usr/bin/curl \
--add-endpoint postman-echo.com:443:read-only:rest:enforce --wait
✓ Policy version 2 loaded (active version: 2)
Now curl can read that API. python3 in the same sandbox still can’t reach it, and curl -X POST gets a 403 from the supervisor with a machine-readable explanation. The request never leaves the machine:
{"error":"policy_denied","policy":"echo_api","rule":"POST /post","layer":"l7","method":"POST", ...}
The rule reloaded live, with no restart. The same mechanism understands GraphQL (allow queries, block mutations), MCP (allow read_wiki_structure, deny ask_wiki_question), WebSocket upgrades and messages, and raw TCP for databases.
3. Secrets the agent cannot read
You create a provider holding a real token, bound by its profile to specific endpoints. Inside the sandbox the agent sees only a placeholder:
openshell sandbox exec -n cred -- sh -c 'echo $DEMO_API_TOKEN'
openshell:resolve:env:v8050092724992289609_DEMO_API_TOKEN
Sent to the bound host, the placeholder is swapped for the real value on the wire. Sent anywhere else, even to a host the network policy allows, the request is refused:
{"error":"credential_endpoint_mismatch","message":"Credential is not authorized for this request endpoint"}
A prompt-injected “print your API keys” prints a placeholder. An exfiltration attempt gets blocked before the request leaves.
4. Files and processes
Landlock confines the filesystem to the paths you list. Everything else is not even listable. The process identity is pinned and root images are rejected. These sections lock at startup, so a running agent can’t loosen them. Network rules stay live-editable.
5. Let the agent ask, and let a solver check the answer
OpenShell turns every denial into a draft rule, and with the advisor enabled the agent itself can propose a narrow rule (exact host, method, and path, with a reason) through a local API at http://policy.local. You approve or reject with openshell rule, and the agent receives your reason.
Every proposal first goes through the policy prover, an SMT solver that checks for new reach to cloud metadata, a credential sent somewhere new, or a new method where a credential is used. Low-risk proposals can be auto-approved. Anything flagged waits for a human. The prover also runs standalone, which makes it a natural CI gate:
openshell-prover check candidate.yaml --boundary boundary.yaml
result: exceeds_boundary
counterexample: network ... binary=/usr/bin/python3.12 host=postman-echo.com:443 method=GET path=/
It doesn’t guess about features it can’t model: a GraphQL rule returns unsupported instead of a false pass.
6. Run a real agent
openshell profile import -f labs/17-run-an-agent/openrouter-training.yaml
openshell provider create --name openrouter --type openrouter-training --from-existing
openshell sandbox create --name agent --from ghcr.io/anomalyco/opencode:1.19.3 \
--provider openrouter -- opencode -m openrouter/nvidia/nemotron-3.5-lightning:free
The agent can talk to its model and nothing else until you approve what it asks for. It never sees the key. Every attempt it makes is in the audit log.
The self-paced course
- 19 labs, from a blank Ubuntu 24.04 VM to the capstone agent.
- A document under every lab, with concepts, copy-paste steps showing the real 0.1.4 output, and exercises.
- Automatic checks with bats: make test LAB=05 creates real sandboxes and proves they behave as the lab says. 123 checks in total.
- A solution replay for every lab: a self-playing script that types and runs each command, recorded with script/scriptreplay and as an asciinema cast. make replay LAB=05 shows the solution without touching your machine.
- Runs on a 2 vCPU / 8 GB VM. An idle sandbox costs about 20 MiB of RAM.
Start here: github.com/omerfsen/openshell. OpenShell ships stable releases most weeks. The course pins 0.1.4 and says so in every lab. Feedback and pull requests are welcome.
Originally published by Omer Sen on LinkedIn, 17 October 2026.






