Jump to content

Claude Code Isolation and Burner Workflow 260211: Difference between revisions

From Game in the Brain Wiki
Rewrite: remove firejail, simplified csb.sh, persistent container workflow with save points
Add YYMMDD naming convention, generic examples, switching between save points
Line 34: Line 34:
* Podman installed on the host
* Podman installed on the host
* A Claude Code account and API access
* A Claude Code account and API access
== Naming Convention ==
Containers and images are named using a short prefix and a date in <code>YYMMDD</code> format. The date identifies when the container or save point was created, making it easy to track your working state over time.
{| class="wikitable"
|-
! Type !! Format !! Example
|-
| Working container || <code>PREFIX-YYMMDD</code> || <code>work-260220</code>
|-
| Saved image (save point) || <code>localhost/PREFIX-YYMMDD:latest</code> || <code>localhost/work-260220:latest</code>
|-
| Golden image (template) || <code>localhost/PREFIX-base:latest</code> || <code>localhost/work-base:latest</code>
|-
| Burner home directory || <code>~/sandbox-homes/PREFIX-YYMMDD</code> || <code>~/sandbox-homes/work-260220</code>
|}
Choose any short prefix that makes sense for your setup. Use the same prefix consistently so your image library stays organised.
'''Example timeline:'''
* You set up a container on the 20th → <code>work-260220</code>, saved as <code>localhost/work-260220:latest</code>
* On the 22nd you want a new save point → commit the running container as <code>localhost/work-260222:latest</code>
* Start a new container from that save point when needed → <code>work-260222</code>
* <code>work-260220</code> is still there — enter it again any time
* Delete whichever images or containers you no longer need


== One-Time Setup: Create the Golden Image ==
== One-Time Setup: Create the Golden Image ==
Line 51: Line 77:
'''[HOST]''' Create a home directory for the base container. Run these as two separate commands:
'''[HOST]''' Create a home directory for the base container. Run these as two separate commands:


  mkdir -p ~/sandbox-homes/claude-base
  mkdir -p ~/sandbox-homes/work-base


  distrobox create --name claude-base --image ubuntu:24.04 --home ~/sandbox-homes/claude-base
  distrobox create --name work-base --image ubuntu:24.04 --home ~/sandbox-homes/work-base


'''[HOST]''' Enter the container:
'''[HOST]''' Enter the container:


  distrobox enter claude-base
  distrobox enter work-base


=== Step 3: Install Claude Code ===
=== Step 3: Install Claude Code ===
Line 102: Line 128:
  exit
  exit


  distrobox stop claude-base
  distrobox stop work-base
  podman container commit claude-base localhost/claude-golden:latest
  podman container commit work-base localhost/work-base:latest


'''[HOST]''' Verify:
'''[HOST]''' Verify:


  podman image ls | grep claude-golden
  podman image ls


You now have a golden image. The base container can be kept or deleted — the image is self-contained.
You now have a golden image. The base container can be kept or deleted — the image is self-contained.
Line 115: Line 141:
=== Starting a New Container ===
=== Starting a New Container ===


When you start a new project or task, clone a fresh container from the golden image.
When starting fresh work, clone a container from the golden image (or any saved image). Use today's date in the name.


'''[HOST]''' Run these as two separate commands:
'''[HOST]''' Run these as two separate commands:


  mkdir -p ~/sandbox-homes/my-project
  mkdir -p ~/sandbox-homes/work-260220


  distrobox create --name my-project --image localhost/claude-golden:latest --home ~/sandbox-homes/my-project
  distrobox create --name work-260220 --image localhost/work-base:latest --home ~/sandbox-homes/work-260220


'''[HOST]''' Enter it:
'''[HOST]''' Enter it:


  distrobox enter my-project
  distrobox enter work-260220


'''[DISTROBOX]''' Launch Claude Code:
'''[DISTROBOX]''' Launch Claude Code:
Line 134: Line 160:
=== Continuing an Existing Container ===
=== Continuing an Existing Container ===


