JG
Johannes Gramm
2 October 2026

Agent Lab: A Sandbox Where AI Agents Can Do Anything Except Get Out

The Idea

AI coding agents are most useful when you let them work: install packages, build containers, run tests, spend hours on a branch without me approving every command. I don't want that on my own machine. My data lives there, and my home network can be reached from it.

The Agent Lab is my answer: a dedicated machine for agents on two Proxmox mini PCs, reachable from anywhere via SSH, with no open port on the router. These are the ideas behind it.

1. Define the protection goal first, then the measures

The most important decision was what I'm actually protecting against. The answer: what happens on the mini PCs stays there. My workstation and the home network stay out of reach.

The agents are explicitly not protected from each other. They all run as the same user, with no egress firewall and no per-agent sandbox. That's deliberate, because each of those barriers would have gotten in the way of the agents' actual work. A clearly stated protection goal turns gaps like these into decisions instead of oversights.

That's why the boundary runs between the hypervisor and the VM, not between machines. Inside the VM, agents may do anything. The Proxmox hosts are off limits, because one of them runs the DNS filter for the whole household. Whoever controls that controls name resolution for every device in the house.

2. Two barriers, both measured

A single barrier is one configuration you can get wrong once. So there are two independent ones:

  • ▸On the overlay network (Tailscale): the agent VM carries its own tag, which never appears as a source in the access rules. I can reach the VM, but the VM can reach nothing. Only what's explicitly allowed is permitted.
  • ▸On the local network: the VM isn't on the home network. It sits on its own bridge with its own subnet. Traffic to the internet goes through NAT; traffic to the home network and to the host's management ports is rejected. The VM no longer has a network card on the home network at all.

"Measured" is meant literally. From inside the VM I tried every target one by one (router, DNS filter, hypervisor UI, workstation) and only moved on once I got a rejection. A rule that only exists in a config file is an assumption.

One exception came later, and it taught me the most. The barrier to the home network also dropped the VM's replies. As a result, Tailscale ran through a relay in Paris even at home, at almost 60 ms latency. The fix is a rule that only lets through replies to connections a home-network device opened itself. The VM may answer, but never start. Latency dropped to 1 ms.

3. No open port means someone else accepts the connection

For my laptop to get home from outside, some device with a public address has to accept the connection. There are exactly three candidates: an open port at home, my own server on the internet, or someone else's rendezvous service. The port and the server were ruled out, so the third one remained.

At first I wanted to run the coordination server myself (Headscale) and make it reachable through a Cloudflare Tunnel. That fails technically: the tunnel strips the Upgrade header on POST requests, which the Tailscale protocol depends on. This applies to any HTTP-level tunnel. But I dropped the idea for good for a different reason. A self-hosted coordination server is one more service I'd have to secure, update and monitor, and that would be the weakest link. I'm staying with the hosted service.

Public applications, on the other hand, run through a Cloudflare Tunnel. It opens the connection from the inside out, my home IP isn't in any DNS record, and an application is only reachable if it's explicitly listed.

4. Parallel agents need separate workspaces

Several agents in the same working directory go wrong; I'd already seen that in another project. So here the separation is built in from the start:

  • ▸One agent, one git worktree. A small script clones a repo once and creates a separate worktree per task. Every agent has its own branch and its own directory.
  • ▸A multiplexer instead of open terminals. The agents run in a terminal multiplexer that starts as a service on the VM. I can detach, close the laptop, and the agents keep working. Later I reconnect from the workstation or the laptop.
  • ▸Real SSH keys, not the VPN service's SSH login. That way the multiplexer and VS Code Remote read the same ssh_config, and access doesn't depend on a single provider.

5. Treat system and data separately

The VM has two disks with different jobs. The system disk is backed up. The data disk with repos, worktrees and Docker is excluded from backups, on purpose. The code belongs on GitHub. Anything that only exists in a worktree and hasn't been pushed isn't backed up either. That rule forces a good habit.

The same goes for cleanup: a weekly job deletes only what can be recreated, meaning old images, build cache and stopped containers. It never touches volumes, repos or worktrees. Automatically deleting data does damage sooner or later.

What I deliberately left out is documented just like what's running. One example is direnv: it automatically runs files from the repo, and with code that agents write, that's an unnecessary risk.

6. The network protects the network, not the keys

This one was uncomfortable: all the network separation protects the home network, but not a single API key on the VM. Every agent can read what the shared user can read.

That leads to a simple rule. A key on the VM is limited to one service, has minimal permissions and a spending limit, and can be revoked in minutes. In the worst case, the service behind the key is at risk, never the network. This learning log is an example: the agent that created this entry can only write drafts with its token. Only I can publish.

7. Small lessons along the way

  • ▸Memory shown isn't memory used. Proxmox showed almost 12 GB for the VM while it was using just over 1 GB. I had turned memory ballooning off completely to get a fixed allocation. But that also removed the device the guest uses to report free pages. The right setting is minimum equals maximum: the allocation stays fixed, and free pages still go back to the host.
  • ▸A DNS server doesn't belong on DHCP. For months the router handed out a DNS address the filter no longer had. The household only kept working because devices also got a different DNS server over IPv6, and almost nothing was filtered during that time.
  • ▸Two services, one port, one problem. Behind the same NAT, the host and the VM used the same Tailscale port and so shared one mapping in the router. Since the VM got its own port, the connection stays direct.
  • ▸The disk numbers were surprising. For random writes, the old SATA SSD was faster than the worn NVMe. That's why the write-heavy work now lives on the SATA disk.

Where it stands

The sandbox is in daily use: agents work in parallel in their own worktrees, and I connect from the workstation or on the road. Two things are still open, and I want to name them honestly. So far the system backup only lives on the same disk as the VM; there's no offsite copy. And I've never tested a restore. Until then, a detailed inventory of the running VM is the recipe for a rebuild, but it isn't a script yet.

To be continued.

ai-agentshomelabproxmoxtailscalenetworkingsecurityclaude-code
0/3+
Agent Lab: A Sandbox Where AI Agents Can Do Anything Except Get Out - Learning Log