Series · part 1 of 4Agentic Engineering FoundationsAll parts
- Sandboxing AI Agents with Dev Containers
- System prompts and skills
- Spec-Driven Development (SDD) for AI Coding Agents
- Tips for writing efficient skills for LLM agents
The agent can read everything
AI agents read whatever lands in their context: code, docs, READMEs and comments in your dependencies, issues and pull requests, the web pages they fetch, sometimes even text inside images. A lot of systems get compromised nowadays by adversarial actors and the chance of you becoming a victim of a maliciously planted prompt injection(opens in new tab) grows every day.
Agents are more secure now than a year ago - execution is done heavily in local sandboxes, without the need for anything to be set up, but even in a local runtime the agent manages to escape its restrictions. It only makes sense that one should approach this problem with caution.
What got me here
I started using AI for coding about 6 months after the boom of Claude Code. I was on a sabbatical and wasn’t looking too much into the scene, while enjoying some quality time off with my family.
Once I came back, I was excited to pick it up. The first thing that I built with it was this blog. I was very cautious, however. Even before I got my Claude subscription, I saw a user on Reddit losing their entire home directory content(opens in new tab) - the agent ran rm -rf tests/ patches/ plan/ ~/. Do you see that dangling ~/ at the end? Yep, that was the end of it.
It got me thinking - I need to protect myself from that before I end up crying about it some day. I was thinking about getting a specific local user with restricted permissions and so on - not a bad idea, but the agent would still have tons of access to my box and I wasn’t sure if it wouldn’t escalate privileges.
The decision was made: dev containers it is.
What is a dev container
Dev containers existed long before agentic workflows. They were built so teams of developers could share a dev environment easily, plus it got them as close as possible to the production environment in which the application runs. You build a Dockerfile for your application to deploy on it, so you might as well use it to run everything else in it. It doesn’t get much closer to prod than that. Of course, deploying to your specific cloud provider, Kubernetes and so on, usually complicates things, but still it helps more than it hurts.
There’s an open-source dev container toolchain(opens in new tab) that integrates almost natively with VS Code (which I recently dropped for Zed(opens in new tab)).
What a good dev container needs
You don’t need my Dockerfile: copy/pasting code is, as my friend Mitko(opens in new tab) says, “a cavemen” approach. Just ask the agent to do it for you. Surely you’ll survive one run before it nukes your machine. If you’re worried about it, do it in a web-based chat session and copy/paste the code (not as caveman-istic as copy/pasting it from my post). The recipe for success is rather trivial:
- Split the
Dockerfileinto stages - one builds the image with all dependencies for dev, one for CI, one for local dev, one for prod build. Include everything that you’d need to run the project - runtime, libraries, git, netcat (for notifications), etc. - In the local dev one, install your agent’s harness (Claude Code, Codex, OpenCode, whatever you’re using)
- Make sure your favourite shell is ported (I use zsh with oh-my-zsh(opens in new tab)), dotfiles, whatever you need
- Get a secure GitHub token (I assume you’re using GitHub for some reason) and assign it to the container’s
ghin a way that it survives restarts or, if you’re feeling more adventurous, propagate your SSH key inside the repo - this can be done in multiple ways, but it’s riskier, especially if you’re using the same SSH key for more than one host - Mount the local code as something like
/app, so that the agent has access to it - Make sure to persist the container’s home directory, so it survives restarts; this is where all configuration changes would go, not only for apps, but for the agent too
- Set up notifications (next section in this article)
- Use
docker-compose.ymlto orchestrate everything, so that a simpledocker compose up -dgets you up and running - When done, use your agent from the container’s terminal
Take a look at Anthropic’s dev container reference(opens in new tab) for a guide on how to set up yours. Its firewall limits outbound traffic to the domains its script allows. The idea applies to any agent: allow only what it needs.
Setting up notifications
Now this part is tricky. I started with the standard Mac Terminal.app with tmux. This didn’t work out well. My goal was to have a notification that, when clicked, focuses the exact terminal tab of the agent that’s calling me. tmux obscured that, as the session propagated the ID of the terminal that originally spawned it, which was long gone.
I changed the setup and used iTerm, which made me code a little notification app for Mac that allowed me to have that experience. Nowadays I’m using something much better: herdr(opens in new tab) - it’s like tmux, as it runs the shell sessions like a server, but it’s made specifically for agentic development. It’s written in Rust, so it’s blazing fast and notifications are handled out of the box.
In any case, streaming notifications from a Docker container to the host machine is a completely different problem. The goal was to allow the container to call code on the host, but not any code (I didn’t want it to run something like rm -rf /), only the notifications. This is the setup I came up with:
- A notifier.sh on my local machine that is executed with a notification parameter (i.e. no direct command execution, but rather saying “hey notification handler, here’s the what’s up”)
- Exposing the script via
socaton port4545 - Configuring the container’s agent hooks to emit a message to
host.docker.internal:4545usingnetcat
notifier.sh
It doesn’t get more simple than that. This is basically what it looked like:
#!/bin/bash
read -r CMD ARG
case "$CMD" in
notify)
terminal-notifier -title "Agent" -message "$ARG" -open "iterm2://session"
;;
*)
echo "DENIED"
;;
esacThe controls to start the listener
pgrep -f 'socat TCP-LISTEN:4545' >/dev/null || \
( \
nohup socat TCP-LISTEN:4545,reuseaddr,fork \
EXEC:.devcontainer/notifier.sh >/tmp/hostcmd.log 2>&1 & \
echo '🔔 Notifier started.'
)pkill -f 'socat TCP-LISTEN:4545' && \
echo '🔕 Notifier stopped.'Setting up the notification trigger
The last thing now was to have notifications being triggered once Claude was waiting for me. For this I had to modify the Claude hooks on the dev container itself.
{
"hooks": {
"Notification": [
{
"matcher": "permission_prompt",
"hooks": [
{
"type": "command",
"command": "printf 'notify blog (waiting)\n' | nc host.docker.internal 4545"
}
]
}
],
"Stop": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "printf 'notify blog (done)\n' | nc host.docker.internal 4545"
}
]
}
]
}
}That’s pretty much it! Containerised execution with notifications.
Containers for the real world
Once your setup grows and you start building heavy automations, at one point you’re going to challenge yourself with building a fully or semi-automated software factory. This is when things get serious. Building a factory in full automation, especially on-premises with production readiness, makes sandboxed execution non-negotiable. I won’t go through this now, but here’s a bunch of valuable projects that are worth your time once you get there:
- Daytona(opens in new tab) - infrastructure for running AI-generated code in isolated sandboxes, with fast cold starts and no session time limit
- Coder(opens in new tab) - self-hosted environments for developers and their agents
- OpenSandbox(opens in new tab) - a sandbox control plane with Docker and Kubernetes backends, network policy, a credential broker and optional gVisor, Kata or Firecracker isolation
- E2B(opens in new tab) - open-source sandboxes for agents, which you can self-host
- Cube Sandbox(opens in new tab) - Tencent Cloud’s open-sourced sandbox service: KVM microVMs with their own kernel that start in under 60ms, compatible with the E2B SDK
Final thoughts
We live in dangerous times. Multiple open-source projects and enterprise environments get hacked every day. Global attacks have increased by 18% year over year in 2026. Leaving an automation machine with full access to your computer is a serious risk, especially in production environments. Be cautious and sandbox your environments sooner rather than later.






