nono: Zero-Latency AI Agent Sandboxing Without Containers
Hook
Spawning a sandboxed AI agent takes 2 milliseconds with nono, versus 500+ milliseconds with Docker. The secret? Ditching containers entirely and betting on kernel security primitives that already exist in your Linux box.
Context
AI agents are graduating from demos to production, and with that comes a terrifying realization: these autonomous systems pull packages from npm, execute shell commands, and make HTTP requests based on LLM outputs you can't predict. Traditional sandboxing reaches for Docker or VMs, but that introduces operational complexity—daemons to manage, images to build, startup latency that breaks agentic workflows requiring rapid tool invocation. A coding agent that shells out to 'npm install' fifty times during a task can't afford 500ms of container overhead per invocation.
The usual workaround is to run agents with full privileges and hope prompt engineering prevents catastrophe, or to wrap everything in Docker and accept the performance tax. nono proposes a third path: use the isolation primitives already in the Linux kernel—seccomp-bpf for syscall filtering, landlock LSM for filesystem access control, network namespaces for connectivity rules—and orchestrate them through declarative profiles that treat security policies as supply chain artifacts. It's the philosophical opposite of heavyweight isolation: instead of adding layers (VMs, containers), it strips privileges from ordinary processes at spawn time.
Technical Insight
nono's architecture centers on profiles: YAML files that define allowlists compiled into kernel enforcement rules. Here's a minimal profile for a Python agent that can only read from its working directory and POST to OpenAI:
name: openai-agent
extends: base/python3
filesystem:
read:
- $AGENT_WORKSPACE/**
- /usr/lib/python3/**
write:
- $AGENT_WORKSPACE/output/**
network:
allow:
- https://api.openai.com/v1/*
syscalls:
- read
- write
- openat
- socket
- connect
- sendto
- recvfrom
The extends field pulls in a vendor-maintained base profile that handles Python interpreter requirements (shared libraries, /dev/urandom, etc.). The dollar-sign templating lets you inject runtime values—$AGENT_WORKSPACE gets resolved when you spawn the process. When you run nono exec openai-agent -- python agent.py, the CLI forks, applies seccomp filters for the syscall allowlist, activates landlock rules for the filesystem paths, creates a network namespace with routing limited to api.openai.com, then execs Python. No daemon. No image layers. Just a process with reduced privileges.
The Sigstore integration is where supply chain paranoia meets runtime security. Profiles are signed using ephemeral certificates tied to GitHub identities, with signatures recorded in Rekor's transparency log. When you fetch a profile from the registry:
nono profile install github.com/nolabs-ai/profiles/openai-agent
The tool verifies the signature against Rekor, checks that the signing identity matches the expected GitHub org, and refuses to proceed if attestations are missing. This treats security policies like container images—you wouldn't run an unsigned Docker image from a random registry, so why run unsigned isolation policies? The profile becomes part of your software bill of materials.
The L7 network filtering is architecturally fascinating. Since TLS makes deep packet inspection impossible without MITM, nono takes a hybrid approach: for HTTPS, it enforces domain-level allowlists using DNS interception (only resolve api.openai.com), then relies on TLS certificate pinning via a generated CA certificate injected into the agent's trust store. For HTTP, it can parse request paths and enforce URL patterns. This means you can write:
network:
allow:
- method: POST
url: https://api.openai.com/v1/chat/completions
- method: GET
url: https://api.openai.com/v1/models
deny:
- https://api.openai.com/v1/fine-tuning/*
And the agent physically cannot invoke fine-tuning endpoints, even if prompt injection convinces it to try. The enforcement happens at the network namespace boundary—packets to denied destinations get dropped by eBPF filters before they leave the sandbox.
Profile composition enables organizational policy inheritance. Your company maintains company/base-agent with corporate network egress rules, security teams publish security/audit-logging that enforces write access to centralized log directories, and individual projects extend both:
name: my-project-agent
extends:
- company/base-agent
- security/audit-logging
filesystem:
write:
- $PROJECT_DIR/artifacts/**
The inheritance chain resolves at load time, merging allowlists (union semantics for filesystem/network, intersection for syscalls to prevent privilege escalation). This is dramatically simpler than container-based approaches where you're layering Dockerfiles or writing K8s admission controllers.
The FFI layer exposes this to other languages via C bindings. A TypeScript agentic framework can programmatically spawn sandboxed subprocesses:
// Rust FFI (conceptual)
pub extern "C" fn nono_spawn(
profile: *const c_char,
command: *const *const c_char,
env: *const *const c_char,
) -> c_int;
This enables hierarchical agent architectures: a privileged orchestrator agent spawns specialized agents with scoped privileges (a file-reader agent with read-only access, a network agent with API-specific allowlists). Each subprocess boundary is a security boundary enforced by the kernel, not by prompt engineering or hoping the LLM behaves.
Gotcha
The Linux-first design means macOS and Windows support is aspirational at best. Landlock and seccomp-bpf are Linux-only kernel features—macOS's sandbox-exec and Windows's job objects are different APIs with different capabilities. The WSL2 dependency on Windows works, but adds the latency and memory overhead of a Linux VM, undermining the 'zero latency' value proposition. If your team develops on Macs or deploys to Windows servers, you're either using Docker (ironic) or maintaining two separate profile sets with unpredictable behavioral differences.
The Sigstore dependency creates availability and trust concerns. Profile verification requires hitting Rekor's public instance over the internet. In air-gapped environments, regulated industries, or regions with restricted connectivity, this is a non-starter. There's no documented offline verification mode or private Sigstore deployment guide. Additionally, you're trusting Sigstore's infrastructure and governance—if their CA is compromised or their policies change, your entire profile verification chain breaks. For a tool positioning itself as security-critical, the lack of decentralization or fallback mechanisms is surprising.
The kernel primitive reliance means you're trusting 30-year-old Linux APIs to hold against motivated attackers. Seccomp bypasses exist—researchers regularly find ways to escape syscall filters via TOCTOU races or unexpected syscall chains. Landlock is only a few years old and hasn't seen the adversarial scrutiny of SELinux or AppArmor. If your threat model includes adversaries exploiting kernel vulnerabilities or zero-days, nono's userspace controls evaporate. MicroVMs like Firecracker provide hardware-enforced isolation (VM page tables, separate kernel instances) that survives kernel exploits. nono explicitly trades defense-in-depth for performance.
Verdict
Use if: You're running AI agents in production with predictable tool usage patterns (filesystem reads, specific API endpoints) and need sub-10ms sandbox startup latency. Your infrastructure is Linux-first, you're comfortable with kernel-level security primitives, and your threat model targets prompt injection or supply chain attacks rather than kernel exploits. The profile inheritance model is genuinely useful for organizations with multiple teams deploying agents—base policies from security, overrides per project. Skip if: You need Windows/macOS parity without WSL2, your environment is air-gapped or has Sigstore connectivity issues, or your threat model includes kernel vulnerabilities (switch to Firecracker or gVisor's microVM isolation). Also skip if you're sandboxing truly untrusted third-party agents where adversaries control the code—nono assumes you trust the agent logic and just want to limit blast radius, not contain a determined attacker with arbitrary code execution.