Sandcat
A Docker & dev container setup for securely running AI agents in `--dangerous` mode. All container traffic is routed through a transparent mitmproxy, enforcing network access rules and injecting secrets.
Install / Use
npx skills add VirtusLab/sandcatInstalls into whichever agent you are using.
Quality Score
Category
Development & EngineeringSupported Platforms
README
Sandcat
Sandcat is a Docker & dev container setup for securely running AI agents. The environment is sandboxed, with controlled network access and transparent secret substitution. All of this is done while retaining the convenience of working in an IDE like VS Code.
All container traffic is routed through a transparent mitmproxy via WireGuard, capturing HTTP/S, DNS, and all other TCP/UDP traffic without per-tool proxy configuration. A straightforward allow/deny list-based engine controls which network requests go through, and a secret substitution system injects credentials at the proxy level so the container never sees real values.
This repository contains:
- a bash CLI to initialize the sandbox for a project, copying and customizing
the necessary files (see
cli/) - reusable proxy definitions under
cli/templates/devcontainer/sandcat/:Dockerfile.wg-client,compose-proxy.yml, andscripts/that perform the network filtering & secret substitution - template application and dev container configuration under
cli/templates/devcontainer/:Dockerfile.app,compose-all.yml,devcontainer.json. This should be fine-tuned for each project and specific development stack, to install required tools and dependencies.
Sandcat can be used as a devcontainer setup, or standalone, providing a shell for secure development.
Sandcat is part of Visdom, VirtusLab's AI-driven software delivery infrastructure.
Quick start
1. Install sandcat CLI
The CLI is a helper script and thin wrapper around docker-compose that simplifies the process of initializing and starting the sandbox.
It has two main tasks:
- copy the necessary configuration files from the
cli/templatesdirectory into your project and customize them based on your choices (development stack, etc.) - run
docker composecommands with the correct compose file automatically detected, so you don't have to remember the file names or paths.
Shell installer (recommended)
Install sandcat CLI to ~/.local/share/sandcat/ with a launcher symlink
at ~/.local/bin/sandcat. Requires yq (Mike Farah's Go variant) already
installed on the host — see yq prerequisite below.
curl -fsSL https://raw.githubusercontent.com/VirtusLab/sandcat/master/install.sh | sh
Ensure ~/.local/bin is on your PATH (the installer prints a hint if it
isn't), then jump to Initialize the sandbox.
Upgrade: re-run the same command. The installer atomically swaps the
existing install; ~/.config/sandcat/ (user settings) is never touched.
Combine with SANDCAT_REF to jump to a different branch/tag/commit:
curl -fsSL https://raw.githubusercontent.com/VirtusLab/sandcat/master/install.sh | SANDCAT_REF=v1.0.0 sh
curl -fsSL https://raw.githubusercontent.com/VirtusLab/sandcat/master/install.sh | SANDCAT_REF=abc123 sh
Custom paths (env overrides), e.g. system-wide install:
SANDCAT_HOME=/opt/sandcat SANDCAT_BIN_DIR=/usr/local/bin \
curl -fsSL https://.../install.sh | sudo -E sh
Non-interactive mode (CI):
curl -fsSL https://.../install.sh | SANDCAT_NON_INTERACTIVE=true sh
Uninstall (preserves user config and Docker state):
bash <(curl -fsSL https://raw.githubusercontent.com/VirtusLab/sandcat/master/install.sh) --uninstall
Env overrides in one place:
| Var | Default | Purpose |
|---|---|---|
| SANDCAT_HOME | $HOME/.local/share/sandcat | Install root |
| SANDCAT_BIN_DIR | $HOME/.local/bin | Launcher symlink dir |
| SANDCAT_REF | master | Branch / tag / commit to fetch |
| SANDCAT_NON_INTERACTIVE | false | Skip all prompts (CI) |
Alternative: git clone
For contributors, or if you prefer to track a working tree directly:
# Clone the repo
git clone https://github.com/VirtusLab/sandcat.git
# Add the sandcat bin directory to your path (add this to your .bashrc or .zshrc)
export PATH="$PWD/sandcat/cli/bin:$PATH"
Update via git pull in the cloned directory.
yq prerequisite
yq is required to edit compose files. Sandcat uses Mike Farah's Go yq; the unrelated Python yq (kislyuk/yq) is not compatible.
On Debian/Ubuntu, apt install yq installs the Python variant. Install Mike Farah's yq instead — for example snap install yq, or download a binary from the release page. Homebrew and Alpine apk already ship the correct one.
2. Initialize the sandbox for your project
sandcat init
This prompts you to select the agent type, IDE (for devcontainer mode), and development stacks to install. You can also pass flags to skip prompts:
sandcat init --agent claude --ide vscode --stacks "python,node"
# With optional features (proxy TUI, 1Password integration)
sandcat init --secret-provider 1password --agent claude --ide vscode
Available agents:
claude(Claude Code CLI)cursor(Cursor IDE)codex(OpenAI Codex CLI — https://github.com/openai/codex)
Available stacks: node, python, java, rust, go, scala, ruby,
dotnet, zig. Versions default to LTS where available (e.g. Node.js LTS,
Java LTS 25). To change a version for a single project, add the desired
package to .devcontainer/devbox.tools.json — see Stack and tool packages
via devbox below for how tool entries
override stack defaults.
Selecting scala automatically includes java as a dependency. Stacks also
install the corresponding VS Code extension (e.g. rust-analyzer for Rust,
metals for Scala).
Stack and tool packages via devbox
All packages inside the sandbox — both stack toolchains and user tools —
are managed with devbox, which resolves
them from Nix. sandcat init generates two config files side by side in
.devcontainer/:
devbox.stack.json — sandcat-managed. Regenerated on every
sandcat init from the --stacks selection plus a baseline of shell tools
every sandbox needs (fd, fzf, gh, jq, ripgrep, tmux, vim).
Do not edit by hand — your changes will be overwritten on the next init.
devbox.tools.json — user-managed. Written once with an empty
packages list; subsequent sandcat init invocations leave it untouched.
Add project-specific tools here.
At image build time the two files are merged into a single devbox global
config. devbox.tools.json wins over devbox.stack.json on:
- Same package name — the
@prefix. Putnodejs@22.5.1in tools to replace the stack'snodejs(without a specifier,nodejsrefers to lts). - Cross-family collisions — tools packages providing the same file
as a stack package win too. Put
openjdk17@latestin tools to make it the active Java over the stack'stemurin-bin-25@latest; the agent'sjava,JAVA_HOMEand the injected mitmproxy CA all resolve to the tools JDK.
Non-overriding tools entries just add to the merged config. Search available packages on nixhub.io.
Example — give the agent yq,
shellcheck, and
hyperfine by dropping them into
devbox.tools.json:
{
"packages": ["yq-go@latest", "shellcheck@latest", "hyperfine@latest"]
}
Then rebuild the agent image:
sandcat run --build
# or, without starting the full stack:
docker compose -f .devcontainer/compose-all.yml build agent
Every shell inside the sandbox — including the agent's — picks up the
packages on PATH. Iterating on devbox.tools.json is the fast path:
the stack install layer stays cached and only the delta downloads
(typically seconds).
Installs are build-time only: devbox add inside the sandbox is not
supported, and no Nix download hosts are added to the network allowlist.
To pin the exact package versions across environments, commit
.devcontainer/devbox.lock next to the JSON files; the build picks it up
automatically.
Optional volume mounts (agent config, .git, .idea) are written into the
generated .devcontainer/compose-all.yml. See Customizing optional volume
mounts below. For scripted sandcat init,
set SANDCAT_* environment variables (see the CLI README).
Customizing optional volume mounts
sandcat init adds optional bind-mounts to services.agent.volumes in
.devcontainer/compose-all.yml. Each mount is an independent line — you can
enable or disable individual paths by editing that file after init. This
works the same way for Claude and Cursor; there are no per-folder sandcat init
flags today.
All-or-nothing at init time (scripted workflows only):
| Agent | Environment variable | Default |
|-----------|-------------------------------|-----------------------------------------|
| Claude | SANDCAT_MOUNT_CLAUDE_CONFIG | true |
| Cursor | SANDCAT_MOUNT_CURSOR_CONFIG | true |
| Codex | SANDCAT_MOUNT_CODEX_CONFIG | true |
| Any | SANDCAT_MOUNT_GIT_READONLY | false (commented in compose) |
| JetBrains | SANDCAT_MOUNT_IDEA_READONLY | false (active when --ide jetbrains) |
| Any | SANDCAT_MOUNT_SHARED_CACHE | true — see Shared dependency caches |
| Any | SANDCAT_GITIGNORE | true (see Gitignore defaults) |
| Any | SANDCAT_RTK | true (see RTK — LLM token compression) |
When an agent mount flag is false, Sandcat lists every path as a foot comment
on the first
Related Skills
healthcheck
385.5kAudit/harden OpenClaw hosts: SSH, firewall, updates, exposure, backups, disk encryption, gateway security.
node-connect
385.5kDiagnose OpenClaw Android, iOS, or macOS node pairing, QR/setup code, route, auth, and connection failures.
python-debugpy
385.5kDebug Python with pdb, breakpoint(), post-mortem inspection, and debugpy remote attach.
prose
385.5kOpenProse VM skill pack. Activate on any `prose` command, .prose files, or OpenProse mentions; orchestrates multi-agent workflows.
