Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Threat model & scope

These are the whole point of the project. Treat any change that weakens one of the security invariants as a bug.

Scope of the boundary

The trust boundary is QEMU: we assume its device/Virtio isolation holds, and defend the host filesystem and the user’s credentials against a (possibly prompt-injected) agent.

Explicitly out of scope:

  • Defending the host against a malicious guest kernel.
  • Being a general-purpose VM manager — ccvm builds exactly one guest and boots it one way.

The VM being the boundary is what makes --dangerously-skip-permissions safe to opt into. writableCwd = false adds a file-level safety net on top.

Default posture — what the defaults do and don’t stop

The invariants stop the host from being persisted to or having its credentials written to disk — they do not sandbox what a prompt-injected agent can read and send.

Under the native-mirroring defaults (open egress + share.* on), the in-VM agent can read the whole project tree and exfiltrate it over open egress (with clipboard.images on, also the host clipboard image — never text; see Image-paste bridge).

What it can no longer read is the host’s OAuth login: the share.* allowlist stages only settings/commands/memory, and .credentials.json is excluded by construction — claude starts unauthenticated and the user’s own in-VM /login or API key authenticates it. The host’s stored credential never crosses; but once authenticated in-VM, the resulting token lives in the ephemeral tmpfs ~/.claude and is readable by the in-VM agent (it has to be), so under open egress it is exfiltratable — same class as the project tree.

The out-of-the-box win is containment (no host access beyond CWD, nothing persists) plus the host login never auto-crossingnot project-exfiltration resistance, a deliberate DevEx choice.

The primary hardening knob

The primary hardening knob is egressAllowlist (default-deny egress), enforced inside the guest — so it only binds a non-root agent.

Setting egressAllowlist also auto-drops the agent’s sudo and, under nix.enable, removes the agent from Nix trusted-users — both load-bearing: a Nix trusted-user is root-equivalent (post-build-hook runs as root) and would otherwise regain root to nft flush the rules (audit S-1; fixed).

To disable all claude config sharing: set all share.* to false, or CCVM_SHARE_CLAUDE_CONFIG=0.

Keep this distinction accurate — understating it turns a sandbox into a liability.

Reporting a vulnerability

Security reports go through GitHub’s private vulnerability reporting for this repository (see SECURITY.md in the repo root). Please don’t open a public issue for anything that could be a boundary break before it’s triaged.