- Isolated Docker container to keep your OS and filesystem safe from malicious prompt injections which may occur during development.
- Uses named volume (rather than bind mount) for an air gap such that Claude cannot touch your file system's files.
- Lightweight.
- Easily clone the state of one container to new ones
- Not using Docker Desktop AI Sandbox due to it's black box-iness.
- Update
/exampleto use latest setup-project.sh - Update this README and
/docsto reflect latest adjustments - Clean up and better realize the alternate setup scripts.
⚠ Use at your own risk! ⚠
I am not responsible for any harm or loss that occurs while using this script or the containers it has created. Use your own caution when dealing with AI tools such as Claude, which might get smart enough to escape the container.
This script has been tested on (so far):
- Windows (WSL)
- Windows (Git BASH)
- Linux
- Mac
For extra security, to prevent "container scapes", make sure the UID for the user within the container (username: node), doesn't match the UID of the user running the container.
The default generated Dockerfile in this script maps user node to UID 5001. Check what your UID is and change the UID for node if it still matches.
# Outside of the container (WSL or Git BASH)
# Get your username
whoami
# Get your ID
id -u <YOUR_USERNAME>
# OR (Alternative method)
echo $UIDWhile alternatives like Podman offer rootless security by default, Code Vault uses Docker for several key reasons:
- WSL/Windows Stability: Docker Desktop provides the most reliable bridge for volume mounting and networking on Windows hosts.
- Ecosystem Compatibility: Many AI agents and development tools expect a Docker-standard environment to function correctly.
- Predictable Permissions: The UID/GID mapping (UID 5001) implemented in this script provides a high level of isolation on the host without the complexity of Podman's sub-UID management.
In short: Docker allowed us to build a "Vault" that is secure without being impossible to use on a daily basis.
For Windows, install Docker Desktop first.
- Run
setup-project.shin a new project directory and follow the instructions. - (Optional): skip naming step by passing a project name as first argument.
sh setup-project.sh <project_name>
- (Optional): automatically build the container with
--build(-b) as 2nd argument.- (Windows) Ensure the Docker Desktop is running.
sh setup-project.sh <new_project_name> -b- This will also automatically run
git initandnpm init -yfrom the/appdirectory inside the sandbox, as well as inject a Git-ignored.envfile there.
- Create project directory
- Copy included
Dockerfile&docker-compose.ymlto project directory- SECURITY NOTE:
tmpfs: /tmpis required because the filesystem is read-only - If desired, update:
- Node version (
Dockerfile) - Container and named volume names (YML file)
- NOTE: named volume name has 2 fields to update in the YML file
- Exposed ports (YML file)
- Node version (
- SECURITY NOTE:
- Build & start the container
# Build (also run this if changes are made to the config)
docker compose up -d --build- Enter the sandbox with the
container_namein the YML file - Initialize npm project and Git
- If using Claude Code (API key):
- A
.envfile should already exist. If not, copy the included.env.exampleto/app/.env - Add API key to
.envfile- The container will automatically use this key for all prompts.
- A
- Run Claude
- If using Claude Pro subscription:
- Log in with the
/logincommand, if not prompted. - Go to magic login link provided in your external browser
- Paste the generated string into the prompt
- Since
/home/nodeis a temporary memory-disk, you will have to log in each time
- Log in with the
# Start the cached version of the container if not already started
docker compose up -d
# Enter the sandbox
docker exec -it [project_name]_container bash
# Init project (first time only)
npm init -y
git init
# Run Claude
claudeEverything will be safe in the [project_name]_data folder, even if the container is deleted, or Docker is updated, or your computer is turned off.
Use the VSCode Dev Containers extension to edit, save, see Claude's changes in real-time:
- Install "Dev Containers" extension in VSCode
- Start the container
- Click
><icon in VSCode (bottom-left corner) - Select "Attach to Running Container..." and select the container
- VSCode will open a new window
- Looks like local setup
- But actually you're inside the Docker volume
# Stop and keep container ready
docker compose stop
# OR...
# Stop and remove container
# (but keeps data thanks to named volume)
docker compose down| Alias | Effect |
|---|---|
la |
Lists all files (including hidden ones) in long format (-la). |
lsg |
Lists files with directories shown first. |
.. |
Moves up one directory (cd ..). |
... |
Moves up two directories (cd ../..). |
gfp |
Fetches all Git remotes and prunes any deleted branches. |
gco |
git checkout, e.g., gco -b feat/new-fature |
gs |
git status |
ga |
git add, e.g., ga ./src |
gd |
git diff |
gds |
git diff --staged |
gl |
Shows a compact, graphical git log of all branches (git log --oneline --graph --all). |
c-exit |
Creates /app/.vault_memory if needed, copies Claude’s memory files there, then exits the shell. |
To add/edit/remove, you can either edit the generated Dockerfile per project or update script-setup.sh. Search for # Helpful aliases. Rebuild the container in order to use your changes next time you use the container.
| Alias | Command | Purpose |
|---|---|---|
c-up |
docker compose up -d |
Start container |
c-down |
docker compose down |
Destroy container (Data/Memory is safe in Volume) |
c-enter |
docker exec -it $(basename "$PWD")_container bash |
Jump in |
c-logs |
docker compose logs -f |
View container logs |
| Name | Command | Purpose |
|---|---|---|
| Import | sh import.sh |
Bulk import code from host to container |
| Backup | sh backup.sh |
Backup entire project volume (.tar.gz) |
| Backup Memory | sh backup-memory.sh |
Snapshot Claude's memory to host |
The Vault uses a "Warm Start" architecture to keep your AI's context persistent, even though the container's OS (/home/node) is reset every time you restart.
- Startup: The container automatically pulls your last saved
.claudememory from the secure volume into the fast RAM disk. - Work: You coding with Claude (everything happens in RAM).
- Exit (
c-exit): CRITICAL step. Instead of typingexit, typec-exitinside the container. This syncs your RAM session back to the secure volume before closing.
To create a permanent, timestamped snapshot of Claude's memory on your Windows host (for long-term storage):
sh backup-memory.sh- Location:
./memory_backup/YYYYMMDD_HHMMSS/ - Contains: Global tool state (
.claude) and project settings (.claude.json).
If you have an existing project folder on Windows that you want to move into a Vault:
- Run
setup-project.sh my-project -bto create an empty Vault. - Copy your existing code files into that new folder.
- Run the import script:
# Handle the `docker cp` & permission fixes automatically
sh import.shBecause of Named Volumes usage, the data is essentially a portable "brain" that can be plugged into any body (container) built.
- Export the Source:
- Run sh backup.sh in the project folder to create the .tar.gz file.
- Create the Target:
- Run ./setup-project.sh [new_project_name] -b to create a fresh, empty environment.
- Transfer the "Brain":
- Copy the backup file from the previous project folder into the new one.
- Inject the State:
- Run sh restore.sh inside the new project folder and select the copied backup file.
- Docker containers are isolated by default, but to see your project's dev server on other devices, use the
--hostflag from within the container.- Example 1 (Vite):
npm run dev -- --host 0.0.0.0 - Example 2 (Astro):
npm run dev -- --host
- Example 1 (Vite):
- Find your computer's IP then navigate to it from any device on your network, including the port number.
- Example:
http://192.168.1.22:5173
- Example:
"Zip it up, and zip it out!"
- Use the generated backup and restore shell scripts or do it manually with the commands below.
# Backup
docker run --rm -v my_project_storage:/source -v $(pwd):/backup alpine tar czf /backup/backup.tar.gz -C /source .
# Restore
docker compose stop
docker run --rm -v your_project_data:/dest -v $(pwd):/backup alpine sh -c "rm -rf /dest/* && tar xzf /backup/your_backup_file.tar.gz -C /dest"
docker compose up -d- Something stuck? Won't start after a restore?
docker compose logs -fwill help find exactly what the error could be - Ensure you are using
docker compose(no hyphen), as the docker-compose command is deprecated. - If you encounter permission issues, ensure you haven't changed the
USER_UIDin the script to match your host UID, as this can cause mount conflicts in WSL
The container uses a 'Hydration' system. To permanently change aliases or shell settings, you must update the Dockerfile and rebuild, as /home/node is a temporary memory-disk for security reasons.
- Always run the npm commands inside the container
- Check file permissions
- If you get "Permission Denied", run the following:
docker exec -u root [container_name] chown -R node:node /appdocker compose restart(recommended after permissions changes)
- Don't forget the
--hostflag to see the server from outside of the container - Use
git initimmediately so Claude can rely on it to see what got changed - Clear out old, unused image layers once a month to save space:
docker system prune- !! WARNING !!: Using the
-aflag will also delete your volumes!
- Clear the build cache:
docker builder prune -a - Clear orphaned networks:
docker network prune - Update digests and hashes every 6 months or so to known-good versions
Want to use a different AI agent (like Aider, OpenDevin, or Mentat) inside the Vault? I'd love to see your configurations!
If you've successfully adapted the setup-project.sh for another tool, please:
- Open an Issue with the title
Agent: [Agent Name] - Share your Dockerfile snippet and any specific
tmpfsmounts required.
To keep the "Vault" secure, all proposed agent configs must:
- Use a non-root user (mapped to something like a UID of 5001).
- Support a read-only filesystem (using
tmpfsfor caches). - Stay minimal (based on
-slimimages where possible).
