Skip to content

Latest commit

 

History

History
171 lines (127 loc) · 9.73 KB

File metadata and controls

171 lines (127 loc) · 9.73 KB

Overview: Choosing a Hazmat Boundary

Use this file to answer three questions quickly:

  1. Which boundary should I choose?
  2. Why does Docker change the answer?
  3. Which deeper doc should I read next?

The Core Problem

Autonomous agent modes bypass most of the friction that normally slows down a mistake. Claude Code --dangerously-skip-permissions, Qwen --yolo, and similar harness modes can execute shell commands and make network calls with the privileges of the environment they are running in. A prompt injection, malicious repository file, compromised MCP server, or bad dependency can then act as that environment.

The point of sandboxing is not to make the agent "behave." It is to limit the blast radius when it does not.

Recommended Defaults

Situation Recommended tier Why
Trusted repo, interactive use, low stakes Tier 0 or Tier 1 Lowest friction; enough when you are watching closely
Need strong isolation without a VM and the repo does not need Docker Tier 2 Separate macOS user gives the biggest jump in blast-radius reduction
Repo needs Docker and can use a private daemon or microVM Tier 3 Docker changes the boundary question; give the agent its own daemon
Repo depends on a shared host Docker daemon Tier 4, or Tier 2 code-only Shared-daemon access is outside Hazmat containment
Unfamiliar repo, heavy autonomy, or sensitive environment Tier 4 Full VM plus rollback is the strongest practical answer

Linux Status

Hazmat has bounded Linux support now, but Linux native agent launch is not the supported default yet.

  • Linux native is plan-only: platform probes, capability-gap reporting, a linux-native launch-spec compiler, Linux package tests, and Linux CI lanes exist. The guarded native launch helper, setup, rollback, and release artifacts are not implemented yet.
  • Apple Container is experimental exec-only: on macOS 26 Apple silicon, hazmat exec --backend=apple-container --image ... -- <command> can run a Linux command in a short-lived VM when HAZMAT_EXPERIMENTAL_APPLE_CONTAINER=1 is set. It is useful for Linux workloads, but host file IO happens as the invoking macOS user, so it is not equivalent to Tier 2 dedicated-user native containment.
  • macOS native remains the production path: the tier table below describes the supported day-to-day containment boundary for AI coding agents today.

Risk Surface Layers

Think about the design in layers. Each tier covers some layers better than others.

1. Project and Agent Runtime Surface

This is the immediate surface: repo files, CLAUDE.md, hidden prompts in documents, hooks, local tooling, and shell execution. Tier 0 and Tier 1 mainly operate here.

2. User and Credential Surface

This is everything reachable from the user account running the agent: ~/.ssh, ~/.aws, ~/.claude, shell config, Keychain-adjacent paths, and browser/session material. Tier 2 is the first tier that changes this meaningfully by switching to a different macOS user.

3. Network and Localhost Surface

This is outbound exfiltration, localhost service abuse, and what the agent can reach on the LAN or internet. pf, proxying, deny-by-default networking, and isolated networks all matter here.

4. Privileged Helper and Daemon Surface

This includes Docker daemons, host services, shared sockets, hypervisors, and anything else that lets the agent ask a more privileged component to act on its behalf. This is why Docker is not just "another binary." If a project needs Docker, the design should usually move from Tier 2 to either Tier 3 (private daemon) or Tier 4 (shared-daemon workflow).

5. Persistence and Recovery Surface

This is what happens after compromise: modified shell rc files, LaunchAgents, poisoned caches, resource exhaustion, and whether you have a clean rollback path. Tier 4 is strongest here because snapshots make recovery predictable.

For the attack-by-attack view, read threat-matrix.md. For concrete incidents that motivated these layers, read incidents-and-cves.md.

Tier Summary

Tier Main boundary Strengths Main gaps Best fit
0 Harness built-in controls Minimal setup, native speed Not a strong security boundary Trusted repos, supervised work
1 Same-user process wrapper Better filesystem and network policy control Still same macOS user; limited against broader host exposure Trusted repos with a bit more rigor
2 Dedicated macOS user + pf + host-side wrappers Strong no-VM blast-radius reduction; separate home and configs Docker is intentionally blocked; container traffic is a different surface Daily development on non-Docker projects
3 Docker Sandboxes / private daemon / devcontainer Best balance when Docker is required and the repo fits a private daemon Shared-daemon projects are out of scope; continuity with Tier 2 is still rough Self-contained Docker repos and medium-to-high risk work
4 Full VM Strongest isolation and recovery story Highest operational overhead Untrusted repos, long autonomy, sensitive environments

Why Docker Changes the Decision

Docker is a separate design branch because a shared daemon is effectively a privileged helper. If the agent can reach a Docker daemon that is not exclusively its own, it can often create containers with bind mounts, privileged flags, or other settings that bypass the intended boundary.

That produces three practical rules:

  • Tier 2 native containment is the default for code editing, with Docker disabled.
  • If the session needs Docker and the repo can live inside a private daemon, opt into Tier 3 with --docker=sandbox, --docker=auto, or project config.
  • If the project depends on the host Docker daemon, do not punch a hole through Tier 2. Use Tier 4, or accept the default code-only Tier 2 session.

Read tier3-docker-sandboxes.md for the private-daemon path and shared-daemon-projects.md for the unsupported shared-daemon case.

Decision Flow

flowchart TD
    Start([Which tier?]) --> Q1{Trusted repo +<br/>supervised use?}
    Q1 -- yes --> T01["Tier 0 or 1<br/><em>process-level controls</em>"]
    Q1 -- no --> Q2{Needs Docker<br/>or Compose?}
    Q2 -- yes --> Q2A{"Needs the host<br/>Docker daemon?"}
    Q2A -- no --> T3["Tier 3<br/><em>dedicated daemon / microVM</em>"]
    Q2A -- yes --> T4A["Tier 4 or Tier 2 code-only<br/><em>shared-daemon project</em>"]
    Q2 -- no --> Q3{Unattended, sensitive<br/>data, or hostile repo?}
    Q3 -- yes --> T4["Tier 4<br/><em>full VM + snapshot rollback</em>"]
    Q3 -- no --> T2["Tier 2<br/><em>dedicated macOS user + pf</em>"]

    style T01 fill:#dfd,stroke:#3a3,color:#000
    style T2 fill:#ddf,stroke:#33a,color:#000
    style T3 fill:#ffd,stroke:#a80,color:#000
    style T4 fill:#fee,stroke:#c33,color:#000
Loading
  1. Is the repo trusted and are you supervising closely? If yes, Tier 0 or Tier 1 may be enough.
  2. Does the repo need Docker, Docker Compose, or a devcontainer workflow? If no, keep evaluating Tier 2.
  3. If yes, can the repo run against a private daemon or microVM? If yes, go to Tier 3. If it needs the host daemon, use Tier 4 or a code-only Tier 2 session.
  4. Do you want the strongest non-VM boundary for normal development? Use Tier 2.
  5. Are you running unattended, handling sensitive data, or working with hostile repos? Use Tier 4.

Dedicated-User UX Note

The key UX improvement in Hazmat's dedicated-user setup is that you stay in your normal shell while the agent process runs as the isolated agent user:

  • hazmat claude, hazmat codex, hazmat opencode, hazmat antigravity, hazmat hermes, and hazmat qwen launch supported harnesses in containment
  • hazmat shell opens an interactive contained shell
  • hazmat exec -- <command> runs one-off tools like make, npx, uv, and uvx

That preserves the boundary without forcing the user to live inside sudo -u agent -i.

Session integrations provide per-stack session ergonomics (read-only toolchain paths, snapshot excludes, safe env passthrough) without weakening Tier 2's containment. Repos can still recommend integrations via .hazmat/integrations.yaml; hazmat prompts once for approval.

Claude Code Permission Modes

Claude Code remains the clearest example of the application-level permission model Hazmat is designed to sit outside. It has five permission modes, from most restrictive to most permissive:

Mode Behavior
Plan Mode Read-only. Claude can analyze but cannot modify anything.
Normal Mode Default. Prompts for confirmation before every sensitive operation.
Auto-accept Edits Auto-approves file read/write. Shell commands still require validation.
Don't Ask Mode Auto-denies all tool usage unless pre-approved via /permissions.
Bypass Mode (--dangerously-skip-permissions) Skips all permission checks. Everything runs without prompting.

What to Read Next