12 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Build / Test / Format
The project is built via make, not directly with swift build. Two Swift packages live in this repo: the root package (Containerization libraries + cctl + macOS-only integration binary) and vminitd/ (the Linux guest init system, cross-compiled with the Static Linux SDK).
make all— build everything (containerization+vminitd+init.ext4rootfs inbin/). DefaultBUILD_CONFIGURATION=debug; passrelease(or usemake release) for optimized builds.make containerization— build just the host-side Swift package (skips vminitd).make vminitd— build vminitd / vmexec only. By default usesLIBC=muslvia the Static Linux SDK;make linux-build LIBC=glibcbuilds via a Linux dev container.make test— unit tests with code coverage.make coverageregenerates the coverage report.make integration— runsbin/containerization-integration. Requires an in-repo kernel underbin/(bin/vmlinux-arm64on arm64,bin/vmlinuz-x86_64orbin/vmlinux-x86_64on x86_64); if absent, runmake fetch-default-kernelto download the Kata-provided kernel for the host arch.- Single test:
swift test --filter ContainerizationOCITests.ReferenceTests/testParsing(Swift Testing / XCTest filter syntax). Targets are listed inPackage.swift. make linux-test— runsswift testinside the Linux dev container (requires thecontainerCLI from apple/container).make linux-build— builds the host-side Swift package (incl.cctl,Containerization, andCloudHypervisor) inside the same Linux dev container. Use this to validate Linux portability of host-side code; the resultingcctlis what the cloud-hypervisor backend ships behind.make linux-integration— runs the cross-platform integration suite against a real cloud-hypervisor VM inside the dev container (nested virt via apple/container's--virtualization). Requires a KVM-capable kernel atkernel/vmlinux-arm64(orkernel/vmlinuz-x86_64on x86_64 hosts) — build viamake -C kernel; the kata-fetched kernel doesn't include KVM. Also requiresmake fetch-cloud-hypervisorandmake linux-buildto have been run first. Linux runs only the cross-platform subset (process true/false/echo hi); the macOS suite is unchanged.make fetch-cloud-hypervisor— downloads the staticcloud-hypervisorv52.0 (aarch64) binary intobin/cloud-hypervisorfor the Linux integration tests.make build-cloud-hypervisor/make build-virtiofsd— build patchedcloud-hypervisor/virtiofsdfrom sources you have cloned into.local/cloud-hypervisorand.local/virtiofsdrespectively. There is no fetch target — clone the upstream repos at the revision you want pinned.build-virtiofsdappliesscripts/patches/virtiofsd-skip-cap-drop-with-sandbox-none.patchand is idempotent. Both run inside the same Linux dev container aslinux-integrationso the resulting binaries are aarch64-linux-gnu.make dist-x86_64— assemblesbin/containerization-x86_64-<sha>.tar.gz(cctl + cloud-hypervisor + virtiofsd + initfs.ext4 + kernel) for x86_64 Linux deployment, cross-compiled inside the aarch64 dev container via the Static Linux SDK (Swift) andcargo zigbuild(Rust). Prereqs:.local/cloud-hypervisorand.local/virtiofsdsource checkouts (clone deliberately — no fetch target), and an x86_64 kernel built viamake -C kernel TARGET_ARCH=x86_64. Per-stage rebuild env vars:REBUILD_VMINITD=1,REBUILD_INITFS=1,REBUILD_CH=1,REBUILD_VIRTIOFSD=1; cctl x86 always rebuilds. Full pipeline, toolchain rationale, and troubleshooting indocs/x86_64-build.md. The orchestrator isscripts/build-dist-x86_64.sh.make fmt— applies.swift-formatand refreshes license headers via hawkeye.make check— formatting + license-header lint (this is what the pre-commit hook runs). Uses.swift-format-nolintfor stricter linting.make pre-commit— installsscripts/pre-commit.fmtas a git pre-commit hook.make protos— regeneratesSources/Containerization/SandboxContext/SandboxContext.{pb,grpc}.swiftfrom the.proto. Touch this whenever the proto changes; never hand-edit the generated files.make cross-prep— installs Swiftly, the pinned Swift toolchain (see.swift-version), and the Static Linux SDK. Run once before the first build.
WARNINGS_AS_ERRORS=true is the default for both packages. Don't disable it casually — CI builds with it on.
Architecture
This is a Swift library package (not a CLI tool) that lets applications run Linux containers on Apple silicon by spawning a lightweight VM per container via Virtualization.framework. The corresponding end-user CLI lives in apple/container and is not part of this repo. cctl here is a playground/example binary, not the shipping product.
The host ↔ guest split
Every Linux container runs inside its own VM. The boundary between host (macOS) and guest (Linux) is the central architectural fact:
- Host side (
Sources/,macOSplatform): orchestrates VMs throughVirtualization.framework(VZVirtualMachineInstance.swift,VZVirtualMachine+Helpers.swift). The user-facing entry points areLinuxContainer(one container per VM) andLinuxPod(multiple containers in one VM, experimental). These build aVMConfiguration, boot the VM with the chosenKerneland a rootfs containingvminitd, then drive the guest via gRPC. - Guest side (
vminitd/, Linux platform):vminitdis PID 1 inside the VM. It exposes a gRPC service over vsock (default port1024) defined bySources/Containerization/SandboxContext/SandboxContext.proto.VminitdCoreimplements that service: launching container processes, handling stdio over vsock, signal/event delivery, cgroups, mounts, and process lifecycle. By default it launches workloads viavmexec(a small helper that runs a single process inside the guest namespace);runcis used only when an OCI runtime path is supplied.
The proto is the contract between the two halves. The .pb.swift and .grpc.swift files in SandboxContext/ are generated — regenerate via make protos after changing SandboxContext.proto. Both host and guest depend on the same generated Swift via the path-dependency wiring in vminitd/Package.swift (containerization is a sibling path package).
VMM backends
Containerization abstracts the VMM behind VirtualMachineManager / VirtualMachineInstance. Two backends ship in this repo, both inside the same Containerization target but gated by #if:
- macOS:
VZVirtualMachineManager/VZVirtualMachineInstance(VZ*files,#if os(macOS)). DrivesVirtualization.frameworkdirectly. - Linux:
CHVirtualMachineManager/CHVirtualMachineInstance(CH*files plusCHProcess,VirtiofsdProcess,Vsock+Linux, all#if os(Linux)). Onecloud-hypervisorsubprocess per VM, REST-on-UDS control plane via the standaloneCloudHypervisorSwift package, virtio-blk / virtio-fs (onevirtiofsdper share) / TAP / vsock for the data plane. SameVminitdguest contract as VZ — only the host-side VMM differs.
The CloudHypervisor library is a thin NIO-based HTTP/1.1-over-UDS client targeting cloud-hypervisor's REST API. It compiles on both platforms (so it can be unit-tested on macOS without a real cloud-hypervisor binary), but is only consumed by the Linux backend at runtime.
Sandbox env vars. CHProcess and VirtiofsdProcess default to the upstream-secure spawn flags. Per-component opt-outs:
CONTAINERIZATION_NO_CH_SECCOMP=1— launch cloud-hypervisor with--seccomp false.CONTAINERIZATION_NO_VIRTIOFSD_SANDBOX=1— launch virtiofsd with--sandbox none.
Both flags emit a one-line logger.warning at start so a relaxed-sandbox VM is loud in the host log. The legacy alias CONTAINERIZATION_RELAXED_SANDBOX=1 continues to flip both at once. These are required inside apple/container's --virtualization dev container, where the host seccomp profile SIGSYS-kills both binaries; make linux-integration sets the legacy alias automatically. Leave them unset in production deployments where the host policy lets CH/virtiofsd run unmolested.
Library targets (Sources/)
These are independently consumable Swift modules. Keep their dependencies narrow:
Containerization— the top-level orchestration layer (LinuxContainer,LinuxPod,VMConfiguration,VminitdgRPC client wrapper, mounts, networking, sockets, image unpacking). Hosts both the macOS (VZ) and Linux (CH) VMM backends behind#if os(...).CloudHypervisor— standalone NIO-based HTTP/1.1-over-UDS client targeting cloud-hypervisor's REST API. Cross-platform (compiles on macOS for unit tests; consumed at runtime only by the Linux side ofContainerization).ContainerizationOCI— OCI image spec types, registry client (push/pull/auth), local OCI layout, content store. Used host-side for image management.ContainerizationEXT4— pure-Swift ext4 reader/formatter; used to build container rootfs blocks (bin/initfs.ext4).ContainerizationArchive— Swift wrapper around vendored libarchive headers (Sources/ContainerizationArchive/CArchive, refreshable viamake update-libarchive-source). Links systemlibarchive,lzma,bz2,z, plus zstd via SwiftPM.ContainerizationNetlink— netlink socket bindings (used by vminitd for in-guest network configuration).ContainerizationOS— POSIX/Darwin/Linux platform shims (Command,Terminal,Socket, signal handling, mount syscalls, keychain). Cross-platform.ContainerizationIO— small NIO-flavored stream/reader utilities.ContainerizationExtras,ContainerizationError,CShim— shared helpers and a tiny C bridge.
Sources/Integration/ is the macOS-only containerization-integration binary (the integration test runner; it is not a testTarget, it's an executableTarget that's invoked by make integration). Unit testTargets live under Tests/.
vminitd internals (vminitd/Sources/)
VminitdCore/Server+GRPC.swiftis the bulk of the guest agent — it implements every RPC declared inSandboxContext.proto.ManagedContainer.swift/ManagedProcess.swiftlaunch container processes viavmexecby default;VminitdCore/Runc/plusRuncProcess.swiftshell out torunconly when an OCI runtime path is supplied.ProcessSupervisorreaps and dispatches exit events.Cgroup/handles cgroup v2 setup.LCShim/andCVersion/are small C bridges (the latter injectsGIT_COMMIT/GIT_TAG/BUILD_TIMEat compile time).vmexecruns a single container process inside the guest namespace and is whatvminitdexecs to launch container workloads.
Conventions
- License headers are required on every Swift file.
make check-licensesruns hawkeye againstscripts/license-header.txt. New files: runmake update-licenses(ormake fmt) before committing. - Formatting:
.swift-format(line length 180, 4-space indent). The lint config (.swift-format-nolint) is what CI enforces.NeverForceUnwrap,NeverUseForceTry, andNeverUseImplicitlyUnwrappedOptionalsare all on — don't introduce!/try!. - Package isolation: prefer adding code to the smallest applicable module. Don't pull
ContainerizationintoContainerizationOCIor similar — the leaf modules are intentionally light so they can be consumed standalone. SandboxContext.protois excluded from theContainerizationtarget (seePackage.swift). The generated.pb.swift/.grpc.swiftfiles are checked in.- Squash-and-merge: PRs land as a single commit, so the PR title/body becomes the commit message — write it accordingly. Commits must be signed (per
CONTRIBUTING.md).
Requirements
Apple silicon Mac, macOS 26, Xcode 26. Swift toolchain version is pinned in .swift-version (currently 6.3.0) and installed via Swiftly during make cross-prep. Older macOS releases are not supported.