You've decided to put a governance layer between your AI agent and the things it can touch. Good. The next question is where that layer sits: woven into the agent you already run, or wrapped around it as a disposable container with a hard wall at the kernel.
node9 does both, and they are not the same tool with a different coat of paint. Native mode is an in-path gate and audit trail. Sandbox mode is containment. They answer different questions, they cost different amounts of friction, and - this is the part most comparisons miss - they compose: sandbox mode runs the native gate inside the box. This is a field guide to which one you want, and when.
Two questions, not one
It helps to separate two things that get blurred whenever people argue about "agent security."
Containment asks: when this agent does something wrong, what can it reach? That's a boundary question, and boundaries are enforced by the operating system - namespaces, a container, a kernel egress filter. Heuristics don't contain anything; a string-matcher running in the agent's own process is not a wall.
Governance and visibility ask: what is the agent actually doing, is this specific command allowed, and what's the record of every call it made? That's a logic question. The OS boundary can't answer it - your egress firewall will happily forward a credential it can't see inside a tool argument, and a container tells you nothing about the agent looping on the same file for an hour.
You need both. node9's two modes map cleanly onto them: native mode is the governance-and-visibility layer, sandbox mode adds the containment boundary. Pick by how much of each you need.
Native mode: the gate that travels with your agent
Native mode is what node9 init gives you. It wires into the agent you already run - via pre-execution hooks for Claude Code, Codex, Gemini, Antigravity, GitHub Copilot CLI, and Hermes; a node9-authored plugin shim for Opencode and Pi; or a transparent MCP gateway for Cursor, Windsurf, VSCode, and Claude Desktop. On the hook and plugin agents, every tool call passes through node9 before it executes. On the MCP-only four, node9 sees what goes through MCP servers and nothing the editor does on its own.
What it does on the way through:
- AST-based policy. node9 parses the structure of a requested shell command with mvdan-sh rather than pattern-matching its text. That's the difference between a denylist that fires on
rm -rfsitting inside a commit message and one that resolves a real destructive command split across a heredoc, a variable, or a base64 pipe. (The full argument is in Why Regex Is Not Enough.) - In-flight DLP. On the hook and plugin agents, every tool argument, file read, and shell payload is scanned for AWS keys, GitHub tokens, Stripe keys, and PEM material before it moves. A
cat .env | base64 | curl ...exfiltration is caught on the way out, not after. - An immutable audit trail. Every decision lands in
~/.node9/audit.log, atomically. That's the record the OS boundary can't give you - what every call did, what it cost, what fired. - Credential jail. Reads of
~/.ssh,~/.aws,.envfiles and any path you add withnode9 jail addare blocked before they happen, from any shell reader. Native mode also has its own egress control,node9 egress watchorlock, off by default, gating destinations rather than contents.
Native mode's strengths are friction and reach. There's no new infrastructure, the agent keeps working against your real filesystem and your real workflow, it's live on every supported agent today, and it governs all of them under one policy with one audit log. For a developer at a terminal, or a gateway-driven agent answering messages, this is the day-to-day layer.
Its honest limit is the same one we say out loud everywhere: a gate in the agent's tool-call path - not at an OS boundary - is a heuristic operating on an attacker-influenced string. It catches the cooperative-mode mistakes that account for most real incidents - the confused rm -rf, the reflexive git push --force, the credential about to leave in an argument. It is not containment. A determined, adversarial agent with arbitrary code execution shares the host's blast radius. When the input flowing into the agent is untrusted, native mode wants a boundary underneath it.
Sandbox mode: the disposable box with a kernel wall
That boundary is what node9 sandbox provides. Two commands:
node9 sandbox new # write node9.sandbox.yaml - what to mount, which hosts to allow
node9 sandbox run # build + boot the jailed agent (your project at /workspace)
Under the hood, run builds a container (a node:22 base with iptables, ipset, and gosu), installs the agent CLI and node9 inside it, and generates an entrypoint that does the load-bearing work as root before the agent ever starts:
- Resolve the allowlist to IPs. Each host in your
outbound.allowis resolved - through every configured nameserver, to cover CDN and anycast rotation - into a kernelipset. - Seal a deny-by-default egress wall.
iptablespolicy onINPUT,FORWARD, andOUTPUTis set toDROP. The only traffic that escapes is loopback, already-established connections, DNS to your resolvers, and packets whose destination is in the allowlist ipset. Everything else is dropped at the kernel - not intercepted in userspace, dropped. - Drop privilege. The entrypoint
gosu-drops from root to a non-rootagentuser (uid 1000), starts node9's daemon, and execs the agent.
The container runs with --rm: it's destroyed on exit. Your project edits land on your real disk because the project is mounted at /workspace; nothing else survives. And node9's own gate, DLP, and audit log run inside the box - so you get containment and governance in the same run, streamed to the same audit log you'd get natively.
A few invariants are worth calling out because they're easy to get wrong:
- The node9 SaaS key never enters the box. It's stripped from the passed environment, the firewall denies node9.ai hosts even if you explicitly allowlist them, and the in-box approver is terminal-only. A compromised agent in the jail can't exfiltrate a key that was never there.
- Only the agent's credential file is mounted - not your whole
~/.claude. Mounting the directory would mask the box's own hook wiring and silently disable governance inside the jail.
The honest scope, Phase 1: a single container, Claude and Codex today, and the agent still holds its own provider credentials inside the box - the egress wall confines them to the hosts you allow, but a true credential broker ("the agent never holds a secret") is a later phase on the roadmap. It requires Docker. And the friction is real: there's a build step, the agent runs in a box, and workflows that assume free run of the host need rethinking.
The part people miss: sandbox is native, plus a wall
The two modes are not a fork in the road. node9 sandbox run boots node9's daemon and hooks inside the container, so the native gate, DLP, and audit trail are all running in there too - against the same shields and the same policy you use on the host. Sandbox mode is native mode with a kernel boundary wrapped around it.
NATIVE SANDBOX
┌──── kernel egress wall ─────┐
agent → [node9 gate] → host │ agent → [node9 gate] → /workspace
│ (+ allowlisted hosts only)
└─────────────────────────────┘
governance, no boundary the same gate, inside a hard wall
That changes the decision from "which one" to "how much boundary do I need underneath the same governance." The gate is constant. What you're really choosing is whether to add the wall.
Choosing: a field guide
Native (node9 init) | Sandbox (node9 sandbox run) | |
|---|---|---|
| What it is | In-path gate + audit trail | Disposable container + kernel egress wall, gate running inside |
| The boundary it gives you | None - it's governance, not containment | Kernel-enforced: blast radius confined to the mounted dir + allowed hosts |
| Setup cost | Zero new infra, works with your real workflow | Docker, a build step, the agent runs in a box |
| Agents today | All wired agents (Claude, Codex, Gemini, Antigravity, Copilot, Hermes, Cursor, …) | Claude, Codex |
| Honest limit | Heuristic on attacker-influenced input; shares host blast radius | Phase 1: agent still holds its own creds in the box |
| Best when | Trusted operator, day-to-day dev, you want gating + visibility + a credential jail with no friction | The agent ingests untrusted input, or you want the hard wall around an autonomous run |
Concretely:
Reach for native when you're the one driving the agent, the work is interactive and local, and what you want is a sharper gate, an audit trail, and a credential jail across every agent you run - without syncing files across a boundary to write a React component.
Reach for sandbox when the content flowing into the agent comes from surfaces you don't control - the open web, inbound email, a multi-user chat, an untrusted MCP server - or when you're letting an agent run autonomously and you want its blast radius confined to a disposable box no matter what the model decides to do.
Use both, and you already are - sandbox mode runs the native gate inside the box, and native mode on the host governs everything that isn't in a box. The strongest posture is the wall and the gate together, which is exactly what a single sandbox run gives you.
Where this sits next to OpenShell and friends
If you've read our architectural review of NVIDIA OpenShell vs. node9, the mental model extends cleanly. node9 now spans the spectrum from a zero-infra in-path gate (native) to a self-contained disposable jail (sandbox). For heavyweight, multi-tenant, kernel-isolation deployments - Landlock policies, a privacy router rerouting inference to self-hosted models, per-session sandboxes at fleet scale - a dedicated infrastructure layer like OpenShell is still the blueprint, and node9 runs happily inside it. Defense in depth didn't change; node9 just covers more of it out of the box.
The same is true for agents that ship their own whole-process wrapping. If your platform already gives you a containerized boundary, run node9 native inside it - you get their wall and node9's gate and audit in one place.
Getting started
# native - in-path on every agent you already run
npx node9-ai init
# sandbox - a disposable, kernel-walled box for Claude or Codex
node9 sandbox new
node9 sandbox run
node9 sandbox tail # watch every action live, from the host
Both modes are local-only, no telemetry, Apache 2.0, and they write to the same audit log - so whichever you start with, you're not locked out of the other. Most teams land on native everywhere and sandbox for the runs that touch something they don't trust. Start with the gate; add the wall where the input gets dangerous.