Semi Safe Agents

It’s good to be paranoid. This goes double when dealing with LLMs that may or may not completely delete your entire filesystem. Fortunately that is extremely straightforward to avoid.

LLMs are becoming increasingly capable, and in all the worst ways. There are already stories out there of “rogue” agents doing things one might consider incredibly stupid or outright disastrous:

I’m sure you get the point. Modern LLMs are now capable of doing things one would expect of an intern if they were given far more permissions and leniency than they ought to be. It really boggles the mind that some people will simply let these tools have unfettered access to just about everything without even basic safeguards in place, but it goes to show that having access to powerful tools does not mean you know how to use them. To be less generous, it appears that some people have been playing stupid games with powerful tools and are somehow surprised at the prizes they win - nobody wants to make this sort of headline.

Paranoia is the name of the game when dealing with any sufficiently complicated technology that might do things to your computer you don’t really want. Even more so when you know this technology theoretically can do anything you can - depending on how it’s run. I’m going to outline here how I avoid making headlines and how even a little bit of paranoia can go a long way nowadays.

I Love/Hate Docker #

Docker is a tool that I have dealt with throughout my career in many ways. Begrudgingly I will admit that it is the way to ensure that you have a portable and consistently configured environment, frankly I have not found a better solution for this (it probably exists and I simply am not aware). It is the obvious choice to me for completely isolating a LLM or agent and give it only what is absolutely necessary.

There are a few caveats here I will list in no particular order:

  • If the AI companies or their marketing departments are to be believed (doubtful) then it is theoretically possible that a mega-capable LLM could find vulnerabilities in your isolation mechanism to escape
  • You will need to allow network access to agents for them to be even usable, so things on your network may still be vulnerable
  • I am not a web dev or planning to use agents to do much web dev, and have not dealt with any nice port forwarding. This means extra steps if you want to test a web server running in the docker env (but I will show how to get files out easily at least)

My use case for agents is currently quite heavy around making a whole host of simple Python scripts, so for the most part I can avoid these downsides. I am very happy to avoid JS/frontend as much as possible and as I have no need to use or test this currently, the drawback being that it is moderately difficult to interact with files through something other than the terminal is not a major problem for me. That being said, port binding on your container is really not the end of the world. As I’ve done to set up these scripts, I’m sure it’s as easy as a prompt in your LLM of choice (and double checking whatever comes out via the docker docs).

Minimal Pi Setup #

My agent harness of choice at this point is Pi. This is not a strong preference, I have also used the Hermes agent and have had it running on a Nuc for some time now, but wanted to toy with Pi after hearing about how simple and straightforward it is. Documentation covers the containerized Docker setup quite thoroughly. After throwing ChatGPT at it, I have arrived at the following Dockerfile which gives me a container with just enough to do quick agent/LLM based development:

FROM node:24-bookworm-slim

