The Gate and the Wall: When to Run Your Agent Native vs. Sandboxed with node9

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 -rf sitting 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, .env files and any path you add with node9 jail add are blocked before they happen, from any shell reader. Native mode also has its own egress control, node9 egress watch or lock, 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:

  1. Resolve the allowlist to IPs. Each host in your outbound.allow is resolved - through every configured nameserver, to cover CDN and anycast rotation - into a kernel ipset.
  2. Seal a deny-by-default egress wall. iptables policy on INPUT, FORWARD, and OUTPUT is set to DROP. 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.
  3. Drop privilege. The entrypoint gosu-drops from root to a non-root agent user (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 isIn-path gate + audit trailDisposable container + kernel egress wall, gate running inside
The boundary it gives youNone - it's governance, not containmentKernel-enforced: blast radius confined to the mounted dir + allowed hosts
Setup costZero new infra, works with your real workflowDocker, a build step, the agent runs in a box
Agents todayAll wired agents (Claude, Codex, Gemini, Antigravity, Copilot, Hermes, Cursor, …)Claude, Codex
Honest limitHeuristic on attacker-influenced input; shares host blast radiusPhase 1: agent still holds its own creds in the box
Best whenTrusted operator, day-to-day dev, you want gating + visibility + a credential jail with no frictionThe 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.

Share
XRedditHacker NewsLinkedIn

Related posts

Security · AI

Securing the Agentic Era: An Architectural Review of NVIDIA OpenShell vs. Node9 Proxy

Two architectural patterns are emerging to secure autonomous agents: infrastructure sandboxing (NVIDIA OpenShell) and execution governance (Node9 Proxy). A technical review of how each works under the hood, where each belongs in your stack, and why the mature answer is defense in depth - one running inside the other.

Security · AI

What Can a Hijacked AI Agent Actually Reach on Your Machine?

A compromised coding agent doesn't need a zero-day - it inherits everything your shell can reach, and its malicious reads look exactly like its helpful ones. Runnable checks to see your own blast radius, why exfil hides in traffic you approved, and how to read your exposure score in ten seconds.

Security · AI

Running Hermes Agent in the Cloud Safely: A Reader's Guide to Their Trust Model

Hermes Agent runs anywhere - $5 VPS, GPU cluster, serverless. NousResearch's SECURITY.md is unusually clear about what it treats as load-bearing and what it does not. An operator-friendly walkthrough: isolation posture, gateway allowlists, terminal backends, skills review, in-process gates - and where each fits relative to the OS-level boundary.