Back to Articles

Nono: Kernel-Level Sandboxing for AI Agents Without the Container Tax

[ View on GitHub ]

Nono: Kernel-Level Sandboxing for AI Agents Without the Container Tax

Hook

Every AI agent you deploy is one prompt injection away from exfiltrating your AWS credentials. Traditional containers add 100ms+ latency per execution. What if sandboxing happened in-process, at the kernel level, with zero performance penalty?

Context

The AI agent security problem is fundamentally different from web application security. When you deploy a chatbot or autonomous agent, you're not just worried about SQL injection or XSS—you're dealing with systems that actively interpret natural language instructions and execute code based on potentially adversarial prompts. The classic approach is to throw these agents into Docker containers or microVMs, which works but introduces significant overhead: startup latency, resource consumption, and operational complexity. For agents that need to spawn dozens of parallel sessions or execute thousands of small tasks, container overhead becomes untenable.

The deeper issue is credential management. Agents need API keys to interact with external services, but giving an agent filesystem access means a compromised agent can simply read your .env file or ~/.aws/credentials. Prior solutions forced you to choose between convenience (mount everything, pray the agent doesn't go rogue) or complexity (orchestrate separate credential injection systems, manage network proxies, accept the latency). Nono emerged from this tension: what if we could leverage modern kernel sandboxing primitives like Linux's Landlock and macOS's Seatbelt to enforce restrictions directly within the agent's process, combine that with a proxy mode for credential injection, and add content-addressable snapshots for instant rollback—all without spawning a single container?

Technical Insight

Nono's architecture centers on irreversible capability restriction. Once you call the sandbox enforcement function, the kernel guarantees that no subsequent code—even with root privileges—can expand those permissions. On Linux, this uses Landlock, a stackable LSM (Linux Security Module) that restricts filesystem and network access. On macOS, it leverages Seatbelt profiles. The key insight is that these are in-process restrictions: no forking, no namespaces, no virtualization overhead.

Here's what minimal sandboxing looks like in Rust:

use nono::{Sandbox, Policy};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Define capabilities before enforcement
    let policy = Policy::new()
        .allow_read("/data/inputs")
        .allow_write("/tmp/agent-workspace")
        .allow_network(&["api.openai.com:443"])
        .deny_all_else();
    
    // Apply sandbox - this is irreversible
    let sandbox = Sandbox::apply(policy)?;
    
    // Everything after this point runs restricted
    // Attempting to read /etc/passwd will fail
    // Attempting to connect to malicious-site.com will fail
    
    run_untrusted_agent_code();
    
    Ok(())
}

The irreversibility is a feature, not a bug. It prevents time-of-check-time-of-use attacks where malicious code might try to remove restrictions after gaining control. Once Sandbox::apply() returns, the kernel enforces those rules for the lifetime of the process.

The credential injection system is where nono gets interesting. Instead of mounting secrets into the sandbox, nono runs a proxy server that intercepts specific network requests:

# Python bindings example
from nono import Sandbox, ProxyConfig

# Configure proxy to inject GitHub token
proxy = ProxyConfig()
proxy.intercept(
    pattern="api.github.com",
    inject_header="Authorization",
    from_keychain="github_agent_token"  # Reads from OS keychain
)

sandbox = Sandbox(
    allow_network=["api.github.com:443"],
    proxy=proxy
)

with sandbox:
    # Agent code never sees the actual token
    # Proxy injects it transparently
    agent.run()

The agent's code makes normal HTTP requests to api.github.com, but the network traffic routes through nono's proxy, which injects the credential from the system keychain. The credential never exists in the sandboxed process's memory or environment variables. If the agent is compromised, there's nothing to exfiltrate.

Content-addressable snapshots add another layer. Nono can capture filesystem state as a Merkle tree, deduplicate blocks via SHA-256 hashing, and restore previous states instantly:

// TypeScript bindings
import { Sandbox, Snapshot } from '@nono/sdk';

const sandbox = new Sandbox({
  workspace: '/tmp/agent-session',
  snapshot: true
});

// Capture state before risky operation
const checkpoint = await sandbox.createSnapshot();

try {
  await agent.executeUserPrompt(untrustedInput);
} catch (error) {
  // Instant rollback - O(1) operation, just swaps pointers
  await sandbox.restoreSnapshot(checkpoint);
}

The snapshot system uses copy-on-write semantics. Creating a snapshot doesn't copy files; it records the current Merkle root. Only modified blocks get written to new storage. This makes snapshots effectively free for read-heavy workloads, which describes most agent sessions.

Nono also integrates Sigstore for supply chain security. Agent instruction files (the prompts and policies that configure agent behavior) can be signed and verified:

# Sign an agent policy file
nono sign agent-policy.yaml --keyless

# Verify before execution
nono run --verify-signature agent-policy.yaml

This brings software attestation practices from traditional CI/CD into the AI agent world. You can ensure that the policy file running in production is exactly what your security team approved, with cryptographic proof and transparency log entries via Sigstore's Rekor.

Gotcha

The big one: nono is alpha software explicitly not security-audited for production use. The developers are transparent about this—using it to protect real secrets right now means accepting that undiscovered vulnerabilities might exist in the sandboxing implementation. Kernel sandboxing is notoriously tricky to get right; a single missed syscall or race condition can create an escape vector. Wait for the security audit if you're deploying agents with access to production credentials or sensitive data.

Irreversibility cuts both ways. Once you apply a sandbox policy, you cannot relax it without restarting the process. This makes nono unsuitable for agents that need dynamic privilege escalation—for example, an agent that starts with limited permissions but might legitimately need to request additional filesystem access based on user approval during execution. You'd need to architect around this with separate processes or accept the restart overhead. Platform support is also limited: macOS and Linux (including WSL2) work now, but native Windows support is still planned. If your deployment targets include Windows servers or you need your agents to run in native Windows environments, nono isn't ready yet.

Verdict

Use if: You're building AI agents that execute untrusted code or process adversarial prompts, performance matters enough that container overhead is unacceptable (think high-frequency agent execution or parallel session spawning), you need fine-grained credential management where secrets never enter the agent's process, or you want filesystem snapshots for instant rollback without storage penalties. It's especially valuable in development workflows where you're iterating on agent behavior and need fast snapshot/restore cycles, or in CI environments running agents against pull requests where supply chain verification via Sigstore adds security without slowing builds. Skip if: You need production-grade security guarantees today (wait 6-12 months for security audits), your deployment targets include native Windows, you require dynamic privilege adjustment during agent execution (the irreversibility model won't work), or your use case already fits comfortably in containers and you value ecosystem maturity over performance. Traditional container isolation with AppArmor/SELinux is battle-tested and might be the safer bet for now if milliseconds don't matter to your workload.

// ADD TO YOUR README
[![Featured on Starlog](https://starlog.is/api/badge/ai-agents/always-further-nono.svg)](https://starlog.is/api/badge-click/ai-agents/always-further-nono)