RUN apt-get update \
    && apt-get install -y --no-install-recommends \
        bash \
        ca-certificates \
        git \
        ripgrep \
        fd-find \
        python3 \
        python3-pip \
        python3-venv \
        build-essential \
        curl \
        wget \
        jq \
        unzip \
        zip \
        tree \
        less \
        vim-tiny \
    && ln -s /usr/bin/fdfind /usr/local/bin/fd \
    && ln -s /usr/bin/python3 /usr/local/bin/python \
    && rm -rf /var/lib/apt/lists/*

RUN npm install -g --ignore-scripts @earendil-works/pi-coding-agent

WORKDIR /workspace

ENTRYPOINT ["pi"]

This is what I would consider a sufficiently minimal environment for my Pi harness to work in. It has access to a very limited set of tools, but more than enough to write simple Python scripts and obviously enough to run the Pi harness. All you need to do to build that is to run docker build and name your image:

docker build -t pi-sandbox .

At this point the docker image should be built. I additionally created some docker volumes to avoid any sort of volume mounting directly in the image:

docker volume create pi-workspace
docker volume create pi-agent-home

Once this is done we can finally drop in to the actual docker environment, just to check that things fail for soon to be obvious reasons:

docker run --rm -it -v pi-workspace:/workspace -v pi-agent-home:/root/.pi/agent pi-sandbox bash

This should avoid running the Pi agent as soon as you enter the container. All you have to check is that things are properly mounted, which means there should be a /workspace directory and /root/.pi/agent and that’s it.

Fully Auto Containerization #

Nobody can remember massive docker command strings, and you don’t need to. I certainly don’t know enough about docker to make sure I run things with the ideal settings each time, and as I did for the Dockerfile I got ChatGPT to again set up some very helpful bash scripts to start the environment for me with some useful settings. After a few iterations/messages back and forth I’ve settled on the following simple startup script:

#!/usr/bin/env bash
set -euo pipefail

IMAGE="pi-sandbox"
WORKSPACE_VOLUME="pi-workspace"
AGENT_VOLUME="pi-agent-home"

exec docker run --rm -it \
  --cap-drop=ALL \
  --security-opt=no-new-privileges:true \
  --pids-limit=512 \
  --memory=4g \
  --cpus=4 \
  -e OPENROUTER_API_KEY \
  -v "${WORKSPACE_VOLUME}:/workspace" \
  -v "${AGENT_VOLUME}:/root/.pi/agent" \
  "${IMAGE}"

I had a few goals here:

  • Running this should feel as simple as running the harness directly and make sure there is 0 friction in using the sandboxed environment
  • Compute usage should be heavily limited, the LLM/agent shouldn’t need much of anything (I’m not using local models for this)
  • Only send my Openrouter key through as an env variable, otherwise this container knows nothing about the environment. Use whatever API token you like here, Pi setup with this is seemingly automatic

If there is any difficulty in using the secure sandboxed agent over one without any built-in paranoia, I’ll probably end up being lazy one day. As in my Simple Server Security post, this is following a similar approach to Caddy in making the secure option almost unreasonably simple. All I have to do now is call the startup script (after making it executable) and I’m dropped in a completely sandboxed environment about as fast as a regular terminal starts up.

Getting files out is yet another part of this that can be automated. As with starting docker and the litany of commands associated with inspecting the filesystem I am not going to remember precisely how to get files out of the container. This is yet another good sign that things are perhaps unreasonably secure. In good news this is once again easily solved by creating an export script that copies the files from the workspace docker volume created earlier to a local directory:

#!/usr/bin/env bash
set -euo pipefail

VOLUME="pi-workspace"
DEST="${1:-$HOME/Downloads/pi-export}"

mkdir -p "$DEST"

docker run --rm \
    -v "${VOLUME}:/workspace:ro" \
    -v "${DEST}:/export" \
    debian:bookworm-slim \
    bash -c 'cp -a /workspace/. /export/'

echo "Exported Pi workspace to:"
echo "  $DEST"

Now after a simple mkdir ~/Downloads/pi-export (or pass whatever other directory you want as an argument) you can trivially get files out. With 3 simple files and only 2 scripts you can have a completely sandboxed agent. In total this took me maybe 30 minutes of sitting around and prompting ChatGPT, there really is no excuse for not setting up a sandbox.

A Note on “Securing” Agents #

I started this post off by saying that plenty of disasters have been caused by LLMs/AI agents being used (or misused). A sandbox can’t 100% stop this, but it makes it at minimum extremely unlikely to ever occur. Each of these instances occurred because someone had little to no boundary between the “agent” or tool they were using and the data that got destroyed, that’s the real danger.

On the sliding scale of data access, this approach is as far as reasonably possible towards the “none at all” end instead of the “here’s my social security number as well as all the numbers on my credit card” end. I am quite obviously more comfortable in the “no you can’t access anything actually” end.

People still want to use LLMs to automate really monotonous stuff. Automating things in a useful way will obviously require access to data. Maybe they just need read permissions sometimes. Not all the time, but on a case-by-case basis perhaps reading relevant context is acceptable. It should be the responsibility of people using tools (even if those tools sound intelligent) to determine when that is acceptable. And also to make sure that the tools aren’t going haywire and nuking your whole inbox just because you thought it was a good reason to give the “agent” what was functionally root access to your whole email account.