Paddock is a secure, lightweight, project-specific sandboxed runtime container environment designed to run AI coding assistants (such as OpenCode, Gemini CLI, and Claude Code) in complete, virtualized isolation from your host system.
Unlike complex multi-bind sandboxes, Paddock utilizes a Pure Containerfile Layering strategy and a Pristine Home Directory strategy to provide 100% transparent and standard host bind-mounting under a highly hardened, VM-level virtualization boundary.
- Linux Host with KVM support (microVM sandboxing is Linux-only).
- Rootless Podman container engine.
- The
kruncontainer runtime (orlibkrun) and thepastanetwork isolator.
curl: Used to query latest stable version metadata from the NPM registry.jq: Used to parse JSON payloads.openssl: Used for portable, cross-platform Base64-to-Hex conversions of registry integrity hashes.
- Simple Command Interface: A single, easily-remembered shell command
./paddock.sh(or nativepodman container runlabel) to build and run sandboxes. - MicroVM Virtualization (
krun): Shakes off standard shared-kernel container namespace boundaries, executing the sandbox cleanly inside its own KVM-backed microVM usinglibkrunand thekrunruntime. - Tightly Hardened Sandbox: Out-of-the-box system-wide hardening:
- Strips all Linux capabilities (
--cap-drop ALL). - Disables all privilege escalation paths (
no-new-privileges). - Mounts the system root filesystem as read-only (
--read-only). - Secures
/tmpinside an in-memory, size-limited, non-executable filesystem (tmpfswithnoexec,nosuid,nodev). - Restricts runaway processes or fork-bombs (
--pids-limit).
- Strips all Linux capabilities (
- Pasta Network Isolation: Combines isolated network namespaces via modern
pastawithpassttranslation annotations (krun.use_passt=1) for high-performance and secure network sandboxing. - MicroVM Resource Capping: Restricts the guest VM from consuming host resources using libkrun annotations:
- Caps the guest's available RAM (
krun.ram_mib). - Restricts the number of virtual CPU cores (
krun.cpus).
- Caps the guest's available RAM (
- Secure Workspace Mounting: Automatically mounts the current host directory at
/home/ai/sandboxinside the container. - Persistent CLI Configs: Standard host bind-mounting to the unprivileged
/home/aifolder preserves credentials, shell histories, and CLI caches across container runs without named volumes. - Clean System Defaults: Environment prompts and standard tools are declared system-wide, keeping
/home/aicompletely empty in the image.
The concrete limits (RAM, vCPU count,
/tmpsize, PID cap) are intentionally not reproduced here. See each profile'sLABEL runin itsContainerfilefor the authoritative values;paddock.sh rundelegates to that same label rather than keeping a second copy.
paddock/
├── profiles/
│ ├── base/
│ │ ├── entrypoint.sh # Secure guest VM privilege-dropping entrypoint
│ │ └── Containerfile # Defines 'paddock-base:latest'
│ └── default/
│ └── Containerfile # Default general-purpose profile: starts FROM paddock-base:latest, builds 'paddock:latest'
├── paddock.sh # Simple execution/orchestration shell script
├── test_paddock.sh # Automated mock-based test suite
└── README.md
To build a profile (or build the core paddock-base):
# Builds the general-purpose default profile
./paddock.sh build
# Or build a specific profile
./paddock.sh build defaultbuild only does work when the image is missing or older than its Containerfile, and refreshes
paddock-base the same way. run performs the same check before launching, so an edit to
LABEL run takes effect on the next run. To rebuild unconditionally:
./paddock.sh rebuild # forces the default profile and its baseThe general-purpose profile always builds and runs as paddock:latest, regardless of where its
Containerfile comes from. Normally that is profiles/default/, but if you create your own
profiles/latest/Containerfile (not shipped by this repo, and listed in .gitignore), it takes
precedence for every spelling of the default profile: ./paddock.sh build, build default, and
build latest all resolve to it. This lets you customize the general-purpose sandbox on your own
machine without touching a tracked file. Any other profile name is unaffected by the override.
The base image installs both opencode (opencode-ai) and the Gemini CLI (@google/gemini-cli)
globally, alongside the Google Cloud CLI. No build-time selection is required.
Because the Google Cloud CLI is published only for
x86_64,paddock-baseis an x86_64-only image.
To run your project inside a sandbox:
# Run inside the general-purpose default profile sandbox
./paddock.sh run
# Or run inside a specific custom profile sandbox
./paddock.sh run defaultThis automatically:
- Prepares a persistent local home folder on the host at
~/.local/share/paddock/homes/default. A single shared home means all profiles reuse the same credentials, shell history, and caches out-of-the-box. - Delegates the launch to the profile's
LABEL runviapodman container runlabel, mounting the current directory at/home/ai/sandbox. - Starts an interactive bash shell as the non-root
aiuser in that workspace.
baseis an abstract parent image with noLABEL run;./paddock.sh run baseis rejected.
Each profile's Containerfile defines a LABEL run, which is the single definition of every mount and security flag. ./paddock.sh run simply invokes it, so running Podman directly is equivalent and needs no checkout of this repository:
podman container runlabel run paddock:latestThe label deliberately omits --name, so Podman assigns a unique container name on each launch and
you can run sandboxes from several directories concurrently. Use podman ps to find the generated
name.
To prevent container file masking (hiding pre-installed files inside the image), Paddock ensures /home/ai in the image is completely empty:
- Custom terminal prompts are initialized globally via
starshipin/etc/bash.bashrc. - Any shell commands run inside write persistent caches natively to the host's
~/.local/share/paddock/homes/default, which is bind-mounted directly to/home/ai.
Additionally, to work seamlessly inside virtualized krun environments (where guest kernels boot standard entrypoints as root by default), Paddock includes a secure /usr/local/bin/entrypoint.sh privilege-dropper. This utility dynamically captures the container's starting directory, registers the ai user's environmental properties, and utilizes setpriv to drop all VM-level permissions down to the unprivileged ai user before spawning your shell.
Paddock includes a robust automated mock-based integration test suite. You can run it on any Linux or macOS environment to verify script behavior and container argument generation without needing podman fully installed:
./test_paddock.shPaddock is released under the terms of the Apache License, Version 2.0 (Apache-2.0).
Copyright (C) 2026 Jan Baier jbaier@suse.cz