agents

TL;DR
Use jail.nix to sandbox your AI agent environment to protect your host machine from rogue agent exfiltrating your API keys or stealing browser cookies. If you also enable Claude’s --dangerously-skip-permissions or Gemini’s --yolo, there is more reason to properly restrict what the agents can do. Besides, proper sandboxing gives you ease of mind knowing that it can’t wreak havoc on your host machine.

Getting Started

For the impatient:

mkdir -p $HOME/repo/jailed-ai-agents
cd $HOME/repo/jailed-ai-agents
git clone https://github.com/kohane27/jailed-ai-agents.git .
direnv allow
./llm.sh --claude

In the prompt, first type ! to run shell commands. Then type ls -a ~ to see what directories and files Claude has access to.

Introduction

I came across two great articles: My Secure AI Agent Setup on deep diving into sandboxing/VM and How I Run LLM Agents in a Secure Nix Sandbox for its initial implementation. They gave me a good starting point but I need to polish it further to make it more “ergonomic” to use.

Integration

flake.nix requires to be in a git-controlled directory and commited to work. I don’t want to commit the same flake.nix in all my repo in order to use the jailed agents. Besides, not all dirs are version-controlled in which I want to use the jailed agents. This is why I put the flake.nix in a specific dir ($HOME/repo/jailed-ai-agents) and use a wrapper shellscript to access the jailed agents.

After cloning kohane27/jailed-ai-agents, the most important files are just flake.nix and llm.sh. The following is how I have extended beyond the original flake.nix.

  1. I have added notifications to commonJailOptions and libnotify to commonPkgs such that notify-send works. I have Claude hooks that uses notify-send to tell me when it’s done:
{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "notify-send --expire-time=3000 --icon=\"braindump\" \"Claude needs your attention\""
          }
        ]
      }
    ]
  },
}

The same with Gemini:

{
  "hooks": {
    "AfterAgent": [
      {
        "matcher": "*",
        "hooks": [
          {
            "type": "command",
            "command": "notify-send --expire-time=3000 --icon=\"braindump\" \"Gemini: Agent finished\""
          }
        ]
      }
    ]
  }
}
  1. I have added wayland to commonJailOptions and wl-clipboard to commonPkgs for clipboard communication between the host and the sandbox. This is to make the /copy command works in both Claude and Gemini.

  2. (set-env "COLORTERM" "truecolor") such that all the TUIs can use truecolor colors.

  3. I route all the requests through my host’s gluetun and to see the network traffic:

(set-env "HTTP_PROXY" "http://127.0.0.1:8888")
(set-env "HTTPS_PROXY" "http://127.0.0.1:8888")
  1. Use add-runtime to inject and mount the directory in which the llm.sh is run. Let’s say your current dir is $HOME/Downloads/test_repo/. When you run llm.sh --claude, it’ll inject $HOME/Downloads/test_repo/ as the dir in which the AI agent has access to and nothing else. It can’t see your host’s ~/.ssh or ~/.mozilla.

  2. I added some packages like openspec and formatters for opencode like prettier and shfmt.

  3. Given we are in a sandbox environment, I added --dangerously-skip-permissions to Claude and --yolo to Gemini. If you’re not comfortable with it, just delete these two flags.

  4. I have added a debug mode to debug/verify the sandbox isolation:

I. Enter the debug shell:

cd $HOME/repo/jailed-ai-agents
nix develop
PROJECT_DIR=$(pwd) nix develop $HOME/repo/jailed-ai-agents --impure -c debug

II. Test what the agent can see:

ls -a ~
ls -a /
ls /tmp

III. Run env to see what environment variables are available:

env

IV: Check network proxy (if you’re using gluetun like I mentioned above)

echo $HTTP_PROXY
  1. I have the following in my home-manager dotfiles such that in any repo, I can just run llm --claude, llm --gemini or llm --opencode:
  home.packages = with pkgs; [
    (writeShellScriptBin "llm" (builtins.readFile /home/username/repo/jailed-ai-agents/llm.sh))
  ];

Scenarios

What happens if the agent decided to run rm -rf /?

Deciding on which agent you use, say, jailed-claude, ~/.claude, ~/.claude.json, and the project dir in which you run the agent will be gone forever. The alternative without a sandbox is your host’s whole home dir is deleted. As you can see, the damage is greatly minimized.

What happens if the agent decided to exfiltrate as much data as possible?

Your crypto wallets are safe. Your browser cookies are safe. What it can’t protect you from is the API key used to run the agent. In other words, if you run jailed-claude, ANTHROPIC_API_KEY will be leaked (jailed-gemini will leak GEMINI_API_KEY) . The sandbox environment needs the API key to run the respective agent.

Conclusion

Feel free to extend it and make it your own. Also, to further understand what jail.nix can do, check out its doc, especially combinators.

Looking for an even more locked-down environment for your agents? Check out Maximum security confinement for your AI agents with microvm.nix.