davidjk.net

AI workspace isolation

Draft · updated

A safe place to experiment with autonomous coding agents: each one gets its own isolated workspace, its own credentials, and nothing of mine it doesn't need.

Why

The project started the first time I watched Claude ask to go looking through my home folder for passwords. It was asking, not doing, but it was enough. I knew I didn't want agents running on my machine without real isolation.

First attempt: wrappers

My first move was to put wrappers around the commands an agent could use. git-auto could commit but not push. jira-auto allowed only a handful of Jira commands.

It worked, up to a point. When I wanted the agent to have its own git credentials, separate from mine, I hit a wall: tools look for credentials in the home folder, and the agent shared my home folder. More to the point, wrappers limit what an agent is supposed to do. They don't stop it reading anything else on the machine. That isn't isolation.

Containers

So each workspace became a Docker container. The popular coding harnesses are baked in (Claude Code, Codex and OpenCode), and the agent gets a home folder of its own, with its own git identity.

Talking to it

Isolation is only half the problem. I also needed a way to work with the agents and to leave sessions running. So I added tmux for persistent sessions, then put the terminals in the browser with ttyd, behind a small web server inside the container.

Browser to container: Caddy, ttyd, tmux and agents, with a bind-mounted workspace browser localhost caddy front door ttyd web terminal tmux persistent sessions agents claude · codex · opencode /workspace bind mount from the host container own home, own credentials, reset on every start
Fig. 1 — One workspace. Only /workspace outlives the container.

The viewers

Once Caddy was in place, I split the browser page in two: the agent's terminal below, and a pane above showing a file from the workspace. The pane checks the file with a lightweight HEAD request every couple of seconds and reloads when it changes, so edits show up as they land. The agent can drive the pane itself, opening whatever it wants me to look at.

Then came custom viewers: rendered Markdown, highlighted source code with line numbers, and draw.io diagrams. HTML pages render as they are. And because the file server can also answer with JSON, an agent can build a single-page HTML app that reads its data straight from the workspace.

That changed how I work. Now I can dictate to Claude or Codex and watch them work on my diagram live.

An agent edits files in the workspace; Caddy serves them; the viewer pane polls with HEAD requests and reloads on change agent claude · codex /workspace files on disk caddy /files/ · raw or json opens a file content HEAD ~2s viewer pane reloads on change markdown · draw.io code · html json for html apps terminal tmux · ttyd browser writes
Fig. 2 — The viewer pane checks for changes with HEAD requests and reloads, so edits appear as the agent makes them.
walkthrough video to come
Fig. 3 — A tour of a workspace session.

Still to write