UTM cannot build without prebuilt native dependencies (QEMU, SPICE, …) staged
into sysroot-* directories at the repo root. Get them from the project's GitHub
Actions Sysroot-* artifacts — don't build them manually unless you're modifying a
dependency (it's slow and fragile). Then build with ./scripts/build_utm.sh and
sign/package with ./scripts/package*.sh. Setup, platform/arch names, schemes, and
signing: Documentation/iOSDevelopment.md.
Layered: platform-independent backend (Services/, Configuration/) + SwiftUI
frontend (Platform/). Full picture: Documentation/Architecture.md. Load-bearing
gotchas:
- Two VM backends behind the
UTMVirtualMachineprotocol —UTMQemuVirtualMachine(QEMU, via the QEMUKit/CocoaSpice packages) andUTMAppleVirtualMachine(Virtualization.framework, macOS-only). Most backend code branches on which. - Config is the serialization format — parallel
CodabletreesUTMQemuConfigurationandUTMAppleConfiguration(PLIST in the.utmbundle). Add new fields there; don't extend theConfiguration/Legacy/readers. - macOS runs QEMU out-of-process (XPC:
QEMUHelper→QEMULauncher); host files reach it only through a security-scoped-bookmark dance — respect it for anything touching disk images, shared dirs, or file handles. iOS runs QEMU in-thread and can launch it only once per process — this constrains start/stop logic. - UTM SE (
iOS-SEscheme,*-tcisysroots) is gated by theWITH_QEMU_TCIcompile flag — grep for it before changing anything that differs between editions.
Edit the generator and regenerate, not the output: Configuration/QEMUConstantGenerated.swift
(scripts/const-gen.py), Scripting/UTMScripting.swift (scripts/bridge-gen.sh
from UTM.sdef), and QAPI/QMP wrappers (produced by the dependency build).
Third-party patches live in patches/.
CONTRIBUTING.md is the source of truth for style, concurrency, design, and
compatibility rules; /utm-review audits the diff against it. Two rules an agent
must apply unprompted — they override default behavior and are easy to violate:
- AI attribution: every AI-assisted commit must carry an
Assisted-by: AGENT:MODELtrailer (e.g.Assisted-by: Claude:claude-opus-4-8) — add it explicitly even if your agent doesn't normally inject attribution trailers; the absence of a default trailer is not an excuse to omit it. Never addCo-authored-by(strip any a tool adds). Commit titles arecomponent: short descriptionexplaining why. - Scope: one feature/fix per PR; don't touch, refactor, or reformat unrelated
files; route new logging through
UTMLogging/loggingatdebuglevel.
Before opening or updating a PR, run /utm-review then /utm-submit —
canonical instructions in .agents/skills/{utm-review,utm-submit}/SKILL.md (thin
shims in .claude/commands/ and .opencode/command/). If your agent doesn't
auto-load them, read and follow those SKILL.md files directly.