AI Jail in practice — the sandbox that learned with us
Security·

AI Jail in practice — the sandbox that learned with us

The first time the sandbox saved us

The previous post told how ai-jail entered Hermes — the Rust sandbox that isolates every terminal execution with bubblewrap, Landlock and Seccomp. That was the story of the installation.

This is the story of the usage. Because installing a sandbox is easy; living with it is another thing.

The first time it truly saved me was on a pip install of an unknown package. Before the jail, that command would have access to everything: .env, Stripe keys, database credentials. Inside the jail, sensitive paths are masked — the process doesn’t even see they exist.

The feeling is strange at first: you realize every Hermes execution could leak your life — and that there’s now a safety net.

The context: from installing to coexisting

The initial config (~/.ai-jail) defines the rules:

Category What it does
Mask (empty) .env, credentials.json, secrets.yml, *token*.json, *.pem, id_ed25519*, id_rsa*
Deny (error) secrets/, *.key, .gnupg/
GPU Active (Ollama needs it)
Docker Active
Worker mode GPU/Docker/Display off

Real usage revealed 3 lessons the docs don’t tell you:

Lesson 1: Landlock is expensive — so we turned it off

First benchmark: with Landlock active, the jail took 5.2s; without it, 3.8s. Almost 40% slower on quick operations.

On WSL (where Hermes runs), the modern kernel supports Landlock — but the latency wasn’t worth it for trivial commands like python3 script.py. The pragmatic decision: no_landlock = true by default on WSL, keeping bubblewrap + Seccomp as the safety net.

# ~/.ai-jail — pragmatic decision
no_landlock = true  # 5.2s → 3.8s on WSL

The lesson: security is a trade-off. A sandbox nobody uses because of latency protects less than a fast one that becomes routine.

Lesson 2: every command has its own needs

At first, jail npm install failed because npm couldn’t find its cache. The solution: command-specific mounts:

Command Extra RW mounts
python ~/.cache/huggingface, ~/.cache/torch
npm ~/.npm, ~/.cache/pnpm
pip ~/.cache/pip
uv ~/.cache/uv
cargo ~/.cargo/registry, ~/.cargo/git

Each one mounts only what it needs — least privilege applied to caches. The process runs isolated, but with access to what legitimately belongs to it.

Lesson 3: worker mode is Hermes’ default mode

When Hermes spawns a worker (delegate_task / Kanban), it automatically detects HERMES_KANBAN_TASK and enters worker mode: GPU, Docker and Display off.

Why? Because a disposable worker doesn’t need GPU (Ollama), Docker or display — and each one is one less attack surface.

jail --worker python3 analyze.py  # no GPU, no Docker, no display

The practical result: every command Hermes runs in production is isolated — normal for common tasks, lockdown for critical operations (deploy, DB restore), worker for disposable processes.

The Arachne integration

Arachne took the concept further: app/sandbox.py uses ai-jail as a backend to isolate:

  • Browser (Playwright) — Chrome with OS sandbox (no --no-sandbox)
  • Pipeline eval() (math, jsonata) — execution in an isolated subprocess (anti-RCE)
  • Custom Python (code stage) — sandboxed_handler() writes handler + input into a tempdir and runs inside the jail
# app/sandbox.py — the essence
def run_sandboxed(cmd, ro_maps=[], rw_maps=[], no_net=False, timeout=60):
  # mounts paths, runs inside the jail, returns stdout/stderr

And the MCP tool arachne_sandbox_status shows active protections in real time — transparency about what’s protected.

Real usage metrics

Item Value
Landlock active (5.2s vs 3.8s — off on WSL)
Commands with custom mounts 5 (python, npm, pip, uv, cargo)
Execution modes 3 (normal, lockdown, worker)
Worker mode Auto via HERMES_KANBAN_TASK
Arachne sandboxable Browser + eval + code stage

Lessons learned

  1. Security is a trade-off — slow sandbox = nobody uses it; fast = becomes routine
  2. Least privilege applied to caches — each command mounts only what it needs
  3. Worker mode is default — GPU/Docker/Display off by default for disposable processes
  4. Transparencyarachne_sandbox_status shows what’s protected
  5. Daily usage is the real test — docs teach you to install; usage teaches you to live
~/lifelog — bash
$cat about.txt
╔══════════════════════════════════════╗
║  Samuel Medeiros                    ║
║  Senior Software Engineer           ║
║  Stack: Python · TypeScript · Rust  ║
║  Projetos: Arachne, Dogwalk,        ║
║            Capivara, TatuEngine      ║
╚══════════════════════════════════════╝
      
$

The sandbox isn’t a feature you install and forget. It’s a discipline you live — and the more you use it, the more you realize what it’s protecting.