YagoGG/hazmat

Easily run potentially dangerous stuff on isolated ephemeral sandboxes.

★ 0Forks 0PythonGitHub ↗Compare

README

hazmat

hazmat is a tiny Python library + CLI meant to run arbitrary commands through pluggable “sandbox” providers and stream their stdout/stderr as the command executes.

Copying files in/out

Every sandbox exposes copy_from_host(source, dest) and copy_to_host(source, dest) to move files/directories across the boundary:

  • Docker uses tar streaming with normalized permissions (dirs 0777, files 0644).
  • Modal uses the sandbox filesystem API (open, mkdir, ls) to stream bytes.
  • Multipass, QEMU (ssh/scp), and Local delegate to their native tools.

Use paths that make sense for the target: container paths for Docker/Modal, remote VM paths for QEMU/Multipass, and normal paths for Local. Destinations are created as needed (like mkdir -p) on both sides.

Installation

# base install
pip install hazmat

# pull in provider-specific extras
pip install "hazmat[docker]"     # requires Docker Python SDK and a running daemon
pip install "hazmat[qemu]"       # no extra Python deps; still needs qemu-system in PATH
pip install "hazmat[multipass]"  # no extra Python deps; still needs multipass in PATH
pip install "hazmat[modal]"      # requires Modal Python SDK and an API token configured

Usage

uv run -m hazmat.cli --help

Local

uv run -m hazmat.cli local -- uname -a

Multipass

uv run -m hazmat.cli multipass --name hazmat-dev -- uname -a

Docker (requires hazmat[docker])

The Docker provider starts a detached container from the image you specify and runs commands inside it using docker exec. The container is removed when the command finishes (or when the CLI exits if you run multiple commands).

uv run -m hazmat.cli docker \
  --image ubuntu:24.04 \
  --network-mode none \
  --env BASE=1 \
  --exec-env FOO=bar \
  -- uname -a

Additional knobs:

  • --pull/--no-pull pulls the image before creating the sandbox.

  • --mount (bind) and --volume (named) mount host paths/volumes.

  • --network-mode, --nano-cpus configure container networking/CPU quota.

  • --env sets container environment; --exec-env sets exec-time environment.

  • --run-extra / --exec-extra pass raw kwargs through to docker run / docker exec (escape hatch; values parsed as JSON when possible).

  • --name forces a specific container name instead of a random one.

  • --workdir sets the working directory for docker exec.

  • Using Colima? Point hazmat (and the Docker SDK) at the Colima socket first:

    export DOCKER_HOST="unix://$HOME/.colima/docker.sock"

Modal (requires hazmat[modal])

The Modal provider spins up a Modal sandbox and runs commands inside it using modal.Sandbox.exec. You can start from a registry image (--image) or fall back to Modal's debian_slim() base, optionally mounting local directories.

uv run -m hazmat.cli modal \
  --image docker.io/library/python:3.12 \
  --app my-app \
  --mount "$PWD:/app" \
  --env BASE=1 \
  --exec-env FOO=bar \
  --workdir /app \
  -- ls -la

Any mounts are added via modal.Image.add_local_dir, and per-command env vars are merged with the defaults you configure at sandbox creation. --app is required and is resolved with modal.App.lookup; pass --no-create-app to avoid creating missing apps during lookup.

QEMU (optional hazmat[qemu])

The QEMU provider boots a VM from a disk image that you supply (for example an image produced by Packer) and runs commands inside it over SSH. The image must contain a user that can log in using the private key you pass to hazmat (or using your SSH agent).

uv run -m hazmat.cli qemu \
  --image ./packer-output/hazmat.qcow2 \
  --ssh-user hazmat \
  --ssh-key ~/.ssh/hazmat_id_ed25519 \
  --memory-mb 4096 \
  --cpus 4 \
  -- uname -a

Additional knobs:

  • --binary, --accel, and --extra-arg allow you to tweak the QEMU invocation (e.g., to enable kvm, hvf, or to attach additional devices).
  • --boot-timeout controls how long hazmat waits for SSH to become available before bailing out, while --probe-interval sets how often to check.
  • --no-snapshot disables QEMU’s -snapshot flag if you need persistent writes (hazmat defaults to ephemeral writes to avoid mutating the input image).
  • --ssh-port pins the forwarded host port; --ssh-option appends raw tokens to the ssh invocation.

Contributors

YagoGG

Issues