If the container already exists and you want to pick up where you left off, just enter it again:
If the container already exists, just enter it again — it retains its full state:


'''[HOST]'''
'''[HOST]'''


  distrobox enter my-project
  distrobox enter work-260220


'''[DISTROBOX]'''
'''[DISTROBOX]'''
Line 145: Line 171:
  ./csb.sh
  ./csb.sh


No setup needed — the container retains its state between sessions.
=== Saving a Save Point ===
 
At any point before a risky change, after a milestone, or at the end of a working day — commit the container state as a named image.
 
'''[HOST]''' Stop the container:
 
distrobox stop work-260220


=== Saving a Checkpoint (Save Point) ===
'''[HOST]''' Commit to a dated image:


At any point — before a risky change, after a milestone, or just when you want a snapshot — you can save the current container state as an image.
podman container commit work-260220 localhost/work-260222:latest


'''[HOST]''' Stop the container:
'''[HOST]''' Start the container again:
 
distrobox enter work-260220
 
The save point <code>localhost/work-260222:latest</code> is now available. Your original container <code>work-260220</code> is unchanged and still usable.


distrobox stop my-project
=== Switching Between Save Points ===


'''[HOST]''' Commit to a named image:
You can branch off from any saved image. Both lines of work remain independent.


podman container commit my-project localhost/my-project-checkpoint-1:latest
'''Example:''' You have been using <code>work-260220</code>. You save a point as <code>localhost/work-260222:latest</code>. Now you can:


You can create as many checkpoints as you want with different names:
* Keep using <code>work-260220</code> as-is
* Start a new container from the 260222 save point:


  podman container commit my-project localhost/my-project-checkpoint-2:latest
  mkdir -p ~/sandbox-homes/work-260222
distrobox create --name work-260222 --image localhost/work-260222:latest --home ~/sandbox-homes/work-260222
distrobox enter work-260222


'''[HOST]''' Restart the container after saving:
* Go back to <code>work-260220</code> at any time:


  distrobox enter my-project
  distrobox enter work-260220


=== Restoring from a Checkpoint ===
=== Restoring from a Save Point ===


If something goes wrong and you want to roll back to a previous save point:
If a container is broken or you want a clean start from a previous state:


'''[HOST]''' Delete the current container:
'''[HOST]''' Delete the current container:


  distrobox rm my-project
  distrobox rm work-260222
  rm -rf ~/sandbox-homes/my-project
  rm -rf ~/sandbox-homes/work-260222


'''[HOST]''' Create a new container from the checkpoint image:
'''[HOST]''' Re-create it from the save point image:


  mkdir -p ~/sandbox-homes/my-project
  mkdir -p ~/sandbox-homes/work-260222
  distrobox create --name my-project --image localhost/my-project-checkpoint-1:latest --home ~/sandbox-homes/my-project
  distrobox create --name work-260222 --image localhost/work-260222:latest --home ~/sandbox-homes/work-260222


=== Promoting a Container to the Golden Image ===
=== Promoting to the Golden Image ===


If a container has a set of tools, config, or improvements you want all future containers to inherit, promote it to the golden image:
If a container has reached a state you want all future containers to start from, promote it:


'''[HOST]'''
'''[HOST]'''


  distrobox stop my-project
  distrobox stop work-260222
  podman container commit my-project localhost/claude-golden:latest
  podman container commit work-260222 localhost/work-base:latest


From now on, new containers cloned from <code>localhost/claude-golden:latest</code> will include those changes.
New containers cloned from <code>localhost/work-base:latest</code> will now include those changes.


=== Managing Images ===
=== Managing Your Image Library ===


'''[HOST]''' List all saved images:
'''[HOST]''' List all images:


  podman image ls
  podman image ls
Line 200: Line 239:
'''[HOST]''' Delete an image you no longer need:
'''[HOST]''' Delete an image you no longer need:


  podman image rm localhost/my-project-checkpoint-1:latest
  podman image rm localhost/work-260220:latest


'''[HOST]''' List all containers (running and stopped):
'''[HOST]''' List all containers:


  podman ps -a
  podman ps -a


'''[HOST]''' Delete a container and its burner home when you are done with a project:
'''[HOST]''' Delete a container and its home when you are done:


  distrobox rm my-project
  distrobox rm work-260220
  rm -rf ~/sandbox-homes/my-project
  rm -rf ~/sandbox-homes/work-260220


== What Distrobox Isolation Provides ==
== What Distrobox Isolation Provides ==
Line 217: Line 256:
! Surface !! Isolated? !! Notes
! Surface !! Isolated? !! Notes
|-
|-
| Host home directory || ✅ Yes || Container uses its own burner home via <code>--home</code>; real <code>~</code> is never touched
| Host home directory || ✅ Yes || Container uses its own burner home via <code>--home</code>; <code>/home/USER</code> is never touched
|-
|-
| Host filesystem via <code>/run/host</code> || ⚠️ Partial || Mounted read-write by default. Add <code>--additional-flags "--mount type=bind,source=/,target=/run/host,ro"</code> at container creation to make it read-only
| Host filesystem via <code>/run/host</code> || ⚠️ Partial || Mounted read-write by default. Add <code>--additional-flags "--mount type=bind,source=/,target=/run/host,ro"</code> at container creation to make it read-only
Line 236: Line 275:
* '''Safety with high permissions:''' If Claude Code runs <code>rm -rf</code> or installs 50 packages, your main system is untouched. The damage stays inside the container.
* '''Safety with high permissions:''' If Claude Code runs <code>rm -rf</code> or installs 50 packages, your main system is untouched. The damage stays inside the container.
* '''Dependency hygiene:''' Agents often install tools to complete tasks. Distrobox keeps this inside the box. Delete the container when you are done with the project.
* '''Dependency hygiene:''' Agents often install tools to complete tasks. Distrobox keeps this inside the box. Delete the container when you are done with the project.
* '''Save points for risky work:''' Before letting an agent attempt something uncertain, commit a checkpoint. If it breaks the container, roll back and try a different approach.
* '''Save points for risky work:''' Before letting an agent attempt something uncertain, commit a save point. If it breaks the container, restore from the save point and try a different approach.
* '''Better integration than Docker:''' Unlike raw Docker, Distrobox integrates naturally with your terminal environment while still keeping the execution environment separate.
* '''Better integration than Docker:''' Unlike raw Docker, Distrobox integrates naturally with your terminal environment while still keeping the execution environment separate.



Revision as of 07:40, 22 February 2026

Claude Code Isolation with Distrobox — Burner Workflow

Overview

This guide documents how to run Claude Code in an isolated environment using Distrobox containers. The core idea:

  • Each project or task lives in its own container — isolated from the host system.
  • Containers are persistent. You enter them, do work, and come back later.
  • At any point you can save the current state as an image — a snapshot you can restore from or clone.
  • A golden image (or template) is a clean, pre-configured base you clone new containers from.
  • You delete containers and images on your own schedule, when you no longer need them.

Think of it like save points in a game: you can keep playing from where you left off, and save whenever you want a checkpoint.

This protects against malicious prompt injection by limiting what Claude Code can access — any damage from a bad agent run stays inside the container and does not touch the host.

Command Context

Every command in this guide is prefixed with where it must be run:

Prefix Meaning
[HOST] Run this in a terminal on your normal Linux desktop, outside any container
[DISTROBOX] Run this inside the Distrobox container after entering it

Prerequisites

  • A Linux host (Fedora, Ubuntu, Arch, etc.)
  • Distrobox installed on the host
  • Podman installed on the host
  • A Claude Code account and API access

Naming Convention

Containers and images are named using a short prefix and a date in YYMMDD format. The date identifies when the container or save point was created, making it easy to track your working state over time.

Type Format Example
Working container PREFIX-YYMMDD work-260220
Saved image (save point) localhost/PREFIX-YYMMDD:latest localhost/work-260220:latest
Golden image (template) localhost/PREFIX-base:latest localhost/work-base:latest
Burner home directory ~/sandbox-homes/PREFIX-YYMMDD ~/sandbox-homes/work-260220

Choose any short prefix that makes sense for your setup. Use the same prefix consistently so your image library stays organised.

Example timeline:

  • You set up a container on the 20th → work-260220, saved as localhost/work-260220:latest
  • On the 22nd you want a new save point → commit the running container as localhost/work-260222:latest
  • Start a new container from that save point when needed → work-260222
  • work-260220 is still there — enter it again any time
  • Delete whichever images or containers you no longer need

One-Time Setup: Create the Golden Image

Run these steps once. The result is a golden image — a clean, pre-configured base you will clone all future work containers from.

Step 1: Install Distrobox

[HOST]

sudo apt install distrobox    # Debian/Ubuntu
sudo dnf install distrobox    # Fedora
yay -S distrobox              # Arch (AUR)

Step 2: Create and Enter the Base Container

[HOST] Create a home directory for the base container. Run these as two separate commands:

mkdir -p ~/sandbox-homes/work-base
distrobox create --name work-base --image ubuntu:24.04 --home ~/sandbox-homes/work-base

[HOST] Enter the container:

distrobox enter work-base

Step 3: Install Claude Code

[DISTROBOX]

curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
sudo apt install -y nodejs
npm install -g @anthropic-ai/claude-code

[DISTROBOX] Log in and verify:

claude

Complete the authentication flow. Your credentials are stored inside the container.

Step 4: Add the Launcher Script

[DISTROBOX] Create a project directory and the launcher script:

mkdir -p ~/project
nano ~/project/csb.sh

Contents:

#!/bin/bash
WORK_DIR="$(cd "$(dirname "$0")" && pwd)"
cd "$WORK_DIR"
echo "Starting Claude Code..."
echo "  Working directory: $WORK_DIR"
echo ""
exec claude "$@"

[DISTROBOX] Make it executable:

chmod +x ~/project/csb.sh

Step 5: Save as the Golden Image

[HOST] Exit the container, then stop and commit it:

exit
distrobox stop work-base
podman container commit work-base localhost/work-base:latest

[HOST] Verify:

podman image ls

You now have a golden image. The base container can be kept or deleted — the image is self-contained.

Daily Workflow

Starting a New Container

When starting fresh work, clone a container from the golden image (or any saved image). Use today's date in the name.

[HOST] Run these as two separate commands:

mkdir -p ~/sandbox-homes/work-260220
distrobox create --name work-260220 --image localhost/work-base:latest --home ~/sandbox-homes/work-260220

[HOST] Enter it:

distrobox enter work-260220

[DISTROBOX] Launch Claude Code:

cd ~/project
./csb.sh

Continuing an Existing Container

If the container already exists, just enter it again — it retains its full state:

[HOST]

distrobox enter work-260220

[DISTROBOX]

cd ~/project
./csb.sh

Saving a Save Point

At any point — before a risky change, after a milestone, or at the end of a working day — commit the container state as a named image.

[HOST] Stop the container:

distrobox stop work-260220

[HOST] Commit to a dated image:

podman container commit work-260220 localhost/work-260222:latest

[HOST] Start the container again:

distrobox enter work-260220

The save point localhost/work-260222:latest is now available. Your original container work-260220 is unchanged and still usable.

Switching Between Save Points

You can branch off from any saved image. Both lines of work remain independent.

Example: You have been using work-260220. You save a point as localhost/work-260222:latest. Now you can:

  • Keep using work-260220 as-is
  • Start a new container from the 260222 save point:
mkdir -p ~/sandbox-homes/work-260222
distrobox create --name work-260222 --image localhost/work-260222:latest --home ~/sandbox-homes/work-260222
distrobox enter work-260222
  • Go back to work-260220 at any time:
distrobox enter work-260220

Restoring from a Save Point

If a container is broken or you want a clean start from a previous state:

[HOST] Delete the current container:

distrobox rm work-260222
rm -rf ~/sandbox-homes/work-260222

[HOST] Re-create it from the save point image:

mkdir -p ~/sandbox-homes/work-260222
distrobox create --name work-260222 --image localhost/work-260222:latest --home ~/sandbox-homes/work-260222

Promoting to the Golden Image

If a container has reached a state you want all future containers to start from, promote it:

[HOST]

distrobox stop work-260222
podman container commit work-260222 localhost/work-base:latest

New containers cloned from localhost/work-base:latest will now include those changes.

Managing Your Image Library

[HOST] List all images:

podman image ls

[HOST] Delete an image you no longer need:

podman image rm localhost/work-260220:latest

[HOST] List all containers:

podman ps -a

[HOST] Delete a container and its home when you are done:

distrobox rm work-260220
rm -rf ~/sandbox-homes/work-260220

What Distrobox Isolation Provides

Surface Isolated? Notes
Host home directory ✅ Yes Container uses its own burner home via --home; /home/USER is never touched
Host filesystem via /run/host ⚠️ Partial Mounted read-write by default. Add --additional-flags "--mount type=bind,source=/,target=/run/host,ro" at container creation to make it read-only
System packages ✅ Yes Container uses its own overlay filesystem
Network ❌ No Container shares the host network namespace. Claude Code requires network access to reach the Anthropic API
Linux kernel ❌ No Rootless containers share the host kernel (acceptable for most threat models)
X11/Wayland display ❌ No GUI apps render on the host desktop

Why the Burner Concept

The Burner Workflow is designed to give Claude Code extensive permissions — auto-allow mode, running system commands, installing packages — without risking your actual computer.

  • Safety with high permissions: If Claude Code runs rm -rf or installs 50 packages, your main system is untouched. The damage stays inside the container.
  • Dependency hygiene: Agents often install tools to complete tasks. Distrobox keeps this inside the box. Delete the container when you are done with the project.
  • Save points for risky work: Before letting an agent attempt something uncertain, commit a save point. If it breaks the container, restore from the save point and try a different approach.
  • Better integration than Docker: Unlike raw Docker, Distrobox integrates naturally with your terminal environment while still keeping the execution environment separate.

Can You Skip Distrobox?

  • Yes, if: You are just testing Claude Code and will manually approve every command (the default safe mode).
  • No, if: You want to use autonomous mode — skipping permission prompts or letting the agent freely install tools. In that case, skipping Distrobox is dangerous and defeats the purpose of this guide.

References

Session Notes 260222 — Testing & Fixes

1. mkdir -p and distrobox create must be run as separate commands

Pasting them as a single line fails silently. Always run them separately on the host.

2. $HOME resolves incorrectly inside --home containers

When distrobox is created with --home pointing to a burner directory, the container's $HOME becomes that directory — not the real user home. Any launcher script variables using $HOME (like CLAUDE_DIR and NVM_DIR) will resolve to wrong paths.

Fix: Hardcode absolute paths in the launcher script if needed.

3. Firejail is incompatible with distrobox --home workflow

Firejail fails with no suitable ...bin/claude executable found inside distrobox when using a custom --home directory. The cause is firejail's whitelist mode blocking Node.js runtime dependencies that Claude Code requires.

Fix: Drop firejail. Distrobox with --home provides sufficient filesystem isolation for the burner workflow. The simplified csb.sh above is the current recommended launcher.

4. Backup script may produce duplicate image files

Manual podman save and a skip-duplicates backup script may use different filename conventions (e.g. imagename_latest.tar.gz vs localhost_imagename_latest.tar.gz), resulting in duplicate files on the backup destination. Check for and remove duplicates after any manual save.

5. @reboot cron needs sleep 30

Network filesystem mounts (e.g. GVFS SMB) are not ready immediately on boot. Without a sleep delay, backup scripts triggered via @reboot cron will fail silently with a "destination not mounted" error. Add sleep 30 before the backup command.