Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
e1e31b7
add IPv6 support (#752)
yibozhuang Jun 3, 2026
9275f36
[Experimental] Add Sandboxy binary (#607)
dcantah Jun 3, 2026
1437c67
Update github actions to use SHA versions (#763)
katiewasnothere Jun 4, 2026
4f6df44
fix(Platform): make arm64 nil-variant and v8 hash identically (#764)
Otoru Jun 16, 2026
e29657b
Pin macOS CI builds to Swift 6.3 for now. (#771)
jglogan Jun 16, 2026
ddd19a6
add x86 kernel config (#765)
crosbymichael Jun 16, 2026
7e2ae5a
docs: fix `traversal` in a public method's documentation (#770)
MojtabaHs Jun 16, 2026
d55cc18
Allow setting log level for vminitd (#772)
adityaramani Jun 17, 2026
5887dc5
[vminitd]: api for freeze/thaw filesystem operations (#685)
saehejkang Jun 19, 2026
6b7b42c
[vminitd]: api for trim filesystem operations (#700)
saehejkang Jun 20, 2026
a132341
Make EXT4Unpacker configurable for journal mode. (#775)
jglogan Jun 23, 2026
d992a19
fix(EXT4): create missing parent dirs for hardlinks during unpack (#777)
malt3 Jun 24, 2026
44bec8b
Support disk image based pod volumes (#780)
adityaramani Jun 26, 2026
ade2052
Add checksum validation to hawkeye installation (#785)
katiewasnothere Jun 30, 2026
b438e97
Add cloud-hypervisor VMM backend for Linux hosts (#782)
crosbymichael Jul 2, 2026
bd709ba
fix: default to restricted OCI capability set (#786)
crosbymichael Jul 2, 2026
7aa4e72
Upgrade grpc-swift-nio-transport to 2.9.0 and remove HTTP2ConnectBuff…
adityabagchi24 Jul 7, 2026
1d5641f
fix hotplug mounting of virtiofs (#794)
crosbymichael Jul 7, 2026
6093542
cctl: deprecate creds over http (#788)
crosbymichael Jul 8, 2026
315341b
add masked and readonly path support (#795)
crosbymichael Jul 8, 2026
2f947e7
Unset CONFIG_VSOCKETS_LOOPBACK in kernel config (#797)
adityaramani Jul 10, 2026
2a591c2
fix(package): exclude CloudHypervisor README (#798)
stephenlclarke Jul 15, 2026
585fd8c
Rename misleading blockSizeInBytes parameter to capacityInBytes in EX…
adityabagchi24 Jul 18, 2026
5d5204a
Add `journal` parameter to EXT4Unpacker `unpack()`. (#806)
jglogan Jul 20, 2026
d9868bb
Add support for tmpfs based pod volumes (#803)
adityaramani Jul 20, 2026
4f8dc6b
Update Package.resolved with latest versions. (#808)
jglogan Jul 22, 2026
2563ed5
feat: virtiofs rootfs hotplug for LinuxPod on cloud-hypervisor (#809)
crosbymichael Jul 23, 2026
450d44e
perf: index EXT4 FileTree children by name to avoid O(n^2) unpack (#793)
kkilchrist Jul 23, 2026
74ace14
Build vminitd and initfs inside the dev container (#810)
crosbymichael Jul 24, 2026
a4d2510
Fix virtiofs makefile syntax (#815)
crosbymichael Jul 27, 2026
50f7722
Mask high mode bits in archive reader (#816)
katiewasnothere Jul 27, 2026
3a224e9
Make nested virt optional for makefile linux_run helper function (#822)
katiewasnothere Jul 28, 2026
7800b46
Dont remount /run/virtiofs when adding container (#824)
adityaramani Jul 28, 2026
ff44a5b
Redact environment variable values in vminitd debug logs (#813)
vyncint Jul 29, 2026
5796abe
Use a custom volume for build cache inside of a container (#828)
adityaramani Jul 31, 2026
2ec221a
Fix bug when comparing the Platform struct (#833)
adityaramani Aug 6, 2026
d18b828
[gitignore]: ignore vscode workspace files (#817)
saehejkang Aug 7, 2026
b9c65e5
fix(oci): render arm64 platform description without redundant v8 (#783)
Halvanhelv Aug 7, 2026
fc2b9bd
align timeout for all platforms (#834)
crosbymichael Aug 10, 2026
55d1dc9
ImageStore: write state.json atomically (#835)
ArnavReddy Aug 10, 2026
dac8a2c
LinuxContainer: finish the copyOut metadata stream on every path (#837)
ArnavReddy Aug 10, 2026
5427fd2
LinuxContainer: stop the VM when setup fails after start (#836)
ArnavReddy Aug 10, 2026
db615ca
fix: efsck searching for external journal (#856)
siddh34 Aug 27, 2026
4294c0f
vmexec: enter the container's IPC namespace on exec (#851)
crosbymichael Aug 27, 2026
53fd7c3
Add LinuxContainer block I/O resources
chrisgeo May 6, 2026
9488c92
Wrap LinuxBlockIO with a Containerization type
chrisgeo May 14, 2026
6352727
Fix formatting and license header for block I/O changes
chrisgeo Jun 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/build-test-images.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,16 +61,16 @@ jobs:
- name: Checkout repository
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
if: ${{ inputs.useBuildx }}
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3
- name: Build dockerfile and push image
uses: docker/build-push-action@v6
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
with:
push: ${{ inputs.publish }}
context: Tests/TestImages/${{ inputs.image }}
Expand Down
93 changes: 78 additions & 15 deletions .github/workflows/containerization-build-template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,29 +15,88 @@ on:
description: Version of containerization
default: test

jobs:
buildAndTest:
jobs:
swift-version:
name: Determine Swift version
if: github.repository == 'apple/containerization'
runs-on: ubuntu-24.04
outputs:
image: ${{ steps.version.outputs.image }}
steps:
- name: Checkout .swift-version
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
with:
sparse-checkout: .swift-version
sparse-checkout-cone-mode: false

- name: Read Swift version
id: version
run: echo "image=swift:$(cat .swift-version)-noble" >> "$GITHUB_OUTPUT"

# Build the guest initfs (vminitd/vmexec compiled static-musl, then packed
# into initfs.ext4 + a rootfs tar) inside a GitHub-native Swift Linux
# container (Docker-backed) — NOT via apple/container, which isn't available
# on GitHub runners. The macOS job below consumes these as an artifact and
# creates the vminit:latest image natively with cctl. The container job is
# unprivileged, so build-initfs.sh uses its `mke2fs -d` fallback (no loop
# mount / no CAP_SYS_ADMIN needed).
buildGuest:
name: Build guest initfs
if: github.repository == 'apple/containerization'
needs: swift-version
timeout-minutes: 30
runs-on: ubuntu-24.04
container: ${{ needs.swift-version.outputs.image }}
steps:
- name: Checkout repository
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
with:
fetch-depth: 0

- name: Install system dependencies
run: apt-get update && apt-get install -y curl make e2fsprogs libarchive-dev libbz2-dev liblzma-dev libssl-dev

- name: Install Static Linux SDK
run: make -C vminitd linux-sdk

- name: Build vminitd (static musl, aarch64)
# Force aarch64 even though this runner is x86_64: the macOS buildAndTest
# job boots arm64 VZ VMs, so vminitd (PID 1) must be an aarch64 binary or
# the guest fails to exec and the integration boot hangs. The Static Linux
# SDK cross-targets both arches from any host (same mechanism dist-x86_64
# uses in reverse).
run: make -C vminitd MUSL_ARCH=aarch64 BUILD_CONFIGURATION=${{ inputs.release && 'release' || 'debug' }}

- name: Build initfs.ext4
run: ./scripts/build-initfs.sh --vminitd vminitd/bin/vminitd --vmexec vminitd/bin/vmexec --ext4 bin/initfs.ext4 --tar bin/init.rootfs.tar.gz

- name: Upload guest initfs
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: initfs
path: |
bin/initfs.ext4
bin/init.rootfs.tar.gz
if-no-files-found: error

buildAndTest:
name: Build and Test repo
if: github.repository == 'apple/containerization'
needs: buildGuest
timeout-minutes: 60
runs-on: [self-hosted, macos, tahoe, ARM64]
permissions:
contents: read
packages: write
env:
DEVELOPER_DIR: "/Applications/Xcode-latest.app/Contents/Developer"
DEVELOPER_DIR: "/Applications/Xcode_swift_6.3.app/Contents/Developer"

steps:
- name: Checkout repository
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
with:
fetch-depth: 0

- name: Activate Swiftly
run: |
source ~/.swiftly/env.sh
cat ~/.swiftly/env.sh

- name: Check formatting
run: |
./scripts/install-hawkeye.sh
Expand All @@ -50,18 +109,22 @@ jobs:
make protos
if ! git diff --quiet ; then echo the following files require formatting or license headers: ; git diff --name-only ; false ; fi

- name: Make containerization and docs
- name: Make containerization, examples, and docs
run: |
make clean containerization docs
make clean containerization examples docs
tar cfz _site.tgz _site
env:
BUILD_CONFIGURATION: ${{ inputs.release && 'release' || 'debug' }}

- name: Make vminitd image
- name: Download guest initfs
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
with:
name: initfs
path: bin

- name: Make vminit image
run: |
source ~/.swiftly/env.sh
make -C vminitd swift linux-sdk
make init
make init-image
env:
BUILD_CONFIGURATION: ${{ inputs.release && 'release' || 'debug' }}

Expand Down Expand Up @@ -113,7 +176,7 @@ jobs:

steps:
- name: Setup Pages
uses: actions/configure-pages@v5
uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5

- name: Download a single artifact
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/docs-release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,4 @@ jobs:
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4

release:
if: startsWith(github.ref, 'refs/tags/')
Expand All @@ -47,7 +47,7 @@ jobs:
packages: read
steps:
- name: Create release
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2
with:
token: ${{ github.token }}
name: ${{ github.ref_name }}-prerelease
Expand Down
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ DerivedData/
workdir/
installer/
.venv/
.vscode/
test_results/
*.pid
*.log
Expand All @@ -20,8 +21,9 @@ test_results/
*.swp
*.tar.gz
*.tar.xz
vmlinux
vmlinux*

# API docs for local preview only.
_site/
_serve/
kernel/vmlinuz-x86_64
91 changes: 91 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# 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, compiled as a static musl binary inside the Linux dev container via the apple/`container` CLI — see `make vminitd`).

- `make all` — build everything (`containerization` + `vminitd` + `init.ext4` rootfs in `bin/`). Default `BUILD_CONFIGURATION=debug`; pass `release` (or use `make release`) for optimized builds.
- `make containerization` — build just the host-side Swift package (skips vminitd).
- `make vminitd` — build vminitd / vmexec only. On macOS this runs `swift build --swift-sdk …-swift-linux-musl` *inside the Linux dev container* via the `container` CLI (the cloud-hypervisor build model), producing static musl binaries at `vminitd/bin/`; no host Swiftly/SDK needed. `make linux-build LIBC=glibc` builds via a Linux dev container.
- `make test` — unit tests with code coverage. `make coverage` regenerates the coverage report.
- `make integration` — runs `bin/containerization-integration`. Requires an in-repo kernel under `bin/` (`bin/vmlinux-arm64` on arm64, `bin/vmlinuz-x86_64` or `bin/vmlinux-x86_64` on x86_64); if absent, run `make fetch-default-kernel` to 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 in `Package.swift`.
- `make linux-test` — runs `swift test` inside the Linux dev container (requires the `container` CLI from apple/container).
- `make linux-build` — builds the host-side Swift package (incl. `cctl`, `Containerization`, and `CloudHypervisor`) inside the same Linux dev container. Use this to validate Linux portability of host-side code; the resulting `cctl` is 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 at `kernel/vmlinux-arm64` (or `kernel/vmlinuz-x86_64` on x86_64 hosts) — build via `make -C kernel`; the kata-fetched kernel doesn't include KVM. Also requires `make fetch-cloud-hypervisor` and `make linux-build` to 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 static `cloud-hypervisor` v52.0 (aarch64) binary into `bin/cloud-hypervisor` for the Linux integration tests.
- `make build-cloud-hypervisor` / `make build-virtiofsd` — build patched `cloud-hypervisor` / `virtiofsd` from sources you have cloned into `.local/cloud-hypervisor` and `.local/virtiofsd` respectively. There is no fetch target — clone the upstream repos at the revision you want pinned. `build-virtiofsd` applies `scripts/patches/virtiofsd-skip-cap-drop-with-sandbox-none.patch` and is idempotent. Both run inside the same Linux dev container as `linux-integration` so the resulting binaries are aarch64-linux-gnu.
- `make dist-x86_64` — assembles `bin/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) and `cargo zigbuild` (Rust). Prereqs: `.local/cloud-hypervisor` and `.local/virtiofsd` source checkouts (clone deliberately — no fetch target), and an x86_64 kernel built via `make -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 in `docs/x86_64-build.md`.** The orchestrator is `scripts/build-dist-x86_64.sh`.
- `make fmt` — applies `.swift-format` and refreshes license headers via hawkeye.
- `make check` — formatting + license-header lint (this is what the pre-commit hook runs). Uses `.swift-format-nolint` for stricter linting.
- `make pre-commit` — installs `scripts/pre-commit.fmt` as a git pre-commit hook.
- `make protos` — regenerates `Sources/Containerization/SandboxContext/SandboxContext.{pb,grpc}.swift` from the `.proto`. Touch this whenever the proto changes; never hand-edit the generated files.
- `make init` / `make init-image` — `init` compiles the guest and builds `bin/initfs.ext4` (+ a rootfs tar) inside the dev container via `scripts/build-initfs.sh` (mkfs + loop mount, with a `mke2fs -d` fallback), then `init-image` creates the `vminit:latest` OCI image from the tar with the native `cctl` (`cctl rootfs create --rootfs <tar> --image vminit:latest`). CI splits these: a Linux container job builds the initfs artifact, the macOS job runs `init-image`. Building the guest on macOS requires the apple/`container` CLI — there is no host Swiftly / Static Linux SDK setup step anymore.

`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`](https://github.com/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/`, `macOS` platform): orchestrates VMs through `Virtualization.framework` (`VZVirtualMachineInstance.swift`, `VZVirtualMachine+Helpers.swift`). The user-facing entry points are `LinuxContainer` (one container per VM) and `LinuxPod` (multiple containers in one VM, experimental). These build a `VMConfiguration`, boot the VM with the chosen `Kernel` and a rootfs containing `vminitd`, then drive the guest via gRPC.
- **Guest side** (`vminitd/`, Linux platform): `vminitd` is PID 1 inside the VM. It exposes a gRPC service over **vsock** (default port `1024`) defined by `Sources/Containerization/SandboxContext/SandboxContext.proto`. `VminitdCore` implements that service: launching container processes, handling stdio over vsock, signal/event delivery, cgroups, mounts, and process lifecycle. By default it launches workloads via `vmexec` (a small helper that runs a single process inside the guest namespace); `runc` is 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)`). Drives `Virtualization.framework` directly.
- **Linux**: `CHVirtualMachineManager` / `CHVirtualMachineInstance` (`CH*` files plus `CHProcess`, `VirtiofsdProcess`, `Vsock+Linux`, all `#if os(Linux)`). One `cloud-hypervisor` subprocess per VM, REST-on-UDS control plane via the standalone [`CloudHypervisor`](./Sources/CloudHypervisor) Swift package, virtio-blk / virtio-fs (one `virtiofsd` per share) / TAP / vsock for the data plane. Same `Vminitd` guest 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`, `Vminitd` gRPC 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 of `Containerization`).
- `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 via `make update-libarchive-source`). Links system `libarchive`, `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 `testTarget`s live under `Tests/`.

### vminitd internals (`vminitd/Sources/`)

- `VminitdCore/Server+GRPC.swift` is the bulk of the guest agent — it implements every RPC declared in `SandboxContext.proto`.
- `ManagedContainer.swift` / `ManagedProcess.swift` launch container processes via `vmexec` by default; `VminitdCore/Runc/` plus `RuncProcess.swift` shell out to `runc` only when an OCI runtime path is supplied. `ProcessSupervisor` reaps and dispatches exit events.
- `Cgroup/` handles cgroup v2 setup. `LCShim/` and `CVersion/` are small C bridges (the latter injects `GIT_COMMIT`/`GIT_TAG`/`BUILD_TIME` at compile time).
- `vmexec` runs a single container process inside the guest namespace and is what `vminitd` execs to launch container workloads.

## Conventions

- **License headers are required** on every Swift file. `make check-licenses` runs hawkeye against `scripts/license-header.txt`. New files: run `make update-licenses` (or `make fmt`) before committing.
- **Formatting**: `.swift-format` (line length 180, 4-space indent). The lint config (`.swift-format-nolint`) is what CI enforces. `NeverForceUnwrap`, `NeverUseForceTry`, and `NeverUseImplicitlyUnwrappedOptionals` are all on — don't introduce `!` / `try!`.
- **Package isolation**: prefer adding code to the smallest applicable module. Don't pull `Containerization` into `ContainerizationOCI` or similar — the leaf modules are intentionally light so they can be consumed standalone.
- **`SandboxContext.proto` is excluded from the `Containerization` target** (see `Package.swift`). The generated `.pb.swift` / `.grpc.swift` files 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. The host-side build uses Xcode's Swift toolchain (`/usr/bin/swift`); the Linux guest is built inside the dev container, so the apple/`container` CLI is required (see the README). The pinned Swift version (`.swift-version`, currently `6.3.0`) tags the dev image and the CI Swift Linux container. Older macOS releases are not supported.
Loading