Skip to content
CodeVetter docs
Esc
navigateopen⌘Jpreview
On this page

Isolation options for running untrusted agent code on Apple Silicon, determinism controls, SARIF-emitting analyzers, and the CodeQL licensing blocker.

Sandboxed execution and static analysis

Verified 2026-08-31. See tooling-decisions.md for the cross-category summary.

Sandboxed execution is the mechanism CodeVetter’s verdicts rest on: if the isolation is not reproducible, the evidence is not either. This page covers isolation on Apple Silicon, the determinism controls that make runs comparable, and static analyzers that emit SARIF.

On a supported host, qualify the official Apple container CLI first. It gives the team a maintained, OCI-compatible lightweight-VM implementation with which to measure containment, mount policy, networking, resource bounds, startup, and teardown before accepting an in-process VMM dependency. The signed 1.3.1 package is now qualified on the Apple Silicon macOS 27 trial host. A cached no-op container started in 0.61 seconds; read-only root/workspace, bounded CPU and memory, an internal no-DNS network, dropped capabilities, host-environment absence, and teardown all behaved as expected. The first image/init-image run took 20.56 seconds and the cached images occupied 1.45 GB.

One native contract failed: the CLI accepted a controlled bind source containing .. when it resolved outside the intended fixture root. CodeVetter must canonicalize and enforce workspace containment itself; Apple Container’s mount validation is not that policy. The Rust mount planner now rejects traversal, escaping symlinks, source replacement, mount-string injection, and malformed guest targets, then revalidates the source identity immediately before returning a read-only bind argument. The qualification receipt records identities, measurements, teardown, and the remaining runner gates.

The selected first adapter is the external Apple CLI on supported Macs. It is already signed and measured, keeps the app bundle free of a VMM/FFI dependency, and does not add nested signing work. Installation and service startup remain explicit owner actions. A product runner must still prove exact-version and local-image preflight, an attested internal network, minimal environment, timeout/cancellation, bounded output, and cleanup before this becomes a shipped isolation claim.

The string-based CLI retains a final TOCTOU window after source revalidation. That boundary is acceptable only for CodeVetter-owned immutable worktrees that the untrusted guest cannot mutate before launch. If concurrently mutable host roots become a requirement, move to Apple’s Containerization Swift package and an audited descriptor-based mount path. Keep libkrun as the fallback if real workloads disprove the first-party path, rather than taking on FFI now.

libkrun (Apache-2.0, containers/libkrun, 2,643★) is a small VMM library written in Rust and built on Apple’s Hypervisor.framework. It is not Docker, not a daemon, and not a subprocess — it links into the existing Rust backend via its C API, boots a minimal Linux microVM per verification run, and tears it down after. That matches the local-first, no-server constraint without requiring the user to install and license a GUI application.

apple/containerization (Apache-2.0, Swift, 8.9k★) is the library beneath that first-party CLI and is the follow-on once a Swift sidecar is acceptable. It claims sub-second boot and is the only option here with native VM state save/restore — the actual warm-start mechanism. It requires macOS 26 and Apple Silicon.

Colima + Lima (MIT / Apache-2.0) is the pragmatic fallback for pre-macOS-26 or Intel machines: a one-time Homebrew install rather than a bundled component.

Comparison

Option License Daemon? Bundleable? macOS ARM?
libkrun Apache-2.0 No — in-process C API Yes, as a Rust dependency Yes, explicit HVF backend
Apple container CLI Apache-2.0 Yes, system service External prerequisite Apple Silicon, macOS 26+
apple/containerization Apache-2.0 No at library level Via a Swift sidecar Apple Silicon, macOS 26+
Colima + Lima MIT / Apache-2.0 CLI-managed VM, no GUI daemon Prerequisite only Yes (vz driver, default since Lima v1.0)
Podman Apache-2.0 podman machine VM Prerequisite only Yes
Docker Desktop Engine Apache-2.0; Desktop app proprietary Yes, GUI app + background VM No Yes, but license-gated
Firecracker Apache-2.0 No — KVM only
gVisor Apache-2.0 No — Linux platforms only
Wasmtime / WasmEdge Apache-2.0 No Yes, trivially Yes

Three findings that overturn common assumptions

  1. Firecracker and gVisor do not run on macOS at all. Firecracker’s own production-host docs state it relies on KVM, which is Linux-only. gVisor’s platform docs list KVM, Systrap and Ptrace — all Linux-specific. There is no ARM-macOS host mode for either. Any plan built around them is dead on arrival.
  2. Docker Desktop is a licensing gate, not just an install burden. The engine (moby) is Apache-2.0, but the Desktop app is proprietary and metered: free only under 250 employees and under $10M revenue. That becomes a legal problem the moment the company crosses either threshold.
  3. WASM cannot be the primary sandbox. It is fast and trivially embeddable, but only runs workloads compiled to WASI. CodeVetter’s actual core workload is TypeScript/Node web tasks with real browser and API behaviour. Keep WASM for pure-computation graders only.

Determinism controls

These are mostly guest/application-layer concerns, so they transfer across whichever isolation option is chosen:

Control Mechanism
Frozen clock libfaketime via LD_PRELOAD inside the guest
Seeded RNG Runtime-level shims, not a hypervisor feature
Network isolation --network none or equivalent on all container/VM options
Filesystem rollback Copy-on-write layers (recreate per run, not a memory snapshot)
CPU/memory limits cgroups-equivalent flags; WASM adds fuel/instruction limits
VM state save/restore VZ/HVF family only — see below

Warm start is the strongest argument for the VZ/HVF family. True memory-state snapshotting is confirmed only for Virtualization.framework-based options. Lima’s vz: implement auto save/restore (PR #2900) measured 37s → 13s boot-to-ready, and notes saveOnStop requires macOS 14+ and is arm64-only. Docker’s and containerd’s copy-on-write layer model does not give you this.

Rust crates for driving isolation in-process

  • libkrun — the VMM itself, Rust, Apache-2.0, consumable via bindgen FFI. The most direct no-shell-out path.
  • bollard — async Docker Engine API client, Apache-2.0, v0.21.1 (2026-08-16). Avoids shelling out to the docker CLI but still needs a running daemon.
  • testcontainers (Rust) — MIT/Apache-2.0, v0.28.0. Higher-level, same daemon prerequisite.
  • Direct Rust bindings to Virtualization.framework exist (virtualization-rs, apple-virtualization, virt-fwk) but are all low-star, thinly maintained side projects. Do not depend on them.

Static analysis with SARIF

Tool License Offline Native SARIF Verdict
Biome Apache-2.0 Yes Verified SARIF 2.1.0 Wired for repository evidence
ast-grep MIT Yes Envelope invalid in 0.45.2 Structural matching is sound, but do not upload its native SARIF
Ruff MIT Yes Yes (--output-format=sarif) Python scope only
Semgrep CLI is LGPL-2.1 Yes Yes Engine fine; rules are the problem
Clippy Apache-2.0 Yes No Needs clippy-sarif converter
ESLint MIT Yes No Third-party formatter
CodeQL Custom Yes Yes Repository-only; 🚫 customer-code product use

🚫 CodeQL is a hard blocker for this product

The CLI license prohibits use “in connection with any codebase that is not an Open Source Codebase” and prohibits automated analysis, CI or CD on such code, absent a paid GitHub Advanced Security agreement.

CodeVetter’s entire purpose is running against users’ private repositories. CodeQL therefore cannot be a default engine unless every user separately holds a commercial GHAS license. This is a legal blocker, not a preference.

⚠️ Semgrep — the engine and the rules have different licenses

The CLI is LGPL-2.1 and runs offline. But the registry rulesets (p/default, p/security-audit, …) are under a separate Semgrep Rules License v1.0: “You may use the rules only for your own internal business purposes. This license does not allow you to distribute the rules, or to make them available to others as a service.”

Running Semgrep against a user’s own code is internal use and fine. Bundling a vendored copy of the registry rules into every install is the gray area — that needs counsel before shipping, and is not cleared by the engine’s LGPL alone.

Biome’s SARIF path is now verified and wired for the public repository, so no second generic linter is justified without a missing-rule case. A focused ast-grep 0.45.2 trial found correct structural locations but an invalid SARIF root: version contains the tool version rather than 2.1.0, and $schema is absent. The tracked receipt keeps this a measured rejection instead of an assumed capability.

Observability — do not add a dependency

Use the existing SQLite database. Cost, latency and tokens per run are a handful of columns on the existing run/verification tables: cost_usd, latency_ms, tokens_in, tokens_out, provider, model.

  • OpenTelemetry Rust (Apache-2.0) is well-maintained but built for exporting to a collector. Running it in-process purely to write rows you could write directly is dependency accumulation.
  • Langfuse core is MIT and self-hostable, but it is a server product (Postgres + web app) — a flat mismatch with “Tauri desktop binary, no server.”

Revisit OTel only if CodeVetter grows a genuine multi-process or remote-agent topology needing distributed tracing.

Open questions

Flagged UNVERIFIED and worth closing before committing engineering time:

  • The Virtualization.framework entitlement question. Apple’s docs are JS-rendered and could not be fetched. Circumstantial evidence is strong — Lima, Colima and apple/container all ship as notarized, non-App-Store binaries using vz without incident, supporting the common understanding that com.apple.security.virtualization is required only for sandboxed Mac App Store apps, not Developer-ID-signed builds. Confirm directly against Apple before relying on it.
  • libkrun cold-boot time on Apple Silicon. “Smallest possible boot time” is a stated design goal; no published benchmark was found.
  • Podman’s default macOS backend (applehv vs libkrun vs QEMU) and its exact network/CPU/memory flags.

Was this page helpful?