SkillAgentSearch skills...

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/sandcat

Installs into whichever agent you are using.

About this skill

Quality Score

0/100

Supported Platforms

Claude Code
Claude Desktop
GitHub Copilot

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, and scripts/ 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/templates directory into your project and customize them based on your choices (development stack, etc.)
  • run docker compose commands 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. Put nodejs@22.5.1 in tools to replace the stack's nodejs (without a specifier, nodejs refers to lts).
  • Cross-family collisions — tools packages providing the same file as a stack package win too. Put openjdk17@latest in tools to make it the active Java over the stack's temurin-bin-25@latest; the agent's java, JAVA_HOME and 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

View on GitHub
GitHub Stars182
CategoryDevelopment
Updated5h ago
Forks14

Languages

Shell

Security Score

100/100

Audited on Aug 8, 2026

No findings