Showing preview only (2,780K chars total). Download the full file or copy to clipboard to get everything.
Repository: apple/containerization
Branch: main
Commit: a59ed894213c
Files: 324
Total size: 2.6 MB
Directory structure:
gitextract_1u63lgkp/
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── 01-bug.yml
│ │ ├── 02-feature.yml
│ │ └── config.yml
│ └── workflows/
│ ├── build-test-images.yml
│ ├── containerization-build-template.yml
│ ├── containerization-build.yml
│ ├── docs-release.yaml
│ └── release.yml
├── .gitignore
├── .spi.yml
├── .swift-format
├── .swift-format-nolint
├── .swift-version
├── CONTRIBUTING.md
├── LICENSE
├── MAINTAINERS.txt
├── Makefile
├── Package.resolved
├── Package.swift
├── Protobuf.Makefile
├── README.md
├── SECURITY.md
├── Sources/
│ ├── CShim/
│ │ ├── capability.c
│ │ ├── exec_command.c
│ │ ├── include/
│ │ │ ├── capability.h
│ │ │ ├── exec_command.h
│ │ │ ├── openat2.h
│ │ │ ├── prctl.h
│ │ │ ├── socket_helpers.h
│ │ │ └── vsock.h
│ │ ├── openat2.c
│ │ ├── prctl.c
│ │ ├── socket_helpers.c
│ │ └── vsock.c
│ ├── Containerization/
│ │ ├── AttachedFilesystem.swift
│ │ ├── Container.swift
│ │ ├── ContainerManager.swift
│ │ ├── ContainerStatistics.swift
│ │ ├── DNSConfiguration.swift
│ │ ├── ExitStatus.swift
│ │ ├── FileMount.swift
│ │ ├── Hash.swift
│ │ ├── HostsConfiguration.swift
│ │ ├── IO/
│ │ │ ├── ReaderStream.swift
│ │ │ ├── Terminal+ReaderStream.swift
│ │ │ └── Writer.swift
│ │ ├── Image/
│ │ │ ├── Image.swift
│ │ │ ├── ImageStore/
│ │ │ │ ├── ImageStore+Export.swift
│ │ │ │ ├── ImageStore+Import.swift
│ │ │ │ ├── ImageStore+OCILayout.swift
│ │ │ │ ├── ImageStore+ReferenceManager.swift
│ │ │ │ └── ImageStore.swift
│ │ │ ├── InitImage.swift
│ │ │ ├── KernelImage.swift
│ │ │ └── Unpacker/
│ │ │ ├── EXT4Unpacker.swift
│ │ │ └── Unpacker.swift
│ │ ├── Interface.swift
│ │ ├── Kernel.swift
│ │ ├── LinuxContainer.swift
│ │ ├── LinuxPod.swift
│ │ ├── LinuxProcess.swift
│ │ ├── LinuxProcessConfiguration.swift
│ │ ├── Mount.swift
│ │ ├── NATInterface.swift
│ │ ├── NATNetworkInterface.swift
│ │ ├── Network.swift
│ │ ├── SandboxContext/
│ │ │ ├── SandboxContext.grpc.swift
│ │ │ ├── SandboxContext.pb.swift
│ │ │ └── SandboxContext.proto
│ │ ├── SystemPlatform.swift
│ │ ├── TimeSyncer.swift
│ │ ├── UnixSocketConfiguration.swift
│ │ ├── UnixSocketRelay.swift
│ │ ├── UnixSocketRelayManager.swift
│ │ ├── VMConfiguration.swift
│ │ ├── VZVirtualMachine+Helpers.swift
│ │ ├── VZVirtualMachineInstance.swift
│ │ ├── VZVirtualMachineManager.swift
│ │ ├── VirtualMachineAgent+Additions.swift
│ │ ├── VirtualMachineAgent.swift
│ │ ├── VirtualMachineInstance.swift
│ │ ├── VirtualMachineManager.swift
│ │ ├── Vminitd+Rosetta.swift
│ │ ├── Vminitd+SocketRelay.swift
│ │ ├── Vminitd.swift
│ │ ├── VmnetNetwork.swift
│ │ └── VsockListener.swift
│ ├── ContainerizationArchive/
│ │ ├── ArchiveError.swift
│ │ ├── ArchiveReader.swift
│ │ ├── ArchiveWriter.swift
│ │ ├── ArchiveWriterConfiguration.swift
│ │ ├── CArchive/
│ │ │ ├── COPYING
│ │ │ ├── archive_swift_bridge.c
│ │ │ └── include/
│ │ │ ├── archive.h
│ │ │ ├── archive_bridge.h
│ │ │ └── archive_entry.h
│ │ ├── TempDir.swift
│ │ └── WriteEntry.swift
│ ├── ContainerizationEXT4/
│ │ ├── Documentation.docc/
│ │ │ └── ext4.md
│ │ ├── EXT4+Extensions.swift
│ │ ├── EXT4+FileTree.swift
│ │ ├── EXT4+Formatter.swift
│ │ ├── EXT4+Ptr.swift
│ │ ├── EXT4+Reader.swift
│ │ ├── EXT4+Types.swift
│ │ ├── EXT4+Xattrs.swift
│ │ ├── EXT4.swift
│ │ ├── EXT4Reader+Export.swift
│ │ ├── EXT4Reader+IO.swift
│ │ ├── FilePath+Extensions.swift
│ │ ├── FileTimestamps.swift
│ │ ├── Formatter+Unpack.swift
│ │ ├── Integer+Extensions.swift
│ │ ├── README.md
│ │ └── UnsafeLittleEndianBytes.swift
│ ├── ContainerizationError/
│ │ └── ContainerizationError.swift
│ ├── ContainerizationExtras/
│ │ ├── AddressAllocator.swift
│ │ ├── AddressError.swift
│ │ ├── AsyncLock.swift
│ │ ├── AsyncMutex.swift
│ │ ├── CIDR.swift
│ │ ├── CIDRv4.swift
│ │ ├── CIDRv6.swift
│ │ ├── FileManager+Temporary.swift
│ │ ├── IPAddress.swift
│ │ ├── IPv4Address.swift
│ │ ├── IPv6Address+Parse.swift
│ │ ├── IPv6Address.swift
│ │ ├── IndexedAddressAllocator.swift
│ │ ├── MACAddress.swift
│ │ ├── NetworkAddress+Allocator.swift
│ │ ├── Prefix.swift
│ │ ├── ProgressEvent.swift
│ │ ├── ProxyUtils.swift
│ │ ├── RotatingAddressAllocator.swift
│ │ ├── TLSUtils.swift
│ │ ├── Timeout.swift
│ │ └── UInt8+DataBinding.swift
│ ├── ContainerizationIO/
│ │ └── ReadStream.swift
│ ├── ContainerizationNetlink/
│ │ ├── NetlinkSession.swift
│ │ ├── NetlinkSocket.swift
│ │ └── Types.swift
│ ├── ContainerizationOCI/
│ │ ├── AnnotationKeys.swift
│ │ ├── Bundle.swift
│ │ ├── Client/
│ │ │ ├── Authentication.swift
│ │ │ ├── KeychainHelper.swift
│ │ │ ├── LocalOCILayoutClient.swift
│ │ │ ├── RegistryClient+Catalog.swift
│ │ │ ├── RegistryClient+Error.swift
│ │ │ ├── RegistryClient+Fetch.swift
│ │ │ ├── RegistryClient+Push.swift
│ │ │ ├── RegistryClient+Referrers.swift
│ │ │ ├── RegistryClient+Token.swift
│ │ │ └── RegistryClient.swift
│ │ ├── Content/
│ │ │ ├── AsyncTypes.swift
│ │ │ ├── Content.swift
│ │ │ ├── ContentStoreProtocol.swift
│ │ │ ├── ContentWriter.swift
│ │ │ ├── LocalContent.swift
│ │ │ ├── LocalContentStore.swift
│ │ │ ├── SHA256+Extensions.swift
│ │ │ ├── String+Extension.swift
│ │ │ └── URL+Extensions.swift
│ │ ├── Descriptor.swift
│ │ ├── FileManager+Size.swift
│ │ ├── ImageConfig.swift
│ │ ├── Index.swift
│ │ ├── Manifest.swift
│ │ ├── MediaType.swift
│ │ ├── Platform.swift
│ │ ├── Reference.swift
│ │ ├── Spec.swift
│ │ ├── State.swift
│ │ └── Version.swift
│ ├── ContainerizationOS/
│ │ ├── AsyncSignalHandler.swift
│ │ ├── BinaryInteger+Extensions.swift
│ │ ├── Command.swift
│ │ ├── File.swift
│ │ ├── FileDescriptor+SecurePath.swift
│ │ ├── Keychain/
│ │ │ ├── KeychainQuery.swift
│ │ │ └── RegistryInfo.swift
│ │ ├── Linux/
│ │ │ ├── Binfmt.swift
│ │ │ ├── Capabilities.swift
│ │ │ └── Epoll.swift
│ │ ├── Mount/
│ │ │ └── Mount.swift
│ │ ├── POSIXError+Helpers.swift
│ │ ├── Path.swift
│ │ ├── Pipe+Close.swift
│ │ ├── README.md
│ │ ├── Reaper.swift
│ │ ├── Signals.swift
│ │ ├── Socket/
│ │ │ ├── BidirectionalRelay.swift
│ │ │ ├── Socket.swift
│ │ │ ├── SocketType.swift
│ │ │ ├── UnixType.swift
│ │ │ └── VsockType.swift
│ │ ├── Syscall.swift
│ │ ├── Sysctl.swift
│ │ ├── Terminal.swift
│ │ ├── URL+Extensions.swift
│ │ └── User.swift
│ ├── Integration/
│ │ ├── ContainerTests.swift
│ │ ├── PodTests.swift
│ │ └── Suite.swift
│ └── cctl/
│ ├── ImageCommand.swift
│ ├── KernelCommand.swift
│ ├── LoginCommand.swift
│ ├── RootfsCommand.swift
│ ├── RunCommand.swift
│ ├── cctl+Utils.swift
│ └── cctl.swift
├── Tests/
│ ├── ContainerizationArchiveTests/
│ │ ├── ArchiveReaderTests.swift
│ │ ├── ArchiveTests.swift
│ │ └── Resources/
│ │ └── test.tar.zst
│ ├── ContainerizationEXT4Tests/
│ │ ├── Resources/
│ │ │ └── content/
│ │ │ └── blobs/
│ │ │ └── sha256/
│ │ │ ├── 48a06049d3738991b011ca8b12473d712b7c40666a1462118dae3c403676afc2
│ │ │ ├── 4f4fb700ef54461cfa02571ae0db9a0dc1e0cdb5577484a6d75e68dc38e8acc1
│ │ │ ├── 8e2eb240a6cd7be1a0d308125afe0060b020e89275ced2e729eda7d4eeff62a2
│ │ │ ├── ad59e9f71edceca7b1ac7c642410858489b743c97233b0a26a5e2098b1443762
│ │ │ └── c6b39de5b33961661dc939b997cc1d30cda01e38005a6c6625fd9c7e748bab44
│ │ ├── TestEXT4ExtendedAttributes.swift
│ │ ├── TestEXT4Format+Create.swift
│ │ ├── TestEXT4Format.swift
│ │ ├── TestEXT4Reader+IO.swift
│ │ ├── TestEXT4Unpacker.swift
│ │ └── TestFormatterUnpack.swift
│ ├── ContainerizationExtrasTests/
│ │ ├── AsyncMutexTests.swift
│ │ ├── ProxyUtilsTests.swift
│ │ ├── TestCIDR.swift
│ │ ├── TestIPAddress.swift
│ │ ├── TestIPv4Address.swift
│ │ ├── TestIPv6Address+Parse.swift
│ │ ├── TestIPv6Address.swift
│ │ ├── TestIPv6IPv4Parsing.swift
│ │ ├── TestMACAddress.swift
│ │ ├── TestNetworkAddress+Allocator.swift
│ │ ├── TestPrefix.swift
│ │ ├── TestTimeout.swift
│ │ └── UInt8+DataBindingTest.swift
│ ├── ContainerizationNetlinkTests/
│ │ ├── MockNetlinkSocket.swift
│ │ ├── NetlinkSessionTest.swift
│ │ └── TypesTest.swift
│ ├── ContainerizationOCITests/
│ │ ├── AuthChallengeTests.swift
│ │ ├── OCIImageTests.swift
│ │ ├── OCIPlatformTests.swift
│ │ ├── OCISpecTests.swift
│ │ ├── ReferenceTests.swift
│ │ └── RegistryClientTests.swift
│ ├── ContainerizationOSTests/
│ │ ├── FileDescriptor+SecurePathTests.swift
│ │ ├── KeychainQueryTests.swift
│ │ ├── SocketTests.swift
│ │ └── UserTests.swift
│ ├── ContainerizationTests/
│ │ ├── ContainerManagerTests.swift
│ │ ├── DNSTests.swift
│ │ ├── HashTests.swift
│ │ ├── HostsTests.swift
│ │ ├── ImageTests/
│ │ │ ├── ContainsAuth.swift
│ │ │ ├── ImageStoreImagePullTests.swift
│ │ │ └── ImageStoreTests.swift
│ │ ├── ImageTests.swift
│ │ ├── KernelTests.swift
│ │ ├── LinuxContainerTests.swift
│ │ └── MountTests.swift
│ └── TestImages/
│ ├── dockermanifestimage/
│ │ └── Dockerfile
│ └── emptyimage/
│ └── Dockerfile
├── examples/
│ ├── README.md
│ └── ctr-example/
│ ├── Makefile
│ ├── Package.resolved
│ ├── Package.swift
│ ├── README.md
│ ├── Sources/
│ │ └── ctr-example/
│ │ └── main.swift
│ ├── ctr-example.entitlements
│ └── lab.md
├── kernel/
│ ├── Makefile
│ ├── README.md
│ ├── build.sh
│ ├── config-arm64
│ └── image/
│ ├── Dockerfile
│ └── sources.list
├── licenserc.toml
├── scripts/
│ ├── check-integration-test-vm-panics.sh
│ ├── cz-header-style.toml
│ ├── ensure-hawkeye-exists.sh
│ ├── install-hawkeye.sh
│ ├── license-header.txt
│ ├── make-docs.sh
│ └── pre-commit.fmt
├── signing/
│ └── vz.entitlements
└── vminitd/
├── .devcontainer/
│ ├── Dockerfile
│ └── devcontainer.json
├── Makefile
├── Package.resolved
├── Package.swift
└── Sources/
├── Cgroup/
│ └── Cgroup2Manager.swift
├── LCShim/
│ ├── include/
│ │ └── syscall.h
│ └── syscall.c
├── vmexec/
│ ├── Console.swift
│ ├── ExecCommand.swift
│ ├── Mount.swift
│ ├── RunCommand.swift
│ └── vmexec.swift
└── vminitd/
├── AgentCommand.swift
├── Application.swift
├── CommandRunner.swift
├── ContainerProcess.swift
├── HostStdio.swift
├── IOCloser+Extensions.swift
├── IOCloser.swift
├── IOPair.swift
├── InitCommand.swift
├── ManagedContainer.swift
├── ManagedProcess.swift
├── MemoryMonitor.swift
├── OSFile+Splice.swift
├── OSFile.swift
├── PauseCommand.swift
├── ProcessSupervisor.swift
├── Runc/
│ ├── ConsoleSocket.swift
│ └── Runc.swift
├── RuncProcess.swift
├── Server+GRPC.swift
├── Server.swift
├── StandardIO.swift
├── TerminalIO.swift
└── VsockProxy.swift
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/ISSUE_TEMPLATE/01-bug.yml
================================================
name: Bug report
description: File a bug report.
title: "[Bug]: "
type: "Bug"
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report!
- type: checkboxes
id: prereqs
attributes:
label: I have done the following
description: Select that you have completed the following prerequisites.
options:
- label: I have searched the existing issues
required: true
- label: If possible, I've reproduced the issue using the 'main' branch of this project
required: false
- type: textarea
id: reproduce
attributes:
label: Steps to reproduce
description: Explain how to reproduce the incorrect behavior.
validations:
required: true
- type: textarea
id: what-happened
attributes:
label: Current behavior
description: A concise description of what you're experiencing.
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected behavior
description: A concise description of what you expected to happen.
validations:
required: true
- type: textarea
attributes:
label: Environment
description: |
Examples:
- **OS**: macOS 26.0 (25A354)
- **Xcode**: Version 26.0 (17A324)
- **Swift**: Apple Swift version 6.2 (swift-6.2-RELEASE)
value: |
- OS:
- Xcode:
- Swift:
render: markdown
validations:
required: true
- type: textarea
id: logs
attributes:
label: Relevant log output
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
value: |
N/A
render: shell
- type: checkboxes
id: terms
attributes:
label: Code of Conduct
description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/apple/.github/blob/main/CODE_OF_CONDUCT.md).
options:
- label: I agree to follow this project's Code of Conduct
required: true
================================================
FILE: .github/ISSUE_TEMPLATE/02-feature.yml
================================================
name: Feature or enhancement request
description: File a request for a feature or enhancement
title: "[Request]: "
type: "Feature"
body:
- type: markdown
attributes:
value: |
Thanks for contributing to the containerization project!
- type: textarea
id: request
attributes:
label: Feature or enhancement request details
description: Describe your proposed feature or enhancement. Code samples that show what's missing, or what new capabilities will be possible, are very helpful! Provide links to existing issues or external references/discussions, if appropriate.
validations:
required: true
- type: checkboxes
id: terms
attributes:
label: Code of Conduct
description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/apple/.github/blob/main/CODE_OF_CONDUCT.md).
options:
- label: I agree to follow this project's Code of Conduct
required: true
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
- name: Containerization community support
url: https://github.com/apple/container/discussions
about: Please ask and answer questions here.
================================================
FILE: .github/workflows/build-test-images.yml
================================================
name: Build and publish containerization test images
permissions:
contents: read
on:
workflow_dispatch:
inputs:
publish:
type: boolean
description: "Publish the built image"
default: false
version:
type: string
description: "Version of the image to create"
default: "test"
image:
type: choice
description: Test image to build
options:
- dockermanifestimage
- emptyimage
default: 'dockermanifestimage'
useBuildx:
type: boolean
description: "Use docker buildx to build the image"
default: false
jobs:
image:
name: Build test images
timeout-minutes: 30
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Check branch
env:
GH_REF: ${{ github.ref }}
PUBLISH: ${{ inputs.publish }}
run: |
if [[ "${GH_REF}" != "refs/heads/main" ]] && [[ "${GH_REF}" != refs/heads/release* ]] && [[ "${PUBLISH}" == "true" ]]; then
echo "❌ Cannot publish an image if we are not on main or a release branch."
exit 1
fi
- name: Check inputs
env:
IMAGE: ${{ inputs.image }}
USE_BUILDX: ${{ inputs.useBuildx }}
run: |
if [[ "${IMAGE}" == "dockermanifestimage" ]] && [[ "${USE_BUILDX}" == "true" ]]; then
echo "❌ dockermanifestimage cannot be built with buildx"
exit 1
fi
if [[ "${IMAGE}" == "emptyimage" ]] && [[ "${USE_BUILDX}" != "true" ]]; then
echo "❌ emptyimage should be built with buildx"
exit 1
fi
- name: Checkout repository
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
- name: Login to GitHub Container Registry
uses: docker/login-action@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
- name: Build dockerfile and push image
uses: docker/build-push-action@v6
with:
push: ${{ inputs.publish }}
context: Tests/TestImages/${{ inputs.image }}
tags: ghcr.io/apple/containerization/${{ inputs.image }}:${{ inputs.version }}
================================================
FILE: .github/workflows/containerization-build-template.yml
================================================
name: Build containerization template
permissions:
contents: read
on:
workflow_call:
inputs:
release:
type: boolean
description: "Create a release"
default: false
version:
type: string
description: Version of containerization
default: test
jobs:
buildAndTest:
name: Build and Test repo
if: github.repository == 'apple/containerization'
timeout-minutes: 60
runs-on: [self-hosted, macos, tahoe, ARM64]
permissions:
contents: read
packages: write
env:
DEVELOPER_DIR: "/Applications/Xcode-latest.app/Contents/Developer"
steps:
- name: Checkout repository
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
with:
fetch-depth: 0
- name: Activate Swiftly
run: |
source /opt/swiftly/env.sh
cat /opt/swiftly/env.sh
- name: Check formatting
run: |
./scripts/install-hawkeye.sh
make fmt
git diff
if ! git diff --quiet ; then echo the following files require formatting or license headers: ; git diff --name-only ; false ; fi
- name: Check protobufs
run: |
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
run: |
make clean containerization docs
tar cfz _site.tgz _site
env:
BUILD_CONFIGURATION: ${{ inputs.release && 'release' || 'debug' }}
- name: Make vminitd image
run: |
source /opt/swiftly/env.sh
make -C vminitd swift linux-sdk
make init
env:
BUILD_CONFIGURATION: ${{ inputs.release && 'release' || 'debug' }}
- name: Test containerization
run: |
make fetch-default-kernel
make test integration
env:
REGISTRY_TOKEN: ${{ github.token }}
REGISTRY_USERNAME: ${{ github.actor }}
- name: Push vminitd image
if: ${{ inputs.release }}
env:
REGISTRY_TOKEN: ${{ github.token }}
REGISTRY_USERNAME: ${{ github.actor }}
REGISTRY_HOST: ghcr.io
VERSION: ${{ inputs.version }}
run: |
bin/cctl images tag vminit:latest "ghcr.io/apple/containerization/vminit:${VERSION}"
bin/cctl images push "ghcr.io/apple/containerization/vminit:${VERSION}"
- name: Create image tar
if: ${{ !inputs.release }}
run: |
bin/cctl images save vminit:latest -o vminit.tar
- name: Save vminit artifact
if: ${{ !inputs.release }}
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: vminit
path: vminit.tar
- name: Save documentation artifact
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: api-docs
path: "./_site.tgz"
retention-days: 14
uploadPages:
# Separate upload step required because upload-pages-artifact needs
# gtar which is not on the macOS runner.
name: Upload artifact for GitHub Pages
needs: buildAndTest
timeout-minutes: 5
runs-on: ubuntu-latest
steps:
- name: Setup Pages
uses: actions/configure-pages@v5
- name: Download a single artifact
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
with:
name: api-docs
- name: Add API docs to documentation
run: |
tar xfz _site.tgz
- name: Upload Artifact
uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4
with:
path: "./_site"
================================================
FILE: .github/workflows/containerization-build.yml
================================================
name: Build containerization
permissions:
contents: read
on:
pull_request:
types: [opened, reopened, synchronize]
push:
branches:
- main
- release/*
jobs:
verify-signatures:
name: Verify commit signatures
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- name: Check all commits are signed
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
commits=$(gh api "repos/${REPO}/pulls/${PR_NUMBER}/commits" --paginate)
unsigned_commits=""
while IFS='|' read -r sha author verified; do
if [ "$verified" != "true" ]; then
unsigned_commits="$unsigned_commits - $sha by $author\n"
fi
done < <(echo "$commits" | jq -r '.[] | "\(.sha)|\(.commit.author.name)|\(.commit.verification.verified)"')
if [ -n "$unsigned_commits" ]; then
echo "::error::The following commits are not signed:"
echo -e "$unsigned_commits"
echo ""
echo "Please sign your commits. See:"
echo " - https://github.com/apple/containerization/blob/main/CONTRIBUTING.md#pull-requests"
echo " - https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits"
exit 1
fi
echo "All commits are signed!"
containerization:
permissions:
contents: read
packages: write
pages: write
uses: ./.github/workflows/containerization-build-template.yml
secrets: inherit
================================================
FILE: .github/workflows/docs-release.yaml
================================================
# Manual workflow for releasing docs ad-hoc. Workflow can only be run for main or release branches.
# Workflow does NOT publish a release of containerization.
name: Deploy application website
permissions:
contents: read
on:
workflow_dispatch:
jobs:
checkBranch:
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags') || startsWith(github.ref, 'refs/heads/release')
steps:
- name: Branch validation
env:
REF_NAME: ${{ github.ref_name }}
run: echo "Branch ${REF_NAME} is allowed"
buildSite:
name: Build application website
needs: checkBranch
uses: ./.github/workflows/containerization-build-template.yml
secrets: inherit
permissions:
contents: read
packages: write
pages: write
deployDocs:
runs-on: ubuntu-latest
needs: [checkBranch, buildSite]
permissions:
contents: read
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
================================================
FILE: .github/workflows/release.yml
================================================
name: Release containerization
permissions:
contents: read
on:
push:
tags:
- "[0-9]+\\.[0-9]+\\.[0-9]+"
jobs:
containerization:
uses: ./.github/workflows/containerization-build-template.yml
with:
release: true
version: ${{ github.ref_name }}
secrets: inherit
permissions:
contents: read
packages: write
pages: write
deployDocs:
if: startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
needs: containerization
permissions:
contents: read
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
release:
if: startsWith(github.ref, 'refs/tags/')
name: Publish release
timeout-minutes: 30
needs: containerization
runs-on: ubuntu-latest
permissions:
contents: write
packages: read
steps:
- name: Create release
uses: softprops/action-gh-release@v2
with:
token: ${{ github.token }}
name: ${{ github.ref_name }}-prerelease
draft: true
make_latest: false
prerelease: true
fail_on_unmatched_files: true
================================================
FILE: .gitignore
================================================
.DS_Store
bin
libexec
.build
.local
xcuserdata/
DerivedData/
.swiftpm/
.netrc
.swiftpm
workdir/
installer/
.venv/
test_results/
*.pid
*.log
*.zip
*.o
*.ext4
*.pkg
*.swp
*.tar.gz
*.tar.xz
vmlinux
# API docs for local preview only.
_site/
_serve/
================================================
FILE: .spi.yml
================================================
version: 1
builder:
configs:
- documentation_targets: [Containerization, ContainerizationEXT4, ContainerizationOS, ContainerizationOCI, ContainerizationNetlink, ContainerizationIO, ContainerizationExtras, ContainerizationArchive, SendableProperty]
swift_version: '6.2'
================================================
FILE: .swift-format
================================================
{
"fileScopedDeclarationPrivacy" : {
"accessLevel" : "private"
},
"indentation" : {
"spaces" : 4
},
"indentConditionalCompilationBlocks" : false,
"indentSwitchCaseLabels" : false,
"lineBreakAroundMultilineExpressionChainComponents" : false,
"lineBreakBeforeControlFlowKeywords" : false,
"lineBreakBeforeEachArgument" : false,
"lineBreakBeforeEachGenericRequirement" : false,
"lineLength" : 180,
"maximumBlankLines" : 1,
"multiElementCollectionTrailingCommas" : true,
"noAssignmentInExpressions" : {
"allowedFunctions" : [
"XCTAssertNoThrow"
]
},
"prioritizeKeepingFunctionOutputTogether" : false,
"respectsExistingLineBreaks" : true,
"rules" : {
"AllPublicDeclarationsHaveDocumentation" : false,
"AlwaysUseLowerCamelCase" : true,
"AmbiguousTrailingClosureOverload" : false,
"BeginDocumentationCommentWithOneLineSummary" : false,
"DoNotUseSemicolons" : true,
"DontRepeatTypeInStaticProperties" : true,
"FileScopedDeclarationPrivacy" : true,
"FullyIndirectEnum" : true,
"GroupNumericLiterals" : true,
"IdentifiersMustBeASCII" : true,
"NeverForceUnwrap" : true,
"NeverUseForceTry" : true,
"NeverUseImplicitlyUnwrappedOptionals" : true,
"NoAccessLevelOnExtensionDeclaration" : true,
"NoAssignmentInExpressions" : true,
"NoBlockComments" : false,
"NoCasesWithOnlyFallthrough" : true,
"NoEmptyTrailingClosureParentheses" : true,
"NoLabelsInCasePatterns" : true,
"NoLeadingUnderscores" : false,
"NoParensAroundConditions" : true,
"NoPlaygroundLiterals" : true,
"NoVoidReturnOnFunctionSignature" : true,
"OmitExplicitReturns" : true,
"OneCasePerLine" : true,
"OneVariableDeclarationPerLine" : true,
"OnlyOneTrailingClosureArgument" : true,
"OrderedImports" : true,
"ReplaceForEachWithForLoop" : true,
"ReturnVoidInsteadOfEmptyTuple" : true,
"TypeNamesShouldBeCapitalized" : true,
"UseEarlyExits" : true,
"UseLetInEveryBoundCaseVariable" : true,
"UseShorthandTypeNames" : true,
"UseSingleLinePropertyGetter" : true,
"UseSynthesizedInitializer" : true,
"UseTripleSlashForDocumentationComments" : true,
"UseWhereClausesInForLoops" : false,
"ValidateDocumentationComments" : true
},
"spacesAroundRangeFormationOperators" : false,
"tabWidth" : 2,
"version" : 1
}
================================================
FILE: .swift-format-nolint
================================================
{
"fileScopedDeclarationPrivacy" : {
"accessLevel" : "private"
},
"indentation" : {
"spaces" : 4
},
"indentConditionalCompilationBlocks" : false,
"indentSwitchCaseLabels" : false,
"lineBreakAroundMultilineExpressionChainComponents" : false,
"lineBreakBeforeControlFlowKeywords" : false,
"lineBreakBeforeEachArgument" : false,
"lineBreakBeforeEachGenericRequirement" : false,
"lineLength" : 180,
"maximumBlankLines" : 1,
"multiElementCollectionTrailingCommas" : true,
"noAssignmentInExpressions" : {
"allowedFunctions" : [
"XCTAssertNoThrow"
]
},
"prioritizeKeepingFunctionOutputTogether" : false,
"respectsExistingLineBreaks" : true,
"rules" : {
"AllPublicDeclarationsHaveDocumentation" : false,
"AlwaysUseLowerCamelCase" : false,
"AmbiguousTrailingClosureOverload" : false,
"BeginDocumentationCommentWithOneLineSummary" : false,
"DoNotUseSemicolons" : true,
"DontRepeatTypeInStaticProperties" : false,
"FileScopedDeclarationPrivacy" : false,
"FullyIndirectEnum" : false,
"GroupNumericLiterals" : false,
"IdentifiersMustBeASCII" : false,
"NeverForceUnwrap" : false,
"NeverUseForceTry" : false,
"NeverUseImplicitlyUnwrappedOptionals" : false,
"NoAccessLevelOnExtensionDeclaration" : false,
"NoAssignmentInExpressions" : false,
"NoBlockComments" : false,
"NoCasesWithOnlyFallthrough" : false,
"NoEmptyTrailingClosureParentheses" : true,
"NoLabelsInCasePatterns" : false,
"NoLeadingUnderscores" : false,
"NoParensAroundConditions" : true,
"NoPlaygroundLiterals" : false,
"NoVoidReturnOnFunctionSignature" : true,
"OmitExplicitReturns" : false,
"OneCasePerLine" : true,
"OneVariableDeclarationPerLine" : true,
"OnlyOneTrailingClosureArgument" : false,
"OrderedImports" : true,
"ReplaceForEachWithForLoop" : false,
"ReturnVoidInsteadOfEmptyTuple" : false,
"TypeNamesShouldBeCapitalized" : false,
"UseEarlyExits" : false,
"UseLetInEveryBoundCaseVariable" : false,
"UseShorthandTypeNames" : true,
"UseSingleLinePropertyGetter" : true,
"UseSynthesizedInitializer" : false,
"UseTripleSlashForDocumentationComments" : true,
"UseWhereClausesInForLoops" : false,
"ValidateDocumentationComments" : false
},
"spacesAroundRangeFormationOperators" : false,
"tabWidth" : 2,
"version" : 1
}
================================================
FILE: .swift-version
================================================
6.3-snapshot-2026-02-27
================================================
FILE: CONTRIBUTING.md
================================================
# 🌈 📦️ Welcome to the Containerization community! 📦️ 🌈
Contributions to Containerization are welcomed and encouraged.
## Index
- [How you can help](#how-you-can-help)
- [Submitting issues and pull requests](#submitting-issues-and-pull-requests)
- [New to open source?](#new-to-open-source)
- [AI contribution guidelines](#ai-contribution-guidelines)
- [Code of conduct](#code-of-conduct)
## How you can help
We would love your contributions in the form of:
🐛 Bug fixes\
⚡️ Performance improvements\
✨ API additions or enhancements\
📝 Documentation\
🧑💻 Project advocacy: blogs, conference talks, and more
Anything else that could enhance the project!
## Submitting issues and pull requests
### Issues
To file a bug or feature request, use [GitHub issues](https://github.com/apple/containerization/issues/new).
🚧 For unexpected behavior or usability limitations, detailed instructions on how to reproduce the issue are appreciated. This will greatly help the priority setting and speed at which maintainers can get to your issue.
### Pull requests
We require all commits be signed with any of GitHub's supported methods, such as GPG or SSH. Information on how to set this up can be found on [GitHub's docs](https://docs.github.com/en/authentication/managing-commit-signature-verification/about-commit-signature-verification#about-commit-signature-verification).
To make a pull request, use [GitHub](https://github.com/apple/containerization/compare). Please give the team a few days to review but it's ok to check in on occasion. We appreciate your contribution!
> [!IMPORTANT]
> If you plan to make substantial changes or add new features, we encourage you to first discuss them with the wider containerization developer community.
> You can do this by filing a [GitHub issue](https://github.com/apple/containerization/issues/new).
> This will save time and increases the chance of your pull request being accepted.
We use a "squash and merge" strategy to keep our `main` branch history clean and easy to follow. When your pull request
is merged, all of your commits will be combined into a single commit.
With the "squash and merge" strategy, the *title* and *body* of your pull request is extremely important. It will become the commit message
for the squashed commit. Think of it as the single, definitive description of your contribution.
Before merging, we'll review the pull request title and body to ensure it:
* Clearly and concisely describes the changes.
* Uses the imperative mood (for example, "Add feature," "Fix bug").
* Provides enough context for future developers to understand the purpose of the change.
The pull request description should be concise and accurately describe the *what* and *why* of your changes.
#### .gitignore contributions
We do not currently accept contributions to add editor specific additions to the root .gitignore. We urge contributors to make a global .gitignore file with their rulesets they may want to add instead. A global .gitignore file can be set like so:
```bash
git config --global core.excludesfile ~/.gitignore
```
#### Formatting contributions
Make sure your contributions are consistent with the rest of the project's formatting. You can do this using our Makefile:
```bash
make fmt
```
#### Applying license header to new files
If you submit a contribution that adds a new file, please add the license header. You can do this using our Makefile:
```bash
make update-licenses
```
## New to open source?
### How do I pick something to work on?
Take a look at the `good first issue` label in the [containerization](https://github.com/apple/containerization/contribute) or [container](https://github.com/apple/container/contribute) project.
Before you start working on an issue:
* Check the comments, assignees, and any references to pull requests — make sure nobody else is actively working on it, or awaiting help or review.
* If someone is assigned to the issue or volunteered to work on it, and there are no signs of progress or activity over at least the past month, don't hesitate to check in with them
* Leave a comment that you have started working on it.
### Getting help
Don't be afraid to ask for help! When asking for help, provide as much information as possible, while highlighting anything you think may be important. Refer to the [MAINTAINERS.txt](MAINTAINERS.txt) file for the appropriate people to ping.
### I didn't get a response from someone. What should I do?
It's possible that you ask someone a question in an issue/pull request and you don't get a response as quickly as you'd like. If you don't get a response within a week, it's okay to politely ping them using an `@` mention. If you don't get a response for 2-3 weeks in a row, please ping someone else.
### I can't finish the contribution I started.
Sometimes an issue ends up bigger, harder, or more time-consuming than expected — **and that’s completely fine.** Be sure to comment on the issue saying you’re stepping away, so that someone else is able to pick it up.
## AI contribution guidelines
We welcome thoughtful use of AI tools in your contributions to this repository. We ask that you adhere to these rules in order to preserve the project's integrity, clarity, and quality, and to respect maintainer bandwidth:
* You should be able to explain and justify every line of code or documentation that was generated or assisted by AI. Your submission should reflect your own understanding and intent.
* Use AI to augment, not totally replace, your reasoning or familiarity, especially for non-trivial parts of the system.
* Avoid dumping AI-generated walls of text that you cannot explain. Low-effort, unexplained submissions will be deprioritized to protect maintainer bandwidth.
AI tools should be used to **enhance, not replace** the human elements that make OSS special: learning, collaboration, and community growth.
## Code of conduct
To clarify of what is expected of our contributors and community members, the Containerization team has adopted the code of conduct defined by the Contributor Covenant. This document is used across many open source communities and articulates our values well. For more detail, please read the [Code of Conduct](https://github.com/apple/.github/blob/main/CODE_OF_CONDUCT.md "Code of Conduct").
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: MAINTAINERS.txt
================================================
This file contains a list of maintainers and past maintainers who have made meaningful changes to this repository.
### Maintainers
Aditya Ramani (adityaramani)
AJ Emory (ajemory)
Danny Canter (dcantah)
Dmitry Kovba (dkovba)
Eric Ernst (egernst)
John Logan (jglogan)
Kathryn Baldauf (katiewasnothere)
Madhu Venugopal (mavenugo)
Michael Crosby (crosbymichael)
Raj Aryan Singh (realrajaryan)
Sidhartha Mani (wlan0)
Yibo Zhuang (yibozhuang)
### Emeritus maintainers
Agam Dua (agamdua)
Evan Hazlett (ehazlett)
Gilbert Song (gilbert88)
Hugh Bussell (hughbussell)
Tanweer Noor (tanweernoor)
Ximena Perez Diaz (ximenanperez)
================================================
FILE: Makefile
================================================
# Copyright © 2025-2026 Apple Inc. and the Containerization project authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Build configuration variables
BUILD_CONFIGURATION ?= debug
WARNINGS_AS_ERRORS ?= true
SWIFT_CONFIGURATION := $(if $(filter-out false,$(WARNINGS_AS_ERRORS)),-Xswiftc -warnings-as-errors) --disable-automatic-resolution
# Commonly used locations
SWIFT := "/usr/bin/swift"
ROOT_DIR := $(shell git rev-parse --show-toplevel)
BUILD_BIN_DIR = $(shell $(SWIFT) build -c $(BUILD_CONFIGURATION) --show-bin-path)
COV_DATA_DIR = $(shell $(SWIFT) test --show-coverage-path | xargs dirname)
COV_REPORT_FILE = $(ROOT_DIR)/code-coverage-report
# Variables for libarchive integration
LIBARCHIVE_UPSTREAM_REPO := https://github.com/libarchive/libarchive
LIBARCHIVE_UPSTREAM_VERSION := v3.7.7
LIBARCHIVE_LOCAL_DIR := workdir/libarchive
KATA_BINARY_PACKAGE := https://github.com/kata-containers/kata-containers/releases/download/3.17.0/kata-static-3.17.0-arm64.tar.xz
include Protobuf.Makefile
.DEFAULT_GOAL := all
.PHONY: all
all: containerization
all: init
.PHONY: release
release: BUILD_CONFIGURATION = release
release: all
.PHONY: containerization
containerization:
@echo Building containerization binaries...
@$(SWIFT) --version
@$(SWIFT) build -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION)
@echo Copying containerization binaries...
@mkdir -p bin
@install "$(BUILD_BIN_DIR)/cctl" ./bin/
@install "$(BUILD_BIN_DIR)/containerization-integration" ./bin/
@echo Signing containerization binaries...
@codesign --force --sign - --timestamp=none --entitlements=signing/vz.entitlements bin/cctl
@codesign --force --sign - --timestamp=none --entitlements=signing/vz.entitlements bin/containerization-integration
.PHONY: init
init: containerization vminitd
@echo Creating init.ext4...
@rm -f bin/init.rootfs.tar.gz bin/init.block
@./bin/cctl rootfs create \
--vminitd vminitd/bin/vminitd \
--vmexec vminitd/bin/vmexec \
--label org.opencontainers.image.source=https://github.com/apple/containerization \
--image vminit:latest \
bin/init.rootfs.tar.gz
.PHONY: cross-prep
cross-prep:
@"$(MAKE)" -C vminitd cross-prep
.PHONY: vminitd
vminitd:
@mkdir -p ./bin
@"$(MAKE)" -C vminitd BUILD_CONFIGURATION=$(BUILD_CONFIGURATION) WARNINGS_AS_ERRORS=$(WARNINGS_AS_ERRORS)
.PHONY: update-libarchive-source
update-libarchive-source:
@echo Updating the libarchive source files...
@git clone $(LIBARCHIVE_UPSTREAM_REPO) --depth 1 --branch $(LIBARCHIVE_UPSTREAM_VERSION) "$(LIBARCHIVE_LOCAL_DIR)"
@cp "$(LIBARCHIVE_LOCAL_DIR)/libarchive/archive_entry.h" Sources/ContainerizationArchive/CArchive/include
@cp "$(LIBARCHIVE_LOCAL_DIR)/libarchive/archive.h" Sources/ContainerizationArchive/CArchive/include
@cp "$(LIBARCHIVE_LOCAL_DIR)/COPYING" Sources/ContainerizationArchive/CArchive/COPYING
@rm -rf "$(LIBARCHIVE_LOCAL_DIR)"
.PHONY: test
test:
@echo Testing all test targets...
@$(SWIFT) test --enable-code-coverage $(SWIFT_CONFIGURATION)
.PHONY: coverage
coverage: test
@echo Generating code coverage report...
@xcrun llvm-cov show --compilation-dir=`pwd` \
-instr-profile=$(COV_DATA_DIR)/default.profdata \
--ignore-filename-regex=".build/" \
--ignore-filename-regex=".pb.swift" \
--ignore-filename-regex=".proto" \
--ignore-filename-regex=".grpc.swift" \
$(BUILD_BIN_DIR)/containerizationPackageTests.xctest/Contents/MacOS/containerizationPackageTests > $(COV_REPORT_FILE)
@echo Code coverage report generated: $(COV_REPORT_FILE)
.PHONY: integration
integration:
ifeq (,$(wildcard bin/vmlinux))
@echo No bin/vmlinux kernel found. See fetch-default-kernel target.
@exit 1
endif
@echo Running the integration tests...
@./bin/containerization-integration
.PHONY: fetch-default-kernel
fetch-default-kernel:
@mkdir -p .local/ bin/
ifeq (,$(wildcard .local/kata.tar.gz))
@curl -SsL -o .local/kata.tar.gz ${KATA_BINARY_PACKAGE}
endif
ifeq (,$(wildcard .local/vmlinux))
@tar -zxf .local/kata.tar.gz -C .local/ --strip-components=1
@cp -L .local/opt/kata/share/kata-containers/vmlinux.container .local/vmlinux
endif
ifeq (,$(wildcard bin/vmlinux))
@cp .local/vmlinux bin/vmlinux
endif
.PHONY: check
check: swift-fmt-check check-licenses
.PHONY: fmt
fmt: swift-fmt update-licenses
.PHONY: swift-fmt
SWIFT_SRC = $(shell find . -type f -name '*.swift' -not -path "*/.*" -not -path "*.pb.swift" -not -path "*.grpc.swift" -not -path "*/checkouts/*")
swift-fmt:
@echo Applying the standard code formatting...
@$(SWIFT) format --recursive --configuration .swift-format -i $(SWIFT_SRC)
swift-fmt-check:
@echo Applying the standard code formatting...
@$(SWIFT) format lint --recursive --strict --configuration .swift-format-nolint $(SWIFT_SRC)
.PHONY: update-licenses
update-licenses:
@echo Updating license headers...
@./scripts/ensure-hawkeye-exists.sh
@.local/bin/hawkeye format --fail-if-unknown --fail-if-updated false
.PHONY: check-licenses
check-licenses:
@echo Checking license headers existence in source files...
@./scripts/ensure-hawkeye-exists.sh
@.local/bin/hawkeye check --fail-if-unknown
.PHONY: pre-commit
pre-commit:
cp Scripts/pre-commit.fmt .git/hooks
touch .git/hooks/pre-commit
cat .git/hooks/pre-commit | grep -v 'hooks/pre-commit\.fmt' > /tmp/pre-commit.new || true
echo 'PRECOMMIT_NOFMT=$${PRECOMMIT_NOFMT} $$(git rev-parse --show-toplevel)/.git/hooks/pre-commit.fmt' >> /tmp/pre-commit.new
mv /tmp/pre-commit.new .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit
.PHONY: serve-docs
serve-docs:
@echo 'to browse: open http://127.0.0.1:8000/containerization/documentation/'
@rm -rf _serve
@mkdir -p _serve
@cp -a _site _serve/containerization
@python3 -m http.server --bind 127.0.0.1 --directory ./_serve
.PHONY: docs
docs:
@echo Updating API documentation...
@rm -rf _site
@scripts/make-docs.sh _site containerization
.PHONY: cleancontent
cleancontent:
@echo Cleaning the content...
@rm -rf ~/Library/Application\ Support/com.apple.containerization
.PHONY: clean
clean:
@echo Cleaning build files...
@rm -rf bin/
@rm -rf _site/
@rm -rf _serve/
@rm -f $(COV_REPORT_FILE)
@$(SWIFT) package clean
@"$(MAKE)" -C vminitd clean
================================================
FILE: Package.resolved
================================================
{
"originHash" : "8b51a9ec068537ab57ce9b8034b5b84a02a4697e4a6be491954e5fbda7e5783b",
"pins" : [
{
"identity" : "async-http-client",
"kind" : "remoteSourceControl",
"location" : "https://github.com/swift-server/async-http-client.git",
"state" : {
"revision" : "60235983163d040f343a489f7e2e77c1918a8bd9",
"version" : "1.26.1"
}
},
{
"identity" : "grpc-swift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/grpc/grpc-swift.git",
"state" : {
"revision" : "a56a157218877ef3e9625f7e1f7b2cb7e46ead1b",
"version" : "1.26.1"
}
},
{
"identity" : "swift-algorithms",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-algorithms.git",
"state" : {
"revision" : "87e50f483c54e6efd60e885f7f5aa946cee68023",
"version" : "1.2.1"
}
},
{
"identity" : "swift-argument-parser",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-argument-parser.git",
"state" : {
"revision" : "011f0c765fb46d9cac61bca19be0527e99c98c8b",
"version" : "1.5.1"
}
},
{
"identity" : "swift-asn1",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-asn1.git",
"state" : {
"revision" : "a54383ada6cecde007d374f58f864e29370ba5c3",
"version" : "1.3.2"
}
},
{
"identity" : "swift-async-algorithms",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-async-algorithms.git",
"state" : {
"revision" : "042e1c4d9d19748c9c228f8d4ebc97bb1e339b0b",
"version" : "1.0.4"
}
},
{
"identity" : "swift-atomics",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-atomics.git",
"state" : {
"revision" : "cd142fd2f64be2100422d658e7411e39489da985",
"version" : "1.2.0"
}
},
{
"identity" : "swift-certificates",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-certificates.git",
"state" : {
"revision" : "999fd70c7803da89f3904d635a6815a2a7cd7585",
"version" : "1.10.0"
}
},
{
"identity" : "swift-collections",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-collections.git",
"state" : {
"revision" : "c1805596154bb3a265fd91b8ac0c4433b4348fb0",
"version" : "1.2.0"
}
},
{
"identity" : "swift-crypto",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-crypto.git",
"state" : {
"revision" : "e8d6eba1fef23ae5b359c46b03f7d94be2f41fed",
"version" : "3.12.3"
}
},
{
"identity" : "swift-docc-plugin",
"kind" : "remoteSourceControl",
"location" : "https://github.com/swiftlang/swift-docc-plugin",
"state" : {
"revision" : "d1691545d53581400b1de9b0472d45eb25c19fed",
"version" : "1.4.4"
}
},
{
"identity" : "swift-docc-symbolkit",
"kind" : "remoteSourceControl",
"location" : "https://github.com/swiftlang/swift-docc-symbolkit",
"state" : {
"revision" : "b45d1f2ed151d057b54504d653e0da5552844e34",
"version" : "1.0.0"
}
},
{
"identity" : "swift-http-structured-headers",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-http-structured-headers.git",
"state" : {
"revision" : "db6eea3692638a65e2124990155cd220c2915903",
"version" : "1.3.0"
}
},
{
"identity" : "swift-http-types",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-http-types.git",
"state" : {
"revision" : "a0a57e949a8903563aba4615869310c0ebf14c03",
"version" : "1.4.0"
}
},
{
"identity" : "swift-log",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-log.git",
"state" : {
"revision" : "3d8596ed08bd13520157f0355e35caed215ffbfa",
"version" : "1.6.3"
}
},
{
"identity" : "swift-nio",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio.git",
"state" : {
"revision" : "34d486b01cd891297ac615e40d5999536a1e138d",
"version" : "2.83.0"
}
},
{
"identity" : "swift-nio-extras",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio-extras.git",
"state" : {
"revision" : "145db1962f4f33a4ea07a32e751d5217602eea29",
"version" : "1.28.0"
}
},
{
"identity" : "swift-nio-http2",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio-http2.git",
"state" : {
"revision" : "4281466512f63d1bd530e33f4aa6993ee7864be0",
"version" : "1.36.0"
}
},
{
"identity" : "swift-nio-ssl",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio-ssl.git",
"state" : {
"revision" : "173cc69a058623525a58ae6710e2f5727c663793",
"version" : "2.36.0"
}
},
{
"identity" : "swift-nio-transport-services",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio-transport-services.git",
"state" : {
"revision" : "cd1e89816d345d2523b11c55654570acd5cd4c56",
"version" : "1.24.0"
}
},
{
"identity" : "swift-numerics",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-numerics.git",
"state" : {
"revision" : "e0ec0f5f3af6f3e4d5e7a19d2af26b481acb6ba8",
"version" : "1.0.3"
}
},
{
"identity" : "swift-protobuf",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-protobuf.git",
"state" : {
"revision" : "102a647b573f60f73afdce5613a51d71349fe507",
"version" : "1.30.0"
}
},
{
"identity" : "swift-service-lifecycle",
"kind" : "remoteSourceControl",
"location" : "https://github.com/swift-server/swift-service-lifecycle.git",
"state" : {
"revision" : "e7187309187695115033536e8fc9b2eb87fd956d",
"version" : "2.8.0"
}
},
{
"identity" : "swift-system",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-system.git",
"state" : {
"revision" : "395a77f0aa927f0ff73941d7ac35f2b46d47c9db",
"version" : "1.6.3"
}
},
{
"identity" : "zstd",
"kind" : "remoteSourceControl",
"location" : "https://github.com/facebook/zstd.git",
"state" : {
"revision" : "f8745da6ff1ad1e7bab384bd1f9d742439278e99",
"version" : "1.5.7"
}
}
],
"version" : 3
}
================================================
FILE: Package.swift
================================================
// swift-tools-version: 6.2
//===----------------------------------------------------------------------===//
// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
// The swift-tools-version declares the minimum version of Swift required to build this package.
import CompilerPluginSupport
import Foundation
import PackageDescription
let package = Package(
name: "containerization",
platforms: [.macOS("15")],
products: [
.library(name: "Containerization", targets: ["Containerization", "ContainerizationError"]),
.library(name: "ContainerizationEXT4", targets: ["ContainerizationEXT4"]),
.library(name: "ContainerizationOCI", targets: ["ContainerizationOCI"]),
.library(name: "ContainerizationNetlink", targets: ["ContainerizationNetlink"]),
.library(name: "ContainerizationIO", targets: ["ContainerizationIO"]),
.library(name: "ContainerizationOS", targets: ["ContainerizationOS"]),
.library(name: "ContainerizationExtras", targets: ["ContainerizationExtras"]),
.library(name: "ContainerizationArchive", targets: ["ContainerizationArchive"]),
.executable(name: "cctl", targets: ["cctl"]),
],
dependencies: [
.package(url: "https://github.com/apple/swift-log.git", from: "1.0.0"),
.package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.3.0"),
.package(url: "https://github.com/apple/swift-collections.git", from: "1.1.4"),
.package(url: "https://github.com/apple/swift-crypto.git", from: "3.0.0"),
.package(url: "https://github.com/grpc/grpc-swift.git", from: "1.26.0"),
.package(url: "https://github.com/apple/swift-protobuf.git", from: "1.29.0"),
.package(url: "https://github.com/apple/swift-nio.git", from: "2.80.0"),
.package(url: "https://github.com/swift-server/async-http-client.git", from: "1.20.1"),
.package(url: "https://github.com/apple/swift-system.git", from: "1.4.0"),
.package(url: "https://github.com/swiftlang/swift-docc-plugin", from: "1.1.0"),
.package(url: "https://github.com/apple/swift-nio-ssl.git", from: "2.36.0"),
.package(url: "https://github.com/facebook/zstd.git", exact: "1.5.7"),
],
targets: [
.target(
name: "ContainerizationError"
),
.target(
name: "Containerization",
dependencies: [
.product(name: "Logging", package: "swift-log"),
.product(name: "GRPC", package: "grpc-swift"),
.product(name: "SystemPackage", package: "swift-system"),
.product(name: "_NIOFileSystem", package: "swift-nio"),
"ContainerizationArchive",
"ContainerizationOCI",
"ContainerizationOS",
"ContainerizationIO",
"ContainerizationExtras",
.target(name: "ContainerizationEXT4", condition: .when(platforms: [.macOS])),
],
exclude: [
"../Containerization/SandboxContext/SandboxContext.proto"
]
),
.executableTarget(
name: "cctl",
dependencies: [
.product(name: "Logging", package: "swift-log"),
.product(name: "ArgumentParser", package: "swift-argument-parser"),
"Containerization",
"ContainerizationArchive",
"ContainerizationEXT4",
"ContainerizationExtras",
"ContainerizationOCI",
"ContainerizationOS",
]
),
.executableTarget(
name: "containerization-integration",
dependencies: [
.product(name: "Logging", package: "swift-log"),
.product(name: "ArgumentParser", package: "swift-argument-parser"),
"Containerization",
],
path: "Sources/Integration"
),
.testTarget(
name: "ContainerizationUnitTests",
dependencies: ["Containerization"],
path: "Tests/ContainerizationTests",
resources: [
.copy("ImageTests/Resources/scratch.tar"),
.copy("ImageTests/Resources/scratch_no_annotations.tar"),
]
),
.target(
name: "ContainerizationEXT4",
dependencies: [
.target(name: "ContainerizationArchive", condition: .when(platforms: [.macOS])),
.product(name: "SystemPackage", package: "swift-system"),
"ContainerizationOS",
],
path: "Sources/ContainerizationEXT4",
exclude: [
"README.md"
]
),
.testTarget(
name: "ContainerizationEXT4Tests",
dependencies: [
"ContainerizationEXT4",
"ContainerizationArchive",
],
resources: [
.copy(
"Resources/content/blobs/sha256/ad59e9f71edceca7b1ac7c642410858489b743c97233b0a26a5e2098b1443762"), // index
.copy(
"Resources/content/blobs/sha256/48a06049d3738991b011ca8b12473d712b7c40666a1462118dae3c403676afc2"), // manifest
.copy(
"Resources/content/blobs/sha256/8e2eb240a6cd7be1a0d308125afe0060b020e89275ced2e729eda7d4eeff62a2"), // config
.copy(
"Resources/content/blobs/sha256/c6b39de5b33961661dc939b997cc1d30cda01e38005a6c6625fd9c7e748bab44"), // layer 1
.copy(
"Resources/content/blobs/sha256/4f4fb700ef54461cfa02571ae0db9a0dc1e0cdb5577484a6d75e68dc38e8acc1"), // layer 2
]
),
.target(
name: "ContainerizationArchive",
dependencies: [
.product(name: "SystemPackage", package: "swift-system"),
"CArchive",
"ContainerizationExtras",
"ContainerizationOS",
],
exclude: [
"CArchive"
]
),
.testTarget(
name: "ContainerizationArchiveTests",
dependencies: [
"ContainerizationArchive"
],
resources: [
.copy("Resources/test.tar.zst")
]
),
.target(
name: "CArchive",
dependencies: [
.product(name: "libzstd", package: "zstd")
],
path: "Sources/ContainerizationArchive/CArchive",
sources: [
"archive_swift_bridge.c"
],
cSettings: [
.define(
"PLATFORM_CONFIG_H", to: "\"config_darwin.h\"",
.when(platforms: [.iOS, .macOS, .macCatalyst, .watchOS, .driverKit, .tvOS])),
.define("PLATFORM_CONFIG_H", to: "\"config_linux.h\"", .when(platforms: [.linux])),
.unsafeFlags(["-fno-modules"]),
],
linkerSettings: [
.linkedLibrary("z"),
.linkedLibrary("bz2"),
.linkedLibrary("lzma"),
.linkedLibrary("archive"),
.linkedLibrary("iconv", .when(platforms: [.macOS])),
.linkedLibrary("crypto", .when(platforms: [.linux])),
]
),
.target(
name: "ContainerizationOCI",
dependencies: [
.product(name: "AsyncHTTPClient", package: "async-http-client"),
.product(name: "Crypto", package: "swift-crypto"),
.product(name: "Logging", package: "swift-log"),
.product(name: "_NIOFileSystem", package: "swift-nio"),
"ContainerizationError",
"ContainerizationOS",
"ContainerizationExtras",
]
),
.testTarget(
name: "ContainerizationOCITests",
dependencies: [
"ContainerizationOCI",
"Containerization",
"ContainerizationIO",
.product(name: "NIO", package: "swift-nio"),
.product(name: "Crypto", package: "swift-crypto"),
]
),
.target(
name: "ContainerizationNetlink",
dependencies: [
.product(name: "Logging", package: "swift-log"),
"ContainerizationOS",
"ContainerizationExtras",
]
),
.testTarget(
name: "ContainerizationNetlinkTests",
dependencies: [
"ContainerizationNetlink"
]
),
.target(
name: "ContainerizationOS",
dependencies: [
.product(name: "Logging", package: "swift-log"),
.product(name: "SystemPackage", package: "swift-system"),
"CShim",
"ContainerizationError",
],
exclude: [
"../ContainerizationOS/README.md"
]
),
.testTarget(
name: "ContainerizationOSTests",
dependencies: [
.product(name: "SystemPackage", package: "swift-system"),
"ContainerizationOS",
"ContainerizationExtras",
]
),
.target(
name: "ContainerizationIO",
dependencies: [
"ContainerizationOS",
.product(name: "NIO", package: "swift-nio"),
.product(name: "NIOCore", package: "swift-nio"),
.product(name: "NIOFoundationCompat", package: "swift-nio"),
]
),
.target(
name: "ContainerizationExtras",
dependencies: [
"ContainerizationError",
.product(name: "Collections", package: "swift-collections"),
.product(name: "Logging", package: "swift-log"),
.product(name: "NIOSSL", package: "swift-nio-ssl"),
]
),
.testTarget(
name: "ContainerizationExtrasTests",
dependencies: [
"ContainerizationExtras",
"CShim",
]
),
.target(
name: "CShim"
),
]
)
================================================
FILE: Protobuf.Makefile
================================================
# Copyright © 2025-2026 Apple Inc. and the Containerization project authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
LOCAL_DIR := $(ROOT_DIR)/.local
LOCAL_BIN_DIR := $(LOCAL_DIR)/bin
# Versions
PROTOC_VERSION := 26.1
# Protoc binary installation
PROTOC_ZIP := protoc-$(PROTOC_VERSION)-osx-universal_binary.zip
PROTOC := $(LOCAL_BIN_DIR)/protoc@$(PROTOC_VERSION)/protoc
$(PROTOC):
@echo Downloading protocol buffers...
@mkdir -p $(LOCAL_DIR)
@curl -OL https://github.com/protocolbuffers/protobuf/releases/download/v$(PROTOC_VERSION)/$(PROTOC_ZIP)
@mkdir -p $(dir $@)
@unzip -jo $(PROTOC_ZIP) bin/protoc -d $(dir $@)
@unzip -o $(PROTOC_ZIP) 'include/*' -d $(dir $@)
@rm -f $(PROTOC_ZIP)
.PHONY: protoc-gen-swift
protoc-gen-swift:
@$(SWIFT) build --product protoc-gen-swift
@$(SWIFT) build --product protoc-gen-grpc-swift
.PHONY: protos
protos: $(PROTOC) protoc-gen-swift
@echo Generating protocol buffers source code...
@$(PROTOC) Sources/Containerization/SandboxContext/SandboxContext.proto \
--plugin=protoc-gen-grpc-swift=$(BUILD_BIN_DIR)/protoc-gen-grpc-swift \
--plugin=protoc-gen-swift=$(BUILD_BIN_DIR)/protoc-gen-swift \
--proto_path=Sources/Containerization/SandboxContext \
--grpc-swift_out="Sources/Containerization/SandboxContext" \
--grpc-swift_opt=Visibility=Public \
--swift_out="Sources/Containerization/SandboxContext" \
--swift_opt=Visibility=Public \
-I.
@"$(MAKE)" update-licenses
.PHONY: clean-proto-tools
clean-proto-tools:
@echo Cleaning proto tools...
@rm -rf $(LOCAL_DIR)/bin/protoc*
================================================
FILE: README.md
================================================
# Containerization
The Containerization package allows applications to use Linux containers.
Containerization is written in [Swift](https://www.swift.org) and uses [Virtualization.framework](https://developer.apple.com/documentation/virtualization) on Apple silicon.
> **Looking for command line binaries for running containers?**\
> They are available in the dedicated [apple/container](https://github.com/apple/container) repository.
Containerization provides APIs to:
- [Manage OCI images](./Sources/ContainerizationOCI/).
- [Interact with remote registries](./Sources/ContainerizationOCI/Client/).
- [Create and populate ext4 file systems](./Sources/ContainerizationEXT4/).
- [Interact with the Netlink socket family](./Sources/ContainerizationNetlink/).
- [Create an optimized Linux kernel for fast boot times](./kernel/).
- [Spawn lightweight virtual machines and manage the runtime environment](./Sources/Containerization/LinuxContainer.swift).
- [Spawn and interact with containerized processes](./Sources/Containerization/LinuxProcess.swift).
- Use Rosetta 2 for running linux/amd64 containers on Apple silicon.
Please view the [API documentation](https://apple.github.io/containerization/documentation/) for information on the Swift packages that Containerization provides.
## Design
Containerization executes each Linux container inside of its own lightweight virtual machine. Clients can create dedicated IP addresses for every container to remove the need for individual port forwarding. Containers achieve sub-second start times using an optimized [Linux kernel configuration](/kernel) and a minimal root filesystem with a lightweight init system.
[vminitd](/vminitd) is a small init system, which is a subproject within Containerization.
`vminitd` is spawned as the initial process inside of the virtual machine and provides a GRPC API over vsock.
The API allows the runtime environment to be configured and containerized processes to be launched.
`vminitd` provides I/O, signals, and events to the calling process when a process is run.
## Requirements
To build the Containerization package, you need:
- Mac with Apple silicon
- macOS 26
- Xcode 26
Older versions of macOS are not supported.
## Example Usage
For examples of how to use some of the libraries surface, the cctl executable is a good start. This app is a useful playground for exploring the API. It contains commands that exercise some of the core functionality of the various products, such as:
1. [Manipulating OCI images](./Sources/cctl/ImageCommand.swift)
2. [Logging in to container registries](./Sources/cctl/LoginCommand.swift)
3. [Creating root filesystem blocks](./Sources/cctl/RootfsCommand.swift)
4. [Running simple Linux containers](./Sources/cctl/RunCommand.swift)
## Linux kernel
A Linux kernel is required for spawning lightweight virtual machines on macOS.
Containerization provides an optimized kernel configuration located in the [kernel](./kernel) directory.
This directory includes a containerized build environment to easily compile a kernel for use with Containerization.
The kernel configuration is a minimal set of features to support fast start times and a light weight environment.
While this configuration will work for the majority of workloads we understand that some will need extra features.
To solve this Containerization provides first class APIs to use different kernel configurations and versions on a per container basis.
This enables containers to be developed and validated across different kernel versions.
See the [README](/kernel/README.md) in the kernel directory for instructions on how to compile the optimized kernel.
### Kernel Support
Containerization allows user provided kernels but tests functionality starting with kernel version `6.14.9`.
### Pre-built Kernel
If you wish to consume a pre-built kernel, make sure it has `VIRTIO` drivers compiled into the kernel (not merely as modules).
The [Kata Containers](https://github.com/kata-containers/kata-containers) project provides a Linux kernel that is optimized for containers, with all required configuration options enabled. The [releases](https://github.com/kata-containers/kata-containers/releases/) page contains downloadable artifacts, and the image itself (`vmlinux.container`) can be found in the `/opt/kata/share/kata-containers/` directory.
## Prepare to build package
Install the recommended version of Xcode.
Set the active developer directory to the installed Xcode (replace `<PATH_TO_XCODE>`):
```bash
sudo xcode-select -s <PATH_TO_XCODE>
```
Install [Swiftly](https://github.com/swiftlang/swiftly), [Swift](https://www.swift.org), and [Static Linux SDK](https://www.swift.org/documentation/articles/static-linux-getting-started.html):
```bash
make cross-prep
```
If you use a custom terminal application, you may need to move this command from `.zprofile` to `.zshrc` (replace `<USERNAME>`):
```bash
# Added by swiftly
. "/Users/<USERNAME>/.swiftly/env.sh"
```
Restart the terminal application. Ensure this command returns `/Users/<USERNAME>/.swiftly/bin/swift` (replace `<USERNAME>`):
```bash
which swift
```
If you've installed or used a Static Linux SDK previously, you may need to remove older SDK versions from the system (replace `<SDK-ID>`):
```bash
swift sdk list
swift sdk remove <SDK-ID>
```
## Build the package
Build Containerization from sources:
```bash
make all
```
## Test the package
After building, run basic and integration tests:
```bash
make test integration
```
A kernel is required to run integration tests.
If you do not have a kernel locally for use a default kernel can be fetched using the `make fetch-default-kernel` target.
Fetching the default kernel only needs to happen after an initial build or after a `make clean`.
```bash
make fetch-default-kernel
make all test integration
```
## Protobufs
Containerization depends on specific versions of `grpc-swift` and `swift-protobuf`. You can install them and re-generate RPC interfaces with:
```bash
make protos
```
## Building a kernel
If you'd like to build your own kernel please see the instructions in the [kernel directory](./kernel/README.md).
## Pre-commit hook
Run `make pre-commit` to install a pre-commit hook that ensures that your changes have correct formatting and license headers when you run `git commit`.
## Documentation
Generate the API documentation for local viewing with:
```bash
make docs
make serve-docs
```
Preview the documentation by running in another terminal:
```bash
open http://localhost:8000/containerization/documentation/
```
## Contributing
Contributions to Containerization are welcomed and encouraged. Please see [CONTRIBUTING.md](/CONTRIBUTING.md) for more information.
## Project Status
Version 0.1.0 is the first official release of Containerization. Earlier versions have no source stability guarantees.
Because the Containerization library is under active development, source stability is only guaranteed within minor versions (for example, between 0.1.1 and 0.1.2). If you don't want potentially source-breaking package updates, you can specify your package dependency using .upToNextMinorVersion(from: "0.1.0") instead.
Future minor versions of the package may introduce changes to these rules as needed.
================================================
FILE: SECURITY.md
================================================
# Security disclosure process
If you believe that you have discovered a security or privacy vulnerability in our open source software, please report it to us using the [GitHub private vulnerability feature](https://github.com/apple/containerization/security/advisories/new). Reports should include specific product and software version(s) that you believe are affected; a technical description of the behavior that you observed and the behavior that you expected; the steps required to reproduce the issue; and a proof of concept or exploit.
The project team will do their best to acknowledge receiving all security reports within 7 days of submission. This initial acknowledgment is neither acceptance nor rejection of your report. The project team may come back to you with further questions or invite you to collaborate while working through the details of your report.
Keep these additional guidelines in mind when submitting your report:
* Reports concerning known, publicly disclosed CVEs can be submitted as normal issues to this project.
* Output from automated security scans or fuzzers MUST include additional context demonstrating the vulnerability with a proof of concept or working exploit.
* Application crashes due to malformed inputs are typically not treated as security vulnerabilities, unless they are shown to also impact other processes on the system.
While we welcome reports for open source software projects, they are not eligible for Apple Security Bounties.
================================================
FILE: Sources/CShim/capability.c
================================================
/*
* Copyright © 2025-2026 Apple Inc. and the Containerization project authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if defined(__linux__)
#include <sys/syscall.h>
#include <unistd.h>
#include "capability.h"
// Capability syscall wrappers
int CZ_capget(void *header, void *data) {
return syscall(SYS_capget, header, data);
}
int CZ_capset(void *header, void *data) {
return syscall(SYS_capset, header, data);
}
#endif
================================================
FILE: Sources/CShim/exec_command.c
================================================
/*
* Copyright © 2025-2026 Apple Inc. and the Containerization project authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if defined(__linux__) || defined(__APPLE__)
#include <errno.h>
#include <fcntl.h>
#include <dirent.h>
#include <limits.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/resource.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#if defined(__linux__)
#include <sys/prctl.h>
#endif
#include "exec_command.h"
#ifndef SYS_close_range
#define SYS_close_range 436
#endif
#ifndef CLOSE_RANGE_CLOEXEC
#define CLOSE_RANGE_CLOEXEC 0x4
#endif
static int mark_cloexec(int fd) {
int flags = fcntl(fd, F_GETFD);
if (flags == -1) return flags;
if (flags & FD_CLOEXEC) return 0;
return fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
}
static int cloexec_from(int min_fd) {
#if defined(__linux__)
// First try close_range.
long ret = syscall(SYS_close_range, min_fd, ~0U, CLOSE_RANGE_CLOEXEC);
if (ret == 0) {
return 0;
}
const char* dirpath = "/proc/self/fd";
#elif defined(__APPLE__)
const char* dirpath = "/dev/fd";
#endif
DIR *dp = opendir(dirpath);
if (!dp) return -1;
int dp_fd = dirfd(dp);
struct dirent *de;
while ((de = readdir(dp))) {
if (de->d_name[0] == '.') continue;
char *end;
long val = strtol(de->d_name, &end, 10);
if (*end || val < 0 || val > INT_MAX) continue;
int fd = (int)val;
if (fd < min_fd || fd == dp_fd) continue;
int ret = mark_cloexec(fd);
if (ret != 0) {
return ret;
}
}
close(dp_fd);
closedir(dp);
return 0;
}
void exec_command_attrs_init(struct exec_command_attrs *attrs) {
attrs->setpgid = 0;
attrs->pgid = 0;
attrs->setsid = 0;
attrs->setctty = 0;
attrs->ctty = 0;
attrs->mask = 0;
attrs->uid = -1;
attrs->gid = -1;
attrs->pdeathSignal = 0;
attrs->setfgpgrp = 0;
}
static void child_handler(const int sync_pipes[2], const char *executable,
char *const args[], char *const environment[],
const int file_handles[], const int file_handle_count,
const char *cwd, const sigset_t old_mask,
const struct exec_command_attrs attrs) {
int i = 0;
int err = 0;
int fd_index = 0;
int fd_table[file_handle_count];
struct rlimit limits = {0};
int syncfd = sync_pipes[1];
struct sigaction action = {0};
// Closing our parent's side of the pipe
if (close(sync_pipes[0]) < 0) {
goto fail;
}
// Setup process group and foreground before clearing signal mask.
if (attrs.setpgid) {
if (setpgid(0, attrs.pgid) < 0) {
goto fail;
}
}
// Make the new process group the foreground process group so it can read from the TTY.
if (attrs.setfgpgrp) {
if (tcsetpgrp(STDIN_FILENO, getpgrp()) < 0) {
if (errno != ENOTTY && errno != ENXIO) {
goto fail;
}
}
}
// clear sighandlers
action.sa_flags = 0;
action.sa_handler = SIG_DFL;
sigemptyset(&action.sa_mask);
for (i = 0; i < NSIG; i++) {
sigaction(i, &action, 0);
}
sigset_t local_mask;
sigemptyset(&local_mask);
if (pthread_sigmask(SIG_SETMASK, &local_mask, NULL) < 0) {
goto fail;
}
// start shuffling fds.
// look at all the file handles and find the highest one,
// use that for our pipe,
//
// Then, we need to start dup2 the fds starting for the final process
// at 0-n.
// as an example we have this list of FDs that should be passed to the
// process:
//
/*
The index of this list is the final result that the new process expects.
The values are open fds provided from the parent process.
[0] == 12
[1] == 7
[2] == 9
[3] == 0
We also have a pipe to sync the child and parent so that adds an additional
parameter to consider.
So we start by finding the highest open fd in the list, then move our pipe to
the next.
i.e. fd12 is highest so move our pipe to fd13
Now start moving all the fds above our pipe as we will need to start placing
the fds in the child process into the right order. Make sure they are all
marked cloexec.
pipe == 13
[0] == 12 dup2 14
[1] == 7 dup2 15
[2] == 9 dup2 16
[3] == 0 dup2 17
Now overwrite the fd table for the child with the current index.
Make index == fd.
pipe == 13
[0] == 14 dup2 0
[1] == 15 dup2 1
[2] == 16 dup2 2
[3] == 17 dup2 3
Clear cloexec on this new fds.
*/
// find the highest fd value in our list.
for (i = 0; i < file_handle_count; i++) {
if (file_handles[i] > fd_index) {
fd_index = file_handles[i];
}
fd_table[i] = file_handles[i];
}
// now fd_index is == to the highest fd in our list of handles.
// Increment it and set our pipe to it.
fd_index++;
if (syncfd != fd_index) {
if (dup2(syncfd, fd_index) < 0) {
goto fail;
}
if (close(syncfd) < 0) {
goto fail;
}
syncfd = fd_index;
}
fd_index++;
// make sure our syncfd retains its cloexec
if (fcntl(syncfd, F_SETFD, FD_CLOEXEC) == -1) {
goto fail;
}
// move the rest of the fds up above our index if they don't match the index.
for (i = 0; i < file_handle_count; i++) {
if (fd_table[i] == i) {
continue;
}
if (dup2(fd_table[i], fd_index) < 0) {
goto fail;
}
if (fcntl(fd_index, F_SETFD, FD_CLOEXEC) == -1) {
goto fail;
}
fd_table[i] = fd_index;
fd_index++;
}
// now create the child process's final fd table. where i == i
for (i = 0; i < file_handle_count; i++) {
if (fd_table[i] != i) {
if (dup2(fd_table[i], i) < 0) {
goto fail;
}
}
// now fd[i] should == i
// clear cloexec as this fd is where we want it.
if (fcntl(i, F_SETFD, 0) == -1) {
goto fail;
}
}
if (attrs.setsid) {
if (setsid() == -1) {
goto fail;
}
}
if (attrs.setctty) {
if (ioctl(attrs.ctty, TIOCSCTTY, 0)) {
goto fail;
}
}
#if defined(__linux__)
// Set parent death signal if specified
if (attrs.pdeathSignal != 0) {
if (prctl(PR_SET_PDEATHSIG, attrs.pdeathSignal) != 0) {
goto fail;
}
}
#endif
// close exec everything outside of our child's fd_table.
if (cloexec_from(file_handle_count) != 0) {
goto fail;
}
// set gid
if (attrs.gid != -1) {
if (setgid(attrs.gid) != 0) {
goto fail;
}
}
// set uid
if (attrs.uid != -1) {
if (setreuid(attrs.uid, attrs.uid) != 0) {
goto fail;
}
}
if (cwd != NULL) {
if (chdir(cwd)) {
goto fail;
}
}
execve(executable, args, environment);
fail:
err = errno;
if (err) {
// send our error to the parent
while (write(syncfd, &err, sizeof(err)) < 0)
;
}
exit(127);
}
int exec_command(pid_t *result, const char *executable, char *const args[],
char *const envp[], const int file_handles[],
const int file_handle_count, const char *working_directory,
struct exec_command_attrs *attrs) {
pid_t pid = 0;
int err = 0;
int sync_pipe[2];
sigset_t old_mask;
sigset_t all;
sigfillset(&all);
if (pipe(sync_pipe)) {
goto fail;
}
if (pthread_sigmask(SIG_SETMASK, &all, &old_mask) < 0) {
goto fail;
}
pid = fork();
if (pid == -1) {
close(sync_pipe[0]);
close(sync_pipe[1]);
goto fail;
}
if (pid == 0) {
// hand off to child
child_handler(sync_pipe, executable, args, envp, file_handles,
file_handle_count, working_directory, old_mask, *attrs);
exit(EXIT_FAILURE);
}
// handle parent operations
if (close(sync_pipe[1]) < 0) {
goto fail;
}
// sync with our child process
err = 0;
ssize_t size = read(sync_pipe[0], &err, sizeof(err));
// -- we didn't get an errno back
if (size != sizeof(err)) {
// will be used as return result
err = 0;
} else {
// we did get an errno back from the child process and our
// err var is set to that errno
// lets set our errno and then reap the process
errno = err;
int status = 0;
waitpid(pid, &status, 0);
// lets continue our journey below
}
if (close(sync_pipe[0]) < 0) {
goto fail;
}
if (err) {
goto fail;
}
(*result) = pid;
err = 0;
fail:
if (pthread_sigmask(SIG_SETMASK, &old_mask, 0) < 0) {
printf("restoring signal mask: %s\n", strerror(errno));
}
if (err) {
printf("exec_command execve: %s\n", strerror(err));
return -1;
}
return 0;
}
#endif
================================================
FILE: Sources/CShim/include/capability.h
================================================
/*
* Copyright © 2025-2026 Apple Inc. and the Containerization project authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __CAPABILITY_H
#define __CAPABILITY_H
#if defined(__linux__)
// Capability syscall wrappers
int CZ_capget(void *header, void *data);
int CZ_capset(void *header, void *data);
#endif
#endif
================================================
FILE: Sources/CShim/include/exec_command.h
================================================
/*
* Copyright © 2025-2026 Apple Inc. and the Containerization project authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef exec_command_h
#define exec_command_h
#if defined(__linux__) || defined(__APPLE__)
#include <sys/types.h>
#include <unistd.h>
struct exec_command_attrs {
int setpgid;
/// parent group id
pid_t pgid;
/// set the controlling terminal
int setctty;
/// controlling terminal fd
int ctty;
/// set the process as session leader
int setsid;
/// set the process user id
uid_t uid;
/// set the process group id
gid_t gid;
/// signal mask for the child process
int mask;
/// parent death signal (Linux only, 0 to disable)
int pdeathSignal;
/// make the new process group the foreground process group
int setfgpgrp;
};
void exec_command_attrs_init(struct exec_command_attrs *attrs);
/// spawn a new child process with the provided attrs
int exec_command(pid_t *result, const char *executable, char *const argv[],
char *const envp[], const int file_handles[],
const int file_handle_count, const char *working_directory,
struct exec_command_attrs *attrs);
#endif /* defined(__linux__) || defined(__APPLE__) */
#endif /* exec_command_h */
================================================
FILE: Sources/CShim/include/openat2.h
================================================
/*
* Copyright © 2026 Apple Inc. and the Containerization project authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __OPENAT2_H
#define __OPENAT2_H
#include <sys/types.h>
#ifndef RESOLVE_IN_ROOT
#define RESOLVE_IN_ROOT 0x10
#endif
struct cz_open_how {
unsigned long long flags;
unsigned long long mode;
unsigned long long resolve;
};
/// openat2(2) wrapper. Musl does not provide openat2 so we invoke the syscall
/// directly. Requires Linux 5.6+.
int CZ_openat2(int dirfd, const char *pathname, struct cz_open_how *how,
size_t size);
#endif
================================================
FILE: Sources/CShim/include/prctl.h
================================================
/*
* Copyright © 2025-2026 Apple Inc. and the Containerization project authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __PRCTL_H
#define __PRCTL_H
#if defined(__linux__)
#include <sys/types.h>
// Capability management prctl wrappers
int CZ_prctl_set_keepcaps();
int CZ_prctl_clear_keepcaps();
int CZ_prctl_capbset_drop(unsigned int capability);
int CZ_prctl_cap_ambient_clear_all();
int CZ_prctl_cap_ambient_raise(unsigned int capability);
#endif
#endif
================================================
FILE: Sources/CShim/include/socket_helpers.h
================================================
/*
* Copyright © 2025-2026 Apple Inc. and the Containerization project authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef socket_helpers_h
#define socket_helpers_h
#include <sys/socket.h>
#include <stdint.h>
// Helper functions to access CMSG macros from Swift
struct cmsghdr* CZ_CMSG_FIRSTHDR(struct msghdr *msg);
void* CZ_CMSG_DATA(struct cmsghdr *cmsg);
size_t CZ_CMSG_SPACE(size_t length);
size_t CZ_CMSG_LEN(size_t length);
#endif /* socket_helpers_h */
================================================
FILE: Sources/CShim/include/vsock.h
================================================
/*
* Copyright © 2025-2026 Apple Inc. and the Containerization project authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//
#ifndef vsock_h
#define vsock_h
#include <sys/ioctl.h>
#ifdef __APPLE__
#include <sys/vsock.h>
#else
#include <sys/socket.h>
#include <linux/vm_sockets.h>
#endif /* __APPLE__ */
extern const unsigned long VsockLocalCIDIoctl;
#endif /* vsock_h */
================================================
FILE: Sources/CShim/openat2.c
================================================
/*
* Copyright © 2026 Apple Inc. and the Containerization project authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if defined(__linux__)
#include <sys/syscall.h>
#include <unistd.h>
#include "openat2.h"
#ifndef SYS_openat2
#define SYS_openat2 437
#endif
int CZ_openat2(int dirfd, const char *pathname, struct cz_open_how *how,
size_t size) {
return syscall(SYS_openat2, dirfd, pathname, how, size);
}
#endif
================================================
FILE: Sources/CShim/prctl.c
================================================
/*
* Copyright © 2025-2026 Apple Inc. and the Containerization project authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if defined(__linux__)
#include <sys/prctl.h>
#include "prctl.h"
// Set keep caps to preserve capabilities across setuid()
int CZ_prctl_set_keepcaps() {
return prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
}
// Clear keep caps after user change
int CZ_prctl_clear_keepcaps() {
return prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0);
}
// Drop capability from bounding set
int CZ_prctl_capbset_drop(unsigned int capability) {
return prctl(PR_CAPBSET_DROP, capability, 0, 0, 0);
}
// Clear all ambient capabilities
int CZ_prctl_cap_ambient_clear_all() {
return prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0);
}
// Raise ambient capability
int CZ_prctl_cap_ambient_raise(unsigned int capability) {
return prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, capability, 0, 0);
}
#endif
================================================
FILE: Sources/CShim/socket_helpers.c
================================================
/*
* Copyright © 2025-2026 Apple Inc. and the Containerization project authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "socket_helpers.h"
struct cmsghdr* CZ_CMSG_FIRSTHDR(struct msghdr *msg) {
return CMSG_FIRSTHDR(msg);
}
void* CZ_CMSG_DATA(struct cmsghdr *cmsg) {
return CMSG_DATA(cmsg);
}
size_t CZ_CMSG_SPACE(size_t length) {
return CMSG_SPACE(length);
}
size_t CZ_CMSG_LEN(size_t length) {
return CMSG_LEN(length);
}
================================================
FILE: Sources/CShim/vsock.c
================================================
/*
* Copyright © 2025-2026 Apple Inc. and the Containerization project authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "vsock.h"
const unsigned long VsockLocalCIDIoctl = IOCTL_VM_SOCKETS_GET_LOCAL_CID;
================================================
FILE: Sources/Containerization/AttachedFilesystem.swift
================================================
//===----------------------------------------------------------------------===//
// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
import ContainerizationExtras
import ContainerizationOCI
/// A filesystem that was attached and able to be mounted inside the runtime environment.
public struct AttachedFilesystem: Sendable {
/// The type of the filesystem.
public var type: String
/// The path to the filesystem within a sandbox.
public var source: String
/// Destination when mounting the filesystem inside a sandbox.
public var destination: String
/// The options to use when mounting the filesystem.
public var options: [String]
#if os(macOS)
public init(mount: Mount, allocator: any AddressAllocator<Character>) throws {
switch mount.type {
case "virtiofs":
let name = try hashMountSource(source: mount.source)
self.source = name
case "ext4":
let char = try allocator.allocate()
self.source = "/dev/vd\(char)"
default:
self.source = mount.source
}
self.type = mount.type
self.options = mount.options
self.destination = mount.destination
}
#endif
public init(type: String, source: String, destination: String, options: [String]) {
self.type = type
self.source = source
self.destination = destination
self.options = options
}
}
================================================
FILE: Sources/Containerization/Container.swift
================================================
//===----------------------------------------------------------------------===//
// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
/// The core protocol container implementations must implement.
public protocol Container {
/// ID for the container.
var id: String { get }
/// The amount of cpus assigned to the container.
var cpus: Int { get }
/// The memory in bytes assigned to the container.
var memoryInBytes: UInt64 { get }
/// The network interfaces assigned to the container.
var interfaces: [any Interface] { get }
}
================================================
FILE: Sources/Containerization/ContainerManager.swift
================================================
//===----------------------------------------------------------------------===//
// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
#if os(macOS)
import ContainerizationError
import ContainerizationEXT4
import ContainerizationOCI
import ContainerizationOS
import Foundation
import ContainerizationExtras
import SystemPackage
import Virtualization
/// A manager for creating and running containers.
/// Supports container networking options.
public struct ContainerManager: Sendable {
public let imageStore: ImageStore
private let vmm: VirtualMachineManager
private var network: Network?
private var containerRoot: URL {
self.imageStore.path.appendingPathComponent("containers")
}
/// Create a new manager with the provided kernel, initfs mount, image store
/// and optional network implementation. This will use a Virtualization.framework
/// backed VMM implicitly.
public init(
kernel: Kernel,
initfs: Mount,
imageStore: ImageStore,
network: Network? = nil,
rosetta: Bool = false,
nestedVirtualization: Bool = false
) throws {
self.imageStore = imageStore
self.network = network
try Self.createRootDirectory(path: self.imageStore.path)
self.vmm = VZVirtualMachineManager(
kernel: kernel,
initialFilesystem: initfs,
rosetta: rosetta,
nestedVirtualization: nestedVirtualization
)
}
/// Create a new manager with the provided kernel, initfs mount, root state
/// directory and optional network implementation. This will use a Virtualization.framework
/// backed VMM implicitly.
public init(
kernel: Kernel,
initfs: Mount,
root: URL? = nil,
network: Network? = nil,
rosetta: Bool = false,
nestedVirtualization: Bool = false
) throws {
if let root {
self.imageStore = try ImageStore(path: root)
} else {
self.imageStore = ImageStore.default
}
self.network = network
try Self.createRootDirectory(path: self.imageStore.path)
self.vmm = VZVirtualMachineManager(
kernel: kernel,
initialFilesystem: initfs,
rosetta: rosetta,
nestedVirtualization: nestedVirtualization
)
}
/// Create a new manager with the provided kernel, initfs reference, image store
/// and optional network implementation. This will use a Virtualization.framework
/// backed VMM implicitly.
public init(
kernel: Kernel,
initfsReference: String,
imageStore: ImageStore,
network: Network? = nil,
rosetta: Bool = false,
nestedVirtualization: Bool = false
) async throws {
self.imageStore = imageStore
self.network = network
try Self.createRootDirectory(path: self.imageStore.path)
let initPath = self.imageStore.path.appendingPathComponent("initfs.ext4")
let initImage = try await self.imageStore.getInitImage(reference: initfsReference)
let initfs = try await {
do {
return try await initImage.initBlock(at: initPath, for: .linuxArm)
} catch let err as ContainerizationError {
guard err.code == .exists else {
throw err
}
return .block(
format: "ext4",
source: initPath.absolutePath(),
destination: "/",
options: ["ro"]
)
}
}()
self.vmm = VZVirtualMachineManager(
kernel: kernel,
initialFilesystem: initfs,
rosetta: rosetta,
nestedVirtualization: nestedVirtualization
)
}
/// Create a new manager with the provided kernel and image reference for the initfs.
/// This will use a Virtualization.framework backed VMM implicitly.
public init(
kernel: Kernel,
initfsReference: String,
root: URL? = nil,
network: Network? = nil,
rosetta: Bool = false,
nestedVirtualization: Bool = false
) async throws {
if let root {
self.imageStore = try ImageStore(path: root)
} else {
self.imageStore = ImageStore.default
}
self.network = network
try Self.createRootDirectory(path: self.imageStore.path)
let initPath = self.imageStore.path.appendingPathComponent("initfs.ext4")
let initImage = try await self.imageStore.getInitImage(reference: initfsReference)
let initfs = try await {
do {
return try await initImage.initBlock(at: initPath, for: .linuxArm)
} catch let err as ContainerizationError {
guard err.code == .exists else {
throw err
}
return .block(
format: "ext4",
source: initPath.absolutePath(),
destination: "/",
options: ["ro"]
)
}
}()
self.vmm = VZVirtualMachineManager(
kernel: kernel,
initialFilesystem: initfs,
rosetta: rosetta,
nestedVirtualization: nestedVirtualization
)
}
/// Create a new manager with the provided vmm and network.
public init(
vmm: any VirtualMachineManager,
network: Network? = nil
) throws {
self.imageStore = ImageStore.default
try Self.createRootDirectory(path: self.imageStore.path)
self.network = network
self.vmm = vmm
}
private static func createRootDirectory(path: URL) throws {
try FileManager.default.createDirectory(
at: path.appendingPathComponent("containers"),
withIntermediateDirectories: true
)
}
/// Returns a new container from the provided image reference.
/// - Parameters:
/// - id: The container ID.
/// - reference: The image reference.
/// - rootfsSizeInBytes: The size of the root filesystem in bytes. Defaults to 8 GiB.
/// - writableLayerSizeInBytes: Optional size for a separate writable layer. When provided,
/// the rootfs becomes read-only and an overlayfs is used with a separate writable layer of this size.
/// - readOnly: Whether to mount the root filesystem as read-only.
/// - networking: Whether to create a network interface for this container. Defaults to `true`.
/// When `false`, no network resources are allocated and `releaseNetwork`/`delete` remain safe to call.
public mutating func create(
_ id: String,
reference: String,
rootfsSizeInBytes: UInt64 = 8.gib(),
writableLayerSizeInBytes: UInt64? = nil,
readOnly: Bool = false,
networking: Bool = true,
configuration: (inout LinuxContainer.Configuration) throws -> Void
) async throws -> LinuxContainer {
let image = try await imageStore.get(reference: reference, pull: true)
return try await create(
id,
image: image,
rootfsSizeInBytes: rootfsSizeInBytes,
writableLayerSizeInBytes: writableLayerSizeInBytes,
readOnly: readOnly,
networking: networking,
configuration: configuration
)
}
/// Returns a new container from the provided image.
/// - Parameters:
/// - id: The container ID.
/// - image: The image.
/// - rootfsSizeInBytes: The size of the root filesystem in bytes. Defaults to 8 GiB.
/// - writableLayerSizeInBytes: Optional size for a separate writable layer. When provided,
/// the rootfs becomes read-only and an overlayfs is used with a separate writable layer of this size.
/// - readOnly: Whether to mount the root filesystem as read-only.
/// - networking: Whether to create a network interface for this container. Defaults to `true`.
/// When `false`, no network resources are allocated and `releaseNetwork`/`delete` remain safe to call.
public mutating func create(
_ id: String,
image: Image,
rootfsSizeInBytes: UInt64 = 8.gib(),
writableLayerSizeInBytes: UInt64? = nil,
readOnly: Bool = false,
networking: Bool = true,
configuration: (inout LinuxContainer.Configuration) throws -> Void
) async throws -> LinuxContainer {
let path = try createContainerRoot(id)
var rootfs = try await unpack(
image: image,
destination: path.appendingPathComponent("rootfs.ext4"),
size: rootfsSizeInBytes
)
if readOnly {
rootfs.options.append("ro")
}
// Create writable layer if size is specified.
var writableLayer: Mount? = nil
if let writableLayerSize = writableLayerSizeInBytes {
writableLayer = try createEmptyFilesystem(
at: path.appendingPathComponent("writable.ext4"),
size: writableLayerSize
)
}
return try await create(
id,
image: image,
rootfs: rootfs,
writableLayer: writableLayer,
networking: networking,
configuration: configuration
)
}
/// Returns a new container from the provided image and root filesystem mount.
/// - Parameters:
/// - id: The container ID.
/// - image: The image.
/// - rootfs: The root filesystem mount pointing to an existing block file.
/// The `destination` field is ignored as mounting is handled internally.
/// - writableLayer: Optional writable layer mount. When provided, an overlayfs is used with
/// rootfs as the lower layer and this as the upper layer.
/// The `destination` field is ignored as mounting is handled internally.
/// - networking: Whether to create a network interface for this container. Defaults to `true`.
/// When `false`, no network resources are allocated and `releaseNetwork`/`delete` remain safe to call.
public mutating func create(
_ id: String,
image: Image,
rootfs: Mount,
writableLayer: Mount? = nil,
networking: Bool = true,
configuration: (inout LinuxContainer.Configuration) throws -> Void
) async throws -> LinuxContainer {
let imageConfig = try await image.config(for: .current).config
return try LinuxContainer(
id,
rootfs: rootfs,
writableLayer: writableLayer,
vmm: self.vmm
) { config in
if let imageConfig {
config.process = .init(from: imageConfig)
}
if networking, let interface = try self.network?.createInterface(id) {
config.interfaces = [interface]
guard let gateway = interface.ipv4Gateway else {
throw ContainerizationError(
.invalidState,
message: "missing ipv4 gateway for container \(id)"
)
}
config.dns = .init(nameservers: [gateway.description])
}
config.bootLog = BootLog.file(path: self.containerRoot.appendingPathComponent(id).appendingPathComponent("bootlog.log"))
try configuration(&config)
}
}
/// Releases network resources for a container.
///
/// - Parameter id: The container ID.
public mutating func releaseNetwork(_ id: String) throws {
try self.network?.releaseInterface(id)
}
/// Releases network resources and removes all files for a container.
/// - Parameter id: The container ID.
public mutating func delete(_ id: String) throws {
try self.releaseNetwork(id)
let path = containerRoot.appendingPathComponent(id)
try FileManager.default.removeItem(at: path)
}
private func createContainerRoot(_ id: String) throws -> URL {
let path = containerRoot.appendingPathComponent(id)
try FileManager.default.createDirectory(at: path, withIntermediateDirectories: false)
return path
}
private func unpack(image: Image, destination: URL, size: UInt64) async throws -> Mount {
do {
let unpacker = EXT4Unpacker(blockSizeInBytes: size)
return try await unpacker.unpack(image, for: .current, at: destination)
} catch let err as ContainerizationError {
if err.code == .exists {
return .block(
format: "ext4",
source: destination.absolutePath(),
destination: "/",
options: []
)
}
throw err
}
}
private func createEmptyFilesystem(at destination: URL, size: UInt64) throws -> Mount {
let path = destination.absolutePath()
guard !FileManager.default.fileExists(atPath: path) else {
throw ContainerizationError(.exists, message: "filesystem already exists at \(path)")
}
let filesystem = try EXT4.Formatter(FilePath(path), minDiskSize: size)
try filesystem.close()
return .block(
format: "ext4",
source: path,
destination: "/",
options: []
)
}
}
extension CIDRv4 {
/// The gateway address of the network.
public var gateway: IPv4Address {
IPv4Address(self.lower.value + 1)
}
}
#endif
================================================
FILE: Sources/Containerization/ContainerStatistics.swift
================================================
//===----------------------------------------------------------------------===//
// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
/// Statistics for a container.
public struct ContainerStatistics: Sendable {
public var id: String
public var process: ProcessStatistics?
public var memory: MemoryStatistics?
public var cpu: CPUStatistics?
public var blockIO: BlockIOStatistics?
public var networks: [NetworkStatistics]?
public var memoryEvents: MemoryEventStatistics?
public init(
id: String,
process: ProcessStatistics? = nil,
memory: MemoryStatistics? = nil,
cpu: CPUStatistics? = nil,
blockIO: BlockIOStatistics? = nil,
networks: [NetworkStatistics]? = nil,
memoryEvents: MemoryEventStatistics? = nil
) {
self.id = id
self.process = process
self.memory = memory
self.cpu = cpu
self.blockIO = blockIO
self.networks = networks
self.memoryEvents = memoryEvents
}
/// Process statistics for a container.
public struct ProcessStatistics: Sendable {
public var current: UInt64
public var limit: UInt64
public init(current: UInt64, limit: UInt64) {
self.current = current
self.limit = limit
}
}
/// Memory statistics for a container.
public struct MemoryStatistics: Sendable {
public var usageBytes: UInt64
public var limitBytes: UInt64
public var swapUsageBytes: UInt64
public var swapLimitBytes: UInt64
public var cacheBytes: UInt64
public var kernelStackBytes: UInt64
public var slabBytes: UInt64
public var pageFaults: UInt64
public var majorPageFaults: UInt64
public var inactiveFile: UInt64
public var anon: UInt64
public init(
usageBytes: UInt64,
limitBytes: UInt64,
swapUsageBytes: UInt64,
swapLimitBytes: UInt64,
cacheBytes: UInt64,
kernelStackBytes: UInt64,
slabBytes: UInt64,
pageFaults: UInt64,
majorPageFaults: UInt64,
inactiveFile: UInt64,
anon: UInt64
) {
self.usageBytes = usageBytes
self.limitBytes = limitBytes
self.swapUsageBytes = swapUsageBytes
self.swapLimitBytes = swapLimitBytes
self.cacheBytes = cacheBytes
self.kernelStackBytes = kernelStackBytes
self.slabBytes = slabBytes
self.pageFaults = pageFaults
self.majorPageFaults = majorPageFaults
self.inactiveFile = inactiveFile
self.anon = anon
}
}
/// CPU statistics for a container.
public struct CPUStatistics: Sendable {
public var usageUsec: UInt64
public var userUsec: UInt64
public var systemUsec: UInt64
public var throttlingPeriods: UInt64
public var throttledPeriods: UInt64
public var throttledTimeUsec: UInt64
public init(
usageUsec: UInt64,
userUsec: UInt64,
systemUsec: UInt64,
throttlingPeriods: UInt64,
throttledPeriods: UInt64,
throttledTimeUsec: UInt64
) {
self.usageUsec = usageUsec
self.userUsec = userUsec
self.systemUsec = systemUsec
self.throttlingPeriods = throttlingPeriods
self.throttledPeriods = throttledPeriods
self.throttledTimeUsec = throttledTimeUsec
}
}
/// Block I/O statistics for a container.
public struct BlockIOStatistics: Sendable {
public var devices: [BlockIODevice]
public init(devices: [BlockIODevice]) {
self.devices = devices
}
}
/// Block I/O statistics for a specific device.
public struct BlockIODevice: Sendable {
public var major: UInt64
public var minor: UInt64
public var readBytes: UInt64
public var writeBytes: UInt64
public var readOperations: UInt64
public var writeOperations: UInt64
public init(
major: UInt64,
minor: UInt64,
readBytes: UInt64,
writeBytes: UInt64,
readOperations: UInt64,
writeOperations: UInt64
) {
self.major = major
self.minor = minor
self.readBytes = readBytes
self.writeBytes = writeBytes
self.readOperations = readOperations
self.writeOperations = writeOperations
}
}
/// Statistics for a network interface.
public struct NetworkStatistics: Sendable {
public var interface: String
public var receivedPackets: UInt64
public var transmittedPackets: UInt64
public var receivedBytes: UInt64
public var transmittedBytes: UInt64
public var receivedErrors: UInt64
public var transmittedErrors: UInt64
public init(
interface: String,
receivedPackets: UInt64,
transmittedPackets: UInt64,
receivedBytes: UInt64,
transmittedBytes: UInt64,
receivedErrors: UInt64,
transmittedErrors: UInt64
) {
self.interface = interface
self.receivedPackets = receivedPackets
self.transmittedPackets = transmittedPackets
self.receivedBytes = receivedBytes
self.transmittedBytes = transmittedBytes
self.receivedErrors = receivedErrors
self.transmittedErrors = transmittedErrors
}
}
/// Memory event counters from cgroup2's memory.events file.
public struct MemoryEventStatistics: Sendable {
/// Number of times the cgroup was reclaimed due to low memory.
public var low: UInt64
/// Number of times the cgroup exceeded its high memory limit.
public var high: UInt64
/// Number of times the cgroup hit its max memory limit.
public var max: UInt64
/// Number of times the cgroup triggered OOM.
public var oom: UInt64
/// Number of processes killed by OOM killer.
public var oomKill: UInt64
public init(low: UInt64, high: UInt64, max: UInt64, oom: UInt64, oomKill: UInt64) {
self.low = low
self.high = high
self.max = max
self.oom = oom
self.oomKill = oomKill
}
}
}
/// Categories of statistics that can be requested.
public struct StatCategory: OptionSet, Sendable {
public let rawValue: Int
public init(rawValue: Int) {
self.rawValue = rawValue
}
/// Process statistics (pids.current, pids.max).
public static let process = StatCategory(rawValue: 1 << 0)
/// Memory usage statistics.
public static let memory = StatCategory(rawValue: 1 << 1)
/// CPU usage statistics.
public static let cpu = StatCategory(rawValue: 1 << 2)
/// Block I/O statistics.
public static let blockIO = StatCategory(rawValue: 1 << 3)
/// Network interface statistics.
public static let network = StatCategory(rawValue: 1 << 4)
/// Memory event counters (OOM kills, pressure events, etc.).
public static let memoryEvents = StatCategory(rawValue: 1 << 5)
/// All available statistics categories.
public static let all: StatCategory = [.process, .memory, .cpu, .blockIO, .network, .memoryEvents]
}
================================================
FILE: Sources/Containerization/DNSConfiguration.swift
================================================
//===----------------------------------------------------------------------===//
// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
/// DNS configuration for a container. The values will be used to
/// construct /etc/resolv.conf for a given container.
public struct DNS: Sendable {
/// The set of default nameservers to use if none are provided
/// in the constructor.
public static let defaultNameservers = ["1.1.1.1"]
/// The nameservers a container should use.
public var nameservers: [String]
/// The DNS domain to use.
public var domain: String?
/// The DNS search domains to use.
public var searchDomains: [String]
/// The DNS options to use.
public var options: [String]
public init(
nameservers: [String] = defaultNameservers,
domain: String? = nil,
searchDomains: [String] = [],
options: [String] = []
) {
self.nameservers = nameservers
self.domain = domain
self.searchDomains = searchDomains
self.options = options
}
}
extension DNS {
public var resolvConf: String {
var text = ""
if !nameservers.isEmpty {
text += nameservers.map { "nameserver \($0)" }.joined(separator: "\n") + "\n"
}
if let domain {
text += "domain \(domain)\n"
}
if !searchDomains.isEmpty {
text += "search \(searchDomains.joined(separator: " "))\n"
}
if !options.isEmpty {
text += "options \(options.joined(separator: " "))\n"
}
return text
}
}
================================================
FILE: Sources/Containerization/ExitStatus.swift
================================================
//===----------------------------------------------------------------------===//
// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
import Foundation
/// ExitStatus contains the exit code for a given container process,
/// as well as the timestamp at which it exited.
public struct ExitStatus: Sendable {
/// The exit code for the process.
public var exitCode: Int32
/// The timestamp when the process exited.
public var exitedAt: Date
public init(exitCode: Int32) {
self.exitCode = exitCode
self.exitedAt = .now
}
public init(exitCode: Int32, exitedAt: Date) {
self.exitCode = exitCode
self.exitedAt = exitedAt
}
}
================================================
FILE: Sources/Containerization/FileMount.swift
================================================
//===----------------------------------------------------------------------===//
// Copyright © 2026 Apple Inc. and the Containerization project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
#if os(macOS)
import ContainerizationError
import ContainerizationOCI
import Foundation
/// Manages single-file mounts by transforming them into virtiofs directory shares
/// plus bind mounts.
///
/// Since virtiofs only supports sharing directories, mounting a single file without
/// exposing the other potential files in that directory needs a little bit of a "hack".
/// The one we've landed on is:
///
/// 1. Creating a temporary directory containing a hardlink to the file
/// 2. Sharing that directory via virtiofs to a holding location in the guest
/// 3. Bind mounting the specific file from the holding location to the final destination
///
/// This type handles all three steps transparently.
struct FileMountContext: Sendable {
/// Metadata for a single prepared file mount.
struct PreparedMount: Sendable {
/// Original file path on host
let hostFilePath: String
/// Where the user wants the file in the container
let containerDestination: String
/// Just the filename
let filename: String
/// Temp directory containing the hardlinked file
let tempDirectory: URL
/// The virtiofs tag (hash of temp dir path). Used to find the AttachedFilesystem
let tag: String
/// Mount options from the original mount
let options: [String]
/// Where we mounted the share in the guest (set after mountHoldingDirectories)
var guestHoldingPath: String?
}
/// Prepared file mounts for this context
var preparedMounts: [PreparedMount]
/// The transformed mounts to pass to the VM (files replaced with directory shares)
private(set) var transformedMounts: [Mount]
private init() {
self.preparedMounts = []
self.transformedMounts = []
}
/// Returns true if there are any file mounts that need handling.
var hasFileMounts: Bool {
!preparedMounts.isEmpty
}
/// Returns the set of virtiofs tags for file mount holding directories.
/// These should be filtered out from OCI spec mounts since we mount them
/// separately under /run.
var holdingDirectoryTags: Set<String> {
Set(preparedMounts.map { $0.tag })
}
}
extension FileMountContext {
/// Prepare mounts for a container, detecting file mounts and transforming them.
///
/// This method stats each virtiofs mount source. If it's a regular file rather than
/// a directory, it creates a temporary directory with a hardlink to the file and
/// substitutes a directory share for the original mount.
///
/// - Parameter mounts: The original mounts from the container config
/// - Returns: A FileMountContext containing transformed mounts and tracking info
static func prepare(mounts: [Mount]) throws -> FileMountContext {
var context = FileMountContext()
var transformed: [Mount] = []
for mount in mounts {
// Only virtiofs mounts can be files
guard case .virtiofs(let runtimeOpts) = mount.runtimeOptions else {
transformed.append(mount)
continue
}
// Stat the source to see if it's a file
let fm = FileManager.default
var isDirectory: ObjCBool = false
guard fm.fileExists(atPath: mount.source, isDirectory: &isDirectory) else {
// Doesn't exist. Let the normal flow handle the error
transformed.append(mount)
continue
}
if isDirectory.boolValue {
// It's a directory, pass through unchanged
transformed.append(mount)
continue
}
// It's a file, so prepare it.
let prepared = try context.prepareFileMount(mount: mount, runtimeOptions: runtimeOpts)
// Create a regular directory share for the temp directory.
// The destination here is unused. We'll mount it ourselves to a location under /run.
let directoryShare = Mount.share(
source: prepared.tempDirectory.path,
destination: "/.file-mount-holding",
options: mount.options.filter { $0 != "bind" },
runtimeOptions: runtimeOpts
)
transformed.append(directoryShare)
}
context.transformedMounts = transformed
return context
}
private mutating func prepareFileMount(
mount: Mount,
runtimeOptions: [String]
) throws -> PreparedMount {
let resolvedSource = URL(fileURLWithPath: mount.source).resolvingSymlinksInPath()
let sourceURL = URL(fileURLWithPath: mount.source)
let filename = sourceURL.lastPathComponent
let tempDir = FileManager.default.temporaryDirectory
.appendingPathComponent("containerization-file-mounts")
.appendingPathComponent(UUID().uuidString)
try FileManager.default.createDirectory(
at: tempDir,
withIntermediateDirectories: true
)
// Hardlink the file (falls back to copy if cross-filesystem)
let destURL = tempDir.appendingPathComponent(filename)
do {
try FileManager.default.linkItem(at: resolvedSource, to: destURL)
} catch {
// Hardlink failed. Fall back to copy
try FileManager.default.copyItem(at: resolvedSource, to: destURL)
}
let tag = try hashMountSource(source: tempDir.path)
let prepared = PreparedMount(
hostFilePath: mount.source,
containerDestination: mount.destination,
filename: filename,
tempDirectory: tempDir,
tag: tag,
options: mount.options,
guestHoldingPath: nil
)
preparedMounts.append(prepared)
return prepared
}
}
extension FileMountContext {
/// Mount the holding directories in the guest for all file mounts.
/// - Parameters:
/// - vmMounts: The AttachedFilesystem array from the VM for this container
/// - agent: The VM agent for RPCs
mutating func mountHoldingDirectories(
vmMounts: [AttachedFilesystem],
agent: any VirtualMachineAgent
) async throws {
for i in preparedMounts.indices {
let prepared = preparedMounts[i]
// Find the attached filesystem by matching the virtiofs tag
guard
let attached = vmMounts.first(where: {
$0.type == "virtiofs" && $0.source == prepared.tag
})
else {
throw ContainerizationError(
.notFound,
message: "could not find attached filesystem for file mount \(prepared.hostFilePath)"
)
}
let guestPath = "/run/file-mounts/\(prepared.tag)"
try await agent.mkdir(path: guestPath, all: true, perms: 0o755)
try await agent.mount(
ContainerizationOCI.Mount(
type: "virtiofs",
source: attached.source,
destination: guestPath,
options: []
))
preparedMounts[i].guestHoldingPath = guestPath
}
}
}
extension FileMountContext {
/// Get the bind mounts to append to the OCI spec.
func ociBindMounts() -> [ContainerizationOCI.Mount] {
preparedMounts.compactMap { prepared in
guard let guestPath = prepared.guestHoldingPath else {
return nil
}
return ContainerizationOCI.Mount(
type: "none",
source: "\(guestPath)/\(prepared.filename)",
destination: prepared.containerDestination,
options: ["bind"] + prepared.options
)
}
}
}
extension FileMountContext {
/// Clean up temp directories.
func cleanUp() {
let fm = FileManager.default
for prepared in preparedMounts {
try? fm.removeItem(at: prepared.tempDirectory)
}
}
}
#endif
================================================
FILE: Sources/Containerization/Hash.swift
================================================
//===----------------------------------------------------------------------===//
// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
#if os(macOS)
import Crypto
import ContainerizationError
import Foundation
public func hashMountSource(source: String) throws -> String {
// Resolve symlinks so different paths to the same directory get the same hash.
let resolvedSource = URL(fileURLWithPath: source).resolvingSymlinksInPath().path
guard let data = resolvedSource.data(using: .utf8) else {
throw ContainerizationError(.invalidArgument, message: "\(source) could not be converted to Data")
}
return String(SHA256.hash(data: data).encoded.prefix(36))
}
#endif
================================================
FILE: Sources/Containerization/HostsConfiguration.swift
================================================
//===----------------------------------------------------------------------===//
// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
/// Static table lookups for a container. The values will be used to
/// construct /etc/hosts for a given container.
public struct Hosts: Sendable {
/// Represents one entry in an /etc/hosts file.
public struct Entry: Sendable {
/// The IPV4 or IPV6 address in String form.
public var ipAddress: String
/// The hostname(s) for the entry.
public var hostnames: [String]
/// An optional comment to be placed to the right side of the entry.
public var comment: String?
public init(ipAddress: String, hostnames: [String], comment: String? = nil) {
self.comment = comment
self.hostnames = hostnames
self.ipAddress = ipAddress
}
/// The information in the structure rendered to a String representation
/// that matches the format /etc/hosts expects.
public var rendered: String {
var line = ipAddress
if !hostnames.isEmpty {
line += " " + hostnames.joined(separator: " ")
}
if let comment {
line += " # \(comment) "
}
return line
}
public static func localHostIPV4(comment: String? = nil) -> Self {
Self(
ipAddress: "127.0.0.1",
hostnames: ["localhost"],
comment: comment
)
}
public static func localHostIPV6(comment: String? = nil) -> Self {
Self(
ipAddress: "::1",
hostnames: ["localhost", "ip6-localhost", "ip6-loopback"],
comment: comment
)
}
public static func ipv6LocalNet(comment: String? = nil) -> Self {
Self(
ipAddress: "fe00::",
hostnames: ["ip6-localnet"],
comment: comment
)
}
public static func ipv6MulticastPrefix(comment: String? = nil) -> Self {
Self(
ipAddress: "ff00::",
hostnames: ["ip6-mcastprefix"],
comment: comment
)
}
public static func ipv6AllNodes(comment: String? = nil) -> Self {
Self(
ipAddress: "ff02::1",
hostnames: ["ip6-allnodes"],
comment: comment
)
}
public static func ipv6AllRouters(comment: String? = nil) -> Self {
Self(
ipAddress: "ff02::2",
hostnames: ["ip6-allrouters"],
comment: comment
)
}
}
/// The entries to be written to /etc/hosts.
public var entries: [Entry]
/// A comment to render at the top of the file.
public var comment: String?
public init(
entries: [Entry],
comment: String? = nil
) {
self.entries = entries
self.comment = comment
}
}
extension Hosts {
/// A default entry that can be used for convenience. It contains a IPV4
/// and IPV6 localhost entry, as well as ipv6 localnet, ipv6 mcastprefix,
/// ipv6 allnodes, and ipv6 allrouters.
public static let `default` = Hosts(entries: [
Entry.localHostIPV4(),
Entry.localHostIPV6(),
Entry.ipv6LocalNet(),
Entry.ipv6MulticastPrefix(),
Entry.ipv6AllNodes(),
Entry.ipv6AllRouters(),
])
/// Returns a string variant of the data that can be written to
/// /etc/hosts directly.
public var hostsFile: String {
var lines: [String] = []
if let comment {
lines.append("# \(comment)")
}
for entry in entries {
lines.append(entry.rendered)
}
return lines.joined(separator: "\n") + "\n"
}
}
================================================
FILE: Sources/Containerization/IO/ReaderStream.swift
================================================
//===----------------------------------------------------------------------===//
// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
import Foundation
/// A type that returns a stream of Data.
public protocol ReaderStream: Sendable {
func stream() -> AsyncStream<Data>
}
================================================
FILE: Sources/Containerization/IO/Terminal+ReaderStream.swift
================================================
//===----------------------------------------------------------------------===//
// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
import ContainerizationOS
import Foundation
extension Terminal: ReaderStream {
public func stream() -> AsyncStream<Data> {
.init { cont in
self.handle.readabilityHandler = { handle in
let data = handle.availableData
if data.isEmpty {
self.handle.readabilityHandler = nil
cont.finish()
return
}
cont.yield(data)
}
}
}
}
extension Terminal: Writer {}
================================================
FILE: Sources/Containerization/IO/Writer.swift
================================================
//===----------------------------------------------------------------------===//
// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
import Foundation
/// A type that writes the provided Data.
public protocol Writer: Sendable {
func write(_ data: Data) throws
func close() throws
}
================================================
FILE: Sources/Containerization/Image/Image.swift
================================================
//===----------------------------------------------------------------------===//
// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
import ContainerizationError
import ContainerizationOCI
import ContainerizationOS
import Foundation
/// Type representing an OCI container image.
public struct Image: Sendable {
private let contentStore: ContentStore
/// The description for the image that comprises of its name and a reference to its root descriptor.
public let description: Description
/// A description of the OCI image.
public struct Description: Sendable {
/// The string reference of the image.
public let reference: String
/// The descriptor identifying the image.
public let descriptor: Descriptor
/// The digest for the image.
public var digest: String { descriptor.digest }
/// The media type of the image.
public var mediaType: String { descriptor.mediaType }
public init(reference: String, descriptor: Descriptor) {
self.reference = reference
self.descriptor = descriptor
}
}
/// The descriptor for the image.
public var descriptor: Descriptor { description.descriptor }
/// The digest of the image.
public var digest: String { description.digest }
/// The media type of the image.
public var mediaType: String { description.mediaType }
/// The string reference for the image.
public var reference: String { description.reference }
public init(description: Description, contentStore: ContentStore) {
self.description = description
self.contentStore = contentStore
}
/// Returns the underlying OCI index for the image.
public func index() async throws -> Index {
guard let content: Content = try await contentStore.get(digest: digest) else {
throw ContainerizationError(.notFound, message: "content with digest \(digest)")
}
return try content.decode()
}
/// Returns the manifest for the specified platform.
public func manifest(for platform: Platform) async throws -> Manifest {
let index = try await self.index()
let desc = index.manifests.first { desc in
desc.platform == platform
}
guard let desc else {
throw ContainerizationError(.unsupported, message: "platform \(platform.description)")
}
guard let content: Content = try await contentStore.get(digest: desc.digest) else {
throw ContainerizationError(.notFound, message: "content with digest \(digest)")
}
return try content.decode()
}
/// Returns the descriptor for the given platform. If it does not exist
/// will throw a ContainerizationError with the code set to .invalidArgument.
public func descriptor(for platform: Platform) async throws -> Descriptor {
let index = try await self.index()
let desc = index.manifests.first { $0.platform == platform }
guard let desc else {
throw ContainerizationError(.invalidArgument, message: "unsupported platform \(platform)")
}
return desc
}
/// Returns the OCI config for the specified platform.
public func config(for platform: Platform) async throws -> ContainerizationOCI.Image {
let manifest = try await self.manifest(for: platform)
let desc = manifest.config
guard let content: Content = try await contentStore.get(digest: desc.digest) else {
throw ContainerizationError(.notFound, message: "content with digest \(digest)")
}
return try content.decode()
}
/// Returns a list of digests to all the referenced OCI objects.
public func referencedDigests() async throws -> [String] {
var referenced: [String] = [self.digest.trimmingDigestPrefix]
let index = try await self.index()
for manifest in index.manifests {
referenced.append(manifest.digest.trimmingDigestPrefix)
guard let m: Manifest = try? await contentStore.get(digest: manifest.digest) else {
// If the requested digest does not exist or is not a manifest. Skip.
// Its safe to skip processing this digest as it wont have any child layers.
continue
}
let descs = m.layers + [m.config]
referenced.append(contentsOf: descs.map { $0.digest.trimmingDigestPrefix })
}
return referenced
}
/// Returns a reference to the content blob for the image. The specified digest must be referenced by the image in one of its layers.
public func getContent(digest: String) async throws -> Content {
guard try await self.referencedDigests().contains(digest.trimmingDigestPrefix) else {
throw ContainerizationError(.internalError, message: "image \(self.reference) does not reference digest \(digest)")
}
guard let content: Content = try await contentStore.get(digest: digest) else {
throw ContainerizationError(.notFound, message: "content with digest \(digest)")
}
return content
}
}
================================================
FILE: Sources/Containerization/Image/ImageStore/ImageStore+Export.swift
================================================
//===----------------------------------------------------------------------===//
// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
//
import ContainerizationError
import ContainerizationExtras
import ContainerizationIO
import ContainerizationOCI
import Crypto
import Foundation
extension ImageStore {
public struct ExportOperation: Sendable {
let name: String
let tag: String
let contentStore: ContentStore
let client: ContentClient
let progress: ProgressHandler?
public init(name: String, tag: String, contentStore: ContentStore, client: ContentClient, progress: ProgressHandler? = nil) {
self.contentStore = contentStore
self.client = client
self.progress = progress
self.name = name
self.tag = tag
}
@discardableResult
public func export(index: Descriptor, platforms: (Platform) -> Bool) async throws -> Descriptor {
var pushQueue: [[Descriptor]] = []
var current: [Descriptor] = [index]
while !current.isEmpty {
let children = try await self.getChildren(descs: current)
let matches = try filterPlatforms(matcher: platforms, children).uniqued { $0.digest }
pushQueue.append(matches)
current = matches
}
let localIndexData = try await self.createIndex(from: index, matching: platforms)
await updatePushProgress(pushQueue: pushQueue, localIndexData: localIndexData)
// We need to work bottom up when pushing an image.
// First, the tar blobs / config layers, then, the manifests and so on...
// When processing a given "level", the requests maybe made in parallel.
// We need to ensure that the child level has been uploaded fully
// before uploading the parent level.
try await withThrowingTaskGroup(of: Void.self) { group in
for layerGroup in pushQueue.reversed() {
for chunk in layerGroup.chunks(ofCount: 8) {
for desc in chunk {
guard let content = try await self.contentStore.get(digest: desc.digest) else {
throw ContainerizationError(.notFound, message: "content with digest \(desc.digest)")
}
group.addTask {
let readStream = try ReadStream(url: content.path)
try await self.pushContent(descriptor: desc, stream: readStream)
}
}
try await group.waitForAll()
}
}
}
// Lastly, we need to construct and push a new index, since we may
// have pushed content only for specific platforms.
let digest = SHA256.hash(data: localIndexData)
let descriptor = Descriptor(
mediaType: MediaTypes.index,
digest: digest.digestString,
size: Int64(localIndexData.count))
let stream = ReadStream(data: localIndexData)
try await self.pushContent(descriptor: descriptor, stream: stream)
return descriptor
}
private func updatePushProgress(pushQueue: [[Descriptor]], localIndexData: Data) async {
for layerGroup in pushQueue {
for desc in layerGroup {
await progress?([
.addTotalSize(desc.size),
.addTotalItems(1),
])
}
}
await progress?([
.addTotalSize(Int64(localIndexData.count)),
.addTotalItems(1),
])
}
private func createIndex(from index: Descriptor, matching: (Platform) -> Bool) async throws -> Data {
guard let content = try await self.contentStore.get(digest: index.digest) else {
throw ContainerizationError(.notFound, message: "content with digest \(index.digest)")
}
var idx: Index = try content.decode()
let manifests = idx.manifests
var matchedManifests: [Descriptor] = []
var skippedPlatforms = false
for manifest in manifests {
guard let p = manifest.platform else {
continue
}
if matching(p) {
matchedManifests.append(manifest)
} else {
skippedPlatforms = true
}
}
if !skippedPlatforms {
return try content.data()
}
idx.manifests = matchedManifests
return try JSONEncoder().encode(idx)
}
private func pushContent(descriptor: Descriptor, stream: ReadStream) async throws {
do {
let generator = {
try stream.reset()
return stream.stream
}
try await client.push(name: name, ref: tag, descriptor: descriptor, streamGenerator: generator, progress: progress)
await progress?([
.addSize(descriptor.size),
.addItems(1),
])
} catch let err as ContainerizationError {
guard err.code != .exists else {
// We reported the total items and size and have to account for them in existing content.
await progress?([
.addSize(descriptor.size),
.addItems(1),
])
return
}
throw err
}
}
private func getChildren(descs: [Descriptor]) async throws -> [Descriptor] {
var out: [Descriptor] = []
for desc in descs {
let mediaType = desc.mediaType
guard let content = try await self.contentStore.get(digest: desc.digest) else {
throw ContainerizationError(.notFound, message: "content with digest \(desc.digest)")
}
switch mediaType {
case MediaTypes.index, MediaTypes.dockerManifestList:
let index: Index = try content.decode()
out.append(contentsOf: index.manifests)
case MediaTypes.imageManifest, MediaTypes.dockerManifest:
let manifest: Manifest = try content.decode()
out.append(manifest.config)
out.append(contentsOf: manifest.layers)
default:
continue
}
}
return out
}
}
}
================================================
FILE: Sources/Containerization/Image/ImageStore/ImageStore+Import.swift
================================================
//===----------------------------------------------------------------------===//
// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
//
import ContainerizationError
import ContainerizationExtras
import ContainerizationOCI
import Foundation
extension ImageStore {
public struct ImportOperation: Sendable {
static let decoder = JSONDecoder()
let client: ContentClient
let ingestDir: URL
let contentStore: ContentStore
let progress: ProgressHandler?
let name: String
let maxConcurrentDownloads: Int
public init(name: String, contentStore: ContentStore, client: ContentClient, ingestDir: URL, progress: ProgressHandler? = nil, maxConcurrentDownloads: Int = 3) {
self.client = client
self.ingestDir = ingestDir
self.contentStore = contentStore
self.progress = progress
self.name = name
self.maxConcurrentDownloads = maxConcurrentDownloads
}
/// Pull the required image layers for the provided descriptor and platform(s) into the given directory using the provided client. Returns a descriptor to the Index manifest.
public func `import`(root: Descriptor, matcher: (ContainerizationOCI.Platform) -> Bool) async throws -> Descriptor {
var toProcess = [root]
while !toProcess.isEmpty {
// Count the total number of blobs and their size
if let progress {
var size: Int64 = 0
for desc in toProcess {
size += desc.size
}
await progress([
.addTotalSize(size),
.addTotalItems(toProcess.count),
])
}
try await self.fetchAll(toProcess)
let children = try await self.walk(toProcess)
let filtered = try filterPlatforms(matcher: matcher, children)
toProcess = filtered.uniqued { $0.digest }
}
guard root.mediaType != MediaTypes.dockerManifestList && root.mediaType != MediaTypes.index else {
return root
}
// Create an index for the root descriptor and write it to the content store
let index = try await self.createIndex(for: root)
// In cases where the root descriptor pointed to `MediaTypes.imageManifest`
// Or `MediaTypes.dockerManifest`, it is required that we check the supported platform
// matches the platforms we were asked to pull. This can be done only after we created
// the Index.
let supportedPlatforms = index.manifests.compactMap { $0.platform }
guard supportedPlatforms.allSatisfy(matcher) else {
throw ContainerizationError(.unsupported, message: "image \(root.digest) does not support required platforms")
}
let writer = try ContentWriter(for: self.ingestDir)
let result = try writer.create(from: index)
return Descriptor(
mediaType: MediaTypes.index,
digest: result.digest.digestString,
size: Int64(result.size))
}
private func getManifestContent<T: Sendable & Codable>(descriptor: Descriptor) async throws -> T {
do {
if let content = try await self.contentStore.get(digest: descriptor.digest.trimmingDigestPrefix) {
return try content.decode()
}
if let content = try? LocalContent(path: ingestDir.appending(path: descriptor.digest.trimmingDigestPrefix)) {
return try content.decode()
}
return try await self.client.fetch(name: name, descriptor: descriptor)
} catch {
throw ContainerizationError(.internalError, message: "cannot fetch content with digest \(descriptor.digest)", cause: error)
}
}
private func walk(_ descriptors: [Descriptor]) async throws -> [Descriptor] {
var out: [Descriptor] = []
for desc in descriptors {
let mediaType = desc.mediaType
switch mediaType {
case MediaTypes.index, MediaTypes.dockerManifestList:
let index: Index = try await self.getManifestContent(descriptor: desc)
out.append(contentsOf: index.manifests)
case MediaTypes.imageManifest, MediaTypes.dockerManifest:
let manifest: Manifest = try await self.getManifestContent(descriptor: desc)
out.append(manifest.config)
out.append(contentsOf: manifest.layers)
default:
// TODO: Explicitly handle other content types
continue
}
}
return out
}
private func fetchAll(_ descriptors: [Descriptor]) async throws {
try await withThrowingTaskGroup(of: Void.self) { group in
var iterator = descriptors.makeIterator()
// Start initial batch of concurrent downloads based on maxConcurrentDownloads
for _ in 0..<self.maxConcurrentDownloads {
if let desc = iterator.next() {
group.addTask {
try await self.fetch(desc)
}
}
}
// As tasks complete, add new ones to maintain concurrency
for try await _ in group {
if let desc = iterator.next() {
group.addTask {
try await self.fetch(desc)
}
}
}
}
}
private func fetch(_ descriptor: Descriptor) async throws {
if let found = try await self.contentStore.get(digest: descriptor.digest) {
try FileManager.default.copyItem(at: found.path, to: ingestDir.appendingPathComponent(descriptor.digest.trimmingDigestPrefix))
await progress?([
// Count the size of the blob
.addSize(descriptor.size),
// Count the number of blobs
.addItems(1),
])
return
}
if descriptor.size > 1.mib() {
try await self.fetchBlob(descriptor)
} else {
try await self.fetchData(descriptor)
}
// Count the number of blobs
await progress?([
.addItems(1)
])
}
private func fetchBlob(_ descriptor: Descriptor) async throws {
let id = UUID().uuidString
let fm = FileManager.default
let tempFile = ingestDir.appendingPathComponent(id)
let (_, digest) = try await client.fetchBlob(name: name, descriptor: descriptor, into: tempFile, progress: progress)
guard digest.digestString == descriptor.digest else {
throw ContainerizationError(.internalError, message: "digest mismatch expected \(descriptor.digest), got \(digest.digestString)")
}
do {
try fm.moveItem(at: tempFile, to: ingestDir.appendingPathComponent(digest.encoded))
} catch let err as NSError {
guard err.code == NSFileWriteFileExistsError else {
throw err
}
try fm.removeItem(at: tempFile)
}
}
@discardableResult
private func fetchData(_ descriptor: Descriptor) async throws -> Data {
let data = try await client.fetchData(name: name, descriptor: descriptor)
let writer = try ContentWriter(for: ingestDir)
let result = try writer.write(data)
if let progress {
let size = Int64(result.size)
await progress([
.addSize(size)
])
}
guard result.digest.digestString == descriptor.digest else {
throw ContainerizationError(.internalError, message: "digest mismatch expected \(descriptor.digest), got \(result.digest.digestString)")
}
return data
}
private func createIndex(for root: Descriptor) async throws -> Index {
switch root.mediaType {
case MediaTypes.index, MediaTypes.dockerManifestList:
return try await self.getManifestContent(descriptor: root)
case MediaTypes.imageManifest, MediaTypes.dockerManifest:
let supportedPlatforms = try await getSupportedPlatforms(for: root)
guard supportedPlatforms.count == 1 else {
throw ContainerizationError(
.internalError,
message:
"descriptor \(root.mediaType) with digest \(root.digest) does not list any supported platform or supports more than one platform, supported platforms: \(supportedPlatforms)"
)
}
let platform = supportedPlatforms.first!
var root = root
root.platform = platform
let index = ContainerizationOCI.Index(
schemaVersion: 2, manifests: [root],
annotations: [
// indicate that this is a synthesized index which is not directly user facing
AnnotationKeys.containerizationIndexIndirect: "true"
])
return index
default:
throw ContainerizationError(.internalError, message: "failed to create index for descriptor \(root.digest), media type \(root.mediaType)")
}
}
private func getSupportedPlatforms(for root: Descriptor) async throws -> [ContainerizationOCI.Platform] {
var supportedPlatforms: [ContainerizationOCI.Platform] = []
var toProcess = [root]
while !toProcess.isEmpty {
let children = try await self.walk(toProcess)
for child in children {
if let p = child.platform {
supportedPlatforms.append(p)
continue
}
switch child.mediaType {
case MediaTypes.imageConfig, MediaTypes.dockerImageConfig:
let config: ContainerizationOCI.Image = try await self.getManifestContent(descriptor: child)
let p = ContainerizationOCI.Platform(
arch: config.architecture, os: config.os, osFeatures: config.osFeatures, variant: config.variant
)
supportedPlatforms.append(p)
default:
continue
}
}
toProcess = children
}
return supportedPlatforms
}
}
}
================================================
FILE: Sources/Containerization/Image/ImageStore/ImageStore+OCILayout.swift
================================================
//===----------------------------------------------------------------------===//
// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
import ContainerizationError
import ContainerizationExtras
import ContainerizationOCI
import Foundation
extension ImageStore {
/// Exports the specified images and their associated layers to an OCI Image Layout directory.
/// This function saves the images identified by the `references` array, including their
/// manifests and layer blobs, into a directory structure compliant with the OCI Image Layout specification at the given `out` URL.
///
/// - Parameters:
/// - references: A list image references that exists in the `ImageStore` that are to be saved in the OCI Image Layout format.
/// - out: A URL to a directory on disk at which the OCI Image Layout structure will be created.
/// - platform: An optional parameter to indicate the platform to be saved for the images.
/// Defaults to `nil` signifying that layers for all supported platforms by the images will be saved.
///
public func save(references: [String], out: URL, platform: Platform? = nil) async throws {
let matcher = createPlatformMatcher(for: platform)
let fileManager = FileManager.default
let tempDir = fileManager.uniqueTemporaryDirectory()
defer {
try? fileManager.removeItem(at: tempDir)
}
var toSave: [Image] = []
for reference in references {
let image = try await self.get(reference: reference)
let allowedMediaTypes = [MediaTypes.dockerManifestList, MediaTypes.index]
guard allowedMediaTypes.contains(image.mediaType) else {
throw ContainerizationError(.internalError, message: "cannot save image \(image.reference) with Index media type \(image.mediaType)")
}
toSave.append(image)
}
let client = try LocalOCILayoutClient(root: out)
var saved: [Descriptor] = []
for image in toSave {
let ref = try Reference.parse(image.reference)
let name = ref.path
guard let tag = ref.tag ?? ref.digest else {
throw ContainerizationError(.invalidArgument, message: "invalid tag/digest for image reference \(image.reference)")
}
let operation = ExportOperation(name: name, tag: tag, contentStore: self.contentStore, client: client, progress: nil)
var descriptor = try await operation.export(index: image.descriptor, platforms: matcher)
client.setImageReferenceAnnotation(descriptor: &descriptor, reference: image.reference)
saved.append(descriptor)
}
try client.createOCILayoutStructure(directory: out, manifests: saved)
}
/// Imports one or more images and their associated layers from an OCI Image Layout directory.
///
/// - Parameters:
/// - directory: A URL to a directory on disk at that follows the OCI Image Layout structure.
/// - progress: An optional handler over which progress update events about the load operation can be received.
/// - Returns: The list of images that were loaded into the `ImageStore`.
///
public func load(from directory: URL, progress: ProgressHandler? = nil) async throws -> [Image] {
let client = try LocalOCILayoutClient(root: directory)
let index = try client.loadIndexFromOCILayout(directory: directory)
let matcher = createPlatformMatcher(for: nil)
var loaded: [Image.Description] = []
let (id, tempDir) = try await self.contentStore.newIngestSession()
do {
for descriptor in index.manifests {
let reference = client.getImageReferencefromDescriptor(descriptor: descriptor)
let ref = try Reference.parse(reference)
let name = ref.path
let operation = ImportOperation(name: name, contentStore: self.contentStore, client: client, ingestDir: tempDir, progress: progress)
let indexDesc = try await operation.import(root: descriptor, matcher: matcher)
loaded.append(Image.Description(reference: reference, descriptor: indexDesc))
}
let loadedImages = loaded
let importedImages = try await self.lock.withLock { lock in
var images: [Image] = []
try await self.contentStore.completeIngestSession(id)
for description in loadedImages {
let img = try await self._create(description: description, lock: lock)
images.append(img)
}
return images
}
guard importedImages.count > 0 else {
throw ContainerizationError(.internalError, message: "failed to import image")
}
return importedImages
} catch {
try? await self.contentStore.cancelIngestSession(id)
throw error
}
}
}
================================================
FILE: Sources/Containerization/Image/ImageStore/ImageStore+ReferenceManager.swift
================================================
//===----------------------------------------------------------------------===//
// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
import ContainerizationError
import ContainerizationOCI
import Foundation
extension ImageStore {
/// A ReferenceManager handles the mappings between an image's
/// reference and the underlying descriptor inside of a content store.
internal actor ReferenceManager {
private let path: URL
private typealias State = [String: Descriptor]
private var images: State
public init(path: URL) throws {
try FileManager.default.createDirectory(at: path, withIntermediateDirectories: true)
self.path = path
self.images = [:]
}
private func load() throws -> State {
let statePath = self.path.appendingPathComponent("state.json")
guard FileManager.default.fileExists(atPath: statePath.absolutePath()) else {
return [:]
}
do {
let data = try Data(contentsOf: statePath)
return try JSONDecoder().decode(State.self, from: data)
} catch {
throw ContainerizationError(.internalError, message: "failed to load image state \(error.localizedDescription)")
}
}
private func save(_ state: State) throws {
let statePath = self.path.appendingPathComponent("state.json")
try JSONEncoder().encode(state).write(to: statePath)
}
public func delete(reference: String) throws {
var state = try self.load()
state.removeValue(forKey: reference)
try self.save(state)
}
public func delete(image: Image.Description) throws {
try self.delete(reference: image.reference)
}
public func create(description: Image.Description) throws {
var state = try self.load()
state[description.reference] = description.descriptor
try self.save(state)
}
public func list() throws -> [Image.Description] {
let state = try self.load()
return state.map { key, val in
let description = Image.Description(reference: key, descriptor: val)
return description
}
}
public func get(reference: String) throws -> Image.Description {
let images = try self.list()
let hit = images.first(where: { image in
image.reference == reference
})
guard let hit else {
throw ContainerizationError(.notFound, message: "image \(reference) not found")
}
return hit
}
}
}
================================================
FILE: Sources/Containerization/Image/ImageStore/ImageStore.swift
================================================
//===----------------------------------------------------------------------===//
// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
import ContainerizationError
import ContainerizationExtras
import ContainerizationOCI
import Foundation
/// An ImageStore handles the mappings between an image's
/// reference and the underlying descriptor inside of a content store.
public actor ImageStore: Sendable {
/// The ImageStore path it was created with.
public nonisolated let path: URL
private let referenceManager: ReferenceManager
internal let contentStore: ContentStore
internal let lock: AsyncLock = AsyncLock()
public init(path: URL, contentStore: ContentStore? = nil) throws {
try FileManager.default.createDirectory(at: path, withIntermediateDirectories: true)
if let contentStore {
self.contentStore = contentStore
} else {
self.contentStore = try LocalContentStore(path: path.appendingPathComponent("content"))
}
self.path = path
self.referenceManager = try ReferenceManager(path: path)
}
/// Return the default image store for the current user.
public static let `default`: ImageStore = {
do {
let root = try defaultRoot()
return try ImageStore(path: root)
} catch {
fatalError("unable to initialize default ImageStore \(error)")
}
}()
private static func defaultRoot() throws -> URL {
let root = FileManager.default.urls(
for: .applicationSupportDirectory,
in: .userDomainMask
).first
guard let root else {
throw ContainerizationError(.notFound, message: "unable to get Application Support directory for current user")
}
return root.appendingPathComponent("com.apple.containerization")
}
}
extension ImageStore {
/// Get an image from the `ImageStore`.
///
/// - Parameters:
/// - reference: Name of the image.
/// - pull: Pull the image if it is not found.
///
/// - Returns: A `Containerization.Image` object whose `reference` matches the given string.
/// This method throws a `ContainerizationError(code: .notFound)` if the provided reference does not exist in the `ImageStore`.
public func get(reference: String, pull: Bool = false) async throws -> Image {
do {
let desc = try await self.referenceManager.get(reference: reference)
return Image(description: desc, contentStore: self.contentStore)
} catch let error as ContainerizationError {
if error.code == .notFound && pull {
return try await self.pull(reference: reference)
}
throw error
}
}
/// Get a list of all images in the `ImageStore`.
///
/// - Returns: A `[Containerization.Image]` for all the images in the `ImageStore`.
public func list() async throws -> [Image] {
try await self.referenceManager.list().map { desc in
Image(description: desc, contentStore: self.contentStore)
}
}
/// Create a new image in the `ImageStore`.
///
/// - Parameters:
/// - description: The underlying `Image.Description` that contains information about the reference and index descriptor for the image to be created.
///
/// - Note: It is assumed that the underlying manifests and blob layers for the image already exists in the `ContentStore` that the `ImageStore` was initialized with. This method is invoked when the `pull(...)` , `load(...)` and `tag(...)` methods are used.
/// - Returns: A `Containerization.Image`
@discardableResult
public func create(description: Image.Description) async throws -> Image {
try await self.lock.withLock { ctx in
try await self._create(description: description, lock: ctx)
}
}
@discardableResult
internal func _create(description: Image.Description, lock: AsyncLock.Context) async throws -> Image {
try await self.referenceManager.create(description: description)
return Image(description: description, contentStore: self.contentStore)
}
/// Delete an image from the `ImageStore`.
///
/// - Parameters:
/// - reference: Name of the image that is to be deleted.
/// - performCleanup: Perform a garbage collection on the `ContentStore`, removing all unreferenced image layers and manifests,
public func delete(reference: String, performCleanup: Bool = false) async throws {
try await self.lock.withLock { lockCtx in
try await self.referenceManager.delete(reference: reference)
if performCleanup {
try await self._cleanUpOrphanedBlobs(lockCtx)
}
}
}
/// Clean up orphaned blobs that are no longer referenced by any image.
///
/// - Returns: Returns a tuple of `(deleted, freed)`.
/// `deleted` : A list of the names of the content items that were deleted from the `ContentStore`,
/// `freed` : The total size of the items that were deleted.
@discardableResult
public func cleanUpOrphanedBlobs() async throws -> (deleted: [String], freed: UInt64) {
try await self.lock.withLock { lockCtx in
try await self._cleanUpOrphanedBlobs(lockCtx)
}
}
/// Calculate the size of orphaned blobs without deleting them.
///
/// - Returns: The total size in bytes of blobs that are not referenced by any image.
public func calculateOrphanedBlobsSize() async throws -> UInt64 {
try await self.lock.withLock { lockCtx in
try await self._calculateOrphanedBlobsSize(lockCtx)
}
}
@discardableResult
private func _cleanUpOrphanedBlobs(_ lock: AsyncLock.Context) async throws -> (deleted: [String], freed: UInt64) {
let images = try await self.list()
var referenced: [String] = []
for image in images {
try await referenced.append(contentsOf: image.referencedDigests().uniqued())
}
let (deleted, size) = try await self.contentStore.delete(keeping: referenced)
return (deleted, size)
}
private func _calculateOrphanedBlobsSize(_ lock: AsyncLock.Context) async throws -> UInt64 {
let images = try await self.list()
var referenced: [String] = []
for image in images {
try await referenced.append(contentsOf: image.referencedDigests().uniqued())
}
// Calculate size of blobs not in the referenced list
let referencedSet = Set(referenced.map { $0.trimmingDigestPrefix })
let blobsPath = self.path.appendingPathComponent("content/blobs/sha256")
let fileManager = FileManager.default
let allBlobs = try fileManager.contentsOfDirectory(
at: blobsPath,
includingPropertiesForKeys: [.fileSizeKey],
options: [.skipsHiddenFiles]
)
var orphanedSize: UInt64 = 0
for blobURL in allBlobs {
let digest = blobURL.lastPathComponent
if !referencedSet.contains(digest) {
if let resourceValues = try? blobURL.resourceValues(forKeys: [.fileSizeKey]),
let size = resourceValues.fileSize
{
orphanedSize += UInt64(size)
}
}
}
return orphanedSize
}
/// Tag an existing image such that it can be referenced by another name.
///
/// - Parameters:
/// - existing: The reference to an image that already exists in the `ImageStore`.
/// - new: The new reference by which the image should also be referenced as.
/// - Note: The new image created in the `ImageStore` will have the same `Image.Description`
/// as that of the image with reference `existing.`
/// - Returns: A `Containerization.Image` object to the newly created image.
public func tag(existing: String, new: String) async throws -> Image {
let old = try await self.get(reference: existing)
let descriptor = old.descriptor
do {
_ = try Reference.parse(new)
} catch {
throw ContainerizationError(.invalidArgument, message: "invalid reference \(new), error: \(error)")
}
let newDescription = Image.Description(reference: new, descriptor: descriptor)
return try await self.create(description: newDescription)
}
}
extension ImageStore {
/// Pull an image and its associated manifest and blob layers from a remote registry.
///
/// - Parameters:
/// - reference: A string that references an image in a remote registry of the form `<host>[:<port>]/repository:<tag>`
/// For example: "docker.io/library/alpine:latest".
/// - platform: An optional parameter to indicate the platform to be pulled for the image.
/// Defaults to `nil` signifying that layers for all supported platforms by the image will be pulled.
/// - insecure: A boolean indicating if the connection to the remote registry should be made via plain-text http or not.
/// Defaults to false, meaning the connection to the registry will be over https.
/// - auth: An object that implements the `Authentication` protocol,
/// used to add any credentials to the HTTP requests that are made to the registry.
/// Defaults to `nil` meaning no additional credentials are added to any HTTP requests made to the registry.
/// - progress: An optional handler over which progress update events about the pull operation can be received.
///
/// - Returns: A `Containerization.Image` object to the newly pulled image.
public func pull(
reference: String, platform: Platform? = nil, insecure: Bool = false,
auth: Authentication? = nil, progress: ProgressHandler? = nil, maxConcurrentDownloads: Int = 3
) async throws -> Image {
let matcher = createPlatformMatcher(for: platform)
let client = try RegistryClient(reference: reference, insecure: insecure, auth: auth, tlsConfiguration: TLSUtils.makeEnvironmentAwareTLSConfiguration())
let ref = try Reference.parse(reference)
let name = ref.path
guard let tag = ref.tag ?? ref.digest else {
throw ContainerizationError(.invalidArgument, message: "invalid tag/digest for image reference \(reference)")
}
let rootDescriptor = try await client.resolve(name: name, tag: tag)
let (id, tempDir) = try await self.contentStore.newIngestSession()
let operation = ImportOperation(
name: name, contentStore: self.contentStore, client: client, ingestDir: tempDir, progress: progress, maxConcurrentDownloads: maxConcurrentDownloads)
do {
let index = try await operation.import(root: rootDescriptor, matcher: matcher)
return try await self.lock.withLock { lock in
try await self.contentStore.completeIngestSession(id)
let description = Image.Description(reference: reference, descriptor: index)
let image = try await self._create(description: description, lock: lock)
return image
}
} catch {
try? await self.contentStore.cancelIngestSession(id)
throw error
}
}
/// Push an image and its associated manifest and blob layers to a remote registry.
///
/// - Parameters:
/// - reference: A string that references an image in the `ImageStore`. It must be of the form `<host>[:<port>]/repository:<tag>`
/// For example: "ghcr.io/foo-bar-baz/image:v1".
/// - platform: An optional parameter to indicate the platform to be pushed for the image.
/// Defaults to `nil` signifying that layers for all supported platforms by the image will be pushed to the remote registry.
/// - insecure: A boolean indicating if the connection to the remote registry should be made via plain-text http or not.
/// Defaults to false, meaning the connection to the registry will be over https.
/// - auth: An object that implements the `Authentication` protocol,
/// used to add any credentials to the HTTP requests that are made to the registry.
/// Defaults to `nil` meaning no additional credentials are added to any HTTP requests made to the registry.
/// - progress: An optional handler over which progress update events about the push operation can be received.
///
public func push(reference: String, platform: Platform? = nil, insecure: Bool = false, auth: Authentication? = nil, progress: ProgressHandler? = nil) async throws {
let matcher = createPlatformMatcher(for: platform)
let img = try await self.get(reference: reference)
let allowedMediaTypes = [MediaTypes.dockerManifestList, MediaTypes.index]
guard allowedMediaTypes.contains(img.mediaType) else {
throw ContainerizationError(.internalError, message: "cannot push image \(reference) with Index media type \(img.mediaType)")
}
let ref = try Reference.parse(reference)
let name = ref.path
guard let tag = ref.tag ?? ref.digest else {
throw ContainerizationError(.invalidArgument, message: "invalid tag/digest for image reference \(reference)")
}
let client = try RegistryClient(reference: reference, insecure: insecure, auth: auth, tlsConfiguration: TLSUtils.makeEnvironmentAwareTLSConfiguration())
let operation = ExportOperation(name: name, tag: tag, contentStore: self.contentStore, client: client, progress: progress)
try await operation.export(index: img.descriptor, platforms: matcher)
}
}
extension ImageStore {
/// Get the image for the init block from the image store.
/// If the image does not exist locally, pull the image.
public func getInitImage(reference: String, auth: Authentication? = nil, progress: ProgressHandler? = nil) async throws -> InitImage {
do {
let image = try await self.get(reference: reference)
return InitImage(image: image)
} catch let error as ContainerizationError {
if error.code == .notFound {
let image = try await self.pull(reference: reference, auth: auth, progress: progress)
return InitImage(image: image)
}
throw error
}
}
}
================================================
FILE: Sources/Containerization/Image/InitImage.swift
================================================
//===----------------------------------------------------------------------===//
// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
import ContainerizationError
import ContainerizationOCI
import Foundation
/// Data representing the image to use as the root filesystem for a virtual machine.
/// Typically this image would contain the guest agent used to facilitate container
/// workloads, as well as any extras that may be useful to have in the guest.
public struct InitImage: Sendable {
public var name: String { image.reference }
let image: Image
public init(image: Image) {
self.image = image
}
}
extension InitImage {
/// Unpack the initial filesystem for the desired platform at a given path.
public func initBlock(at: URL, for platform: SystemPlatform) async throws -> Mount {
let unpacker = EXT4Unpacker(blockSizeInBytes: 512.mib())
gitextract_1u63lgkp/
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── 01-bug.yml
│ │ ├── 02-feature.yml
│ │ └── config.yml
│ └── workflows/
│ ├── build-test-images.yml
│ ├── containerization-build-template.yml
│ ├── containerization-build.yml
│ ├── docs-release.yaml
│ └── release.yml
├── .gitignore
├── .spi.yml
├── .swift-format
├── .swift-format-nolint
├── .swift-version
├── CONTRIBUTING.md
├── LICENSE
├── MAINTAINERS.txt
├── Makefile
├── Package.resolved
├── Package.swift
├── Protobuf.Makefile
├── README.md
├── SECURITY.md
├── Sources/
│ ├── CShim/
│ │ ├── capability.c
│ │ ├── exec_command.c
│ │ ├── include/
│ │ │ ├── capability.h
│ │ │ ├── exec_command.h
│ │ │ ├── openat2.h
│ │ │ ├── prctl.h
│ │ │ ├── socket_helpers.h
│ │ │ └── vsock.h
│ │ ├── openat2.c
│ │ ├── prctl.c
│ │ ├── socket_helpers.c
│ │ └── vsock.c
│ ├── Containerization/
│ │ ├── AttachedFilesystem.swift
│ │ ├── Container.swift
│ │ ├── ContainerManager.swift
│ │ ├── ContainerStatistics.swift
│ │ ├── DNSConfiguration.swift
│ │ ├── ExitStatus.swift
│ │ ├── FileMount.swift
│ │ ├── Hash.swift
│ │ ├── HostsConfiguration.swift
│ │ ├── IO/
│ │ │ ├── ReaderStream.swift
│ │ │ ├── Terminal+ReaderStream.swift
│ │ │ └── Writer.swift
│ │ ├── Image/
│ │ │ ├── Image.swift
│ │ │ ├── ImageStore/
│ │ │ │ ├── ImageStore+Export.swift
│ │ │ │ ├── ImageStore+Import.swift
│ │ │ │ ├── ImageStore+OCILayout.swift
│ │ │ │ ├── ImageStore+ReferenceManager.swift
│ │ │ │ └── ImageStore.swift
│ │ │ ├── InitImage.swift
│ │ │ ├── KernelImage.swift
│ │ │ └── Unpacker/
│ │ │ ├── EXT4Unpacker.swift
│ │ │ └── Unpacker.swift
│ │ ├── Interface.swift
│ │ ├── Kernel.swift
│ │ ├── LinuxContainer.swift
│ │ ├── LinuxPod.swift
│ │ ├── LinuxProcess.swift
│ │ ├── LinuxProcessConfiguration.swift
│ │ ├── Mount.swift
│ │ ├── NATInterface.swift
│ │ ├── NATNetworkInterface.swift
│ │ ├── Network.swift
│ │ ├── SandboxContext/
│ │ │ ├── SandboxContext.grpc.swift
│ │ │ ├── SandboxContext.pb.swift
│ │ │ └── SandboxContext.proto
│ │ ├── SystemPlatform.swift
│ │ ├── TimeSyncer.swift
│ │ ├── UnixSocketConfiguration.swift
│ │ ├── UnixSocketRelay.swift
│ │ ├── UnixSocketRelayManager.swift
│ │ ├── VMConfiguration.swift
│ │ ├── VZVirtualMachine+Helpers.swift
│ │ ├── VZVirtualMachineInstance.swift
│ │ ├── VZVirtualMachineManager.swift
│ │ ├── VirtualMachineAgent+Additions.swift
│ │ ├── VirtualMachineAgent.swift
│ │ ├── VirtualMachineInstance.swift
│ │ ├── VirtualMachineManager.swift
│ │ ├── Vminitd+Rosetta.swift
│ │ ├── Vminitd+SocketRelay.swift
│ │ ├── Vminitd.swift
│ │ ├── VmnetNetwork.swift
│ │ └── VsockListener.swift
│ ├── ContainerizationArchive/
│ │ ├── ArchiveError.swift
│ │ ├── ArchiveReader.swift
│ │ ├── ArchiveWriter.swift
│ │ ├── ArchiveWriterConfiguration.swift
│ │ ├── CArchive/
│ │ │ ├── COPYING
│ │ │ ├── archive_swift_bridge.c
│ │ │ └── include/
│ │ │ ├── archive.h
│ │ │ ├── archive_bridge.h
│ │ │ └── archive_entry.h
│ │ ├── TempDir.swift
│ │ └── WriteEntry.swift
│ ├── ContainerizationEXT4/
│ │ ├── Documentation.docc/
│ │ │ └── ext4.md
│ │ ├── EXT4+Extensions.swift
│ │ ├── EXT4+FileTree.swift
│ │ ├── EXT4+Formatter.swift
│ │ ├── EXT4+Ptr.swift
│ │ ├── EXT4+Reader.swift
│ │ ├── EXT4+Types.swift
│ │ ├── EXT4+Xattrs.swift
│ │ ├── EXT4.swift
│ │ ├── EXT4Reader+Export.swift
│ │ ├── EXT4Reader+IO.swift
│ │ ├── FilePath+Extensions.swift
│ │ ├── FileTimestamps.swift
│ │ ├── Formatter+Unpack.swift
│ │ ├── Integer+Extensions.swift
│ │ ├── README.md
│ │ └── UnsafeLittleEndianBytes.swift
│ ├── ContainerizationError/
│ │ └── ContainerizationError.swift
│ ├── ContainerizationExtras/
│ │ ├── AddressAllocator.swift
│ │ ├── AddressError.swift
│ │ ├── AsyncLock.swift
│ │ ├── AsyncMutex.swift
│ │ ├── CIDR.swift
│ │ ├── CIDRv4.swift
│ │ ├── CIDRv6.swift
│ │ ├── FileManager+Temporary.swift
│ │ ├── IPAddress.swift
│ │ ├── IPv4Address.swift
│ │ ├── IPv6Address+Parse.swift
│ │ ├── IPv6Address.swift
│ │ ├── IndexedAddressAllocator.swift
│ │ ├── MACAddress.swift
│ │ ├── NetworkAddress+Allocator.swift
│ │ ├── Prefix.swift
│ │ ├── ProgressEvent.swift
│ │ ├── ProxyUtils.swift
│ │ ├── RotatingAddressAllocator.swift
│ │ ├── TLSUtils.swift
│ │ ├── Timeout.swift
│ │ └── UInt8+DataBinding.swift
│ ├── ContainerizationIO/
│ │ └── ReadStream.swift
│ ├── ContainerizationNetlink/
│ │ ├── NetlinkSession.swift
│ │ ├── NetlinkSocket.swift
│ │ └── Types.swift
│ ├── ContainerizationOCI/
│ │ ├── AnnotationKeys.swift
│ │ ├── Bundle.swift
│ │ ├── Client/
│ │ │ ├── Authentication.swift
│ │ │ ├── KeychainHelper.swift
│ │ │ ├── LocalOCILayoutClient.swift
│ │ │ ├── RegistryClient+Catalog.swift
│ │ │ ├── RegistryClient+Error.swift
│ │ │ ├── RegistryClient+Fetch.swift
│ │ │ ├── RegistryClient+Push.swift
│ │ │ ├── RegistryClient+Referrers.swift
│ │ │ ├── RegistryClient+Token.swift
│ │ │ └── RegistryClient.swift
│ │ ├── Content/
│ │ │ ├── AsyncTypes.swift
│ │ │ ├── Content.swift
│ │ │ ├── ContentStoreProtocol.swift
│ │ │ ├── ContentWriter.swift
│ │ │ ├── LocalContent.swift
│ │ │ ├── LocalContentStore.swift
│ │ │ ├── SHA256+Extensions.swift
│ │ │ ├── String+Extension.swift
│ │ │ └── URL+Extensions.swift
│ │ ├── Descriptor.swift
│ │ ├── FileManager+Size.swift
│ │ ├── ImageConfig.swift
│ │ ├── Index.swift
│ │ ├── Manifest.swift
│ │ ├── MediaType.swift
│ │ ├── Platform.swift
│ │ ├── Reference.swift
│ │ ├── Spec.swift
│ │ ├── State.swift
│ │ └── Version.swift
│ ├── ContainerizationOS/
│ │ ├── AsyncSignalHandler.swift
│ │ ├── BinaryInteger+Extensions.swift
│ │ ├── Command.swift
│ │ ├── File.swift
│ │ ├── FileDescriptor+SecurePath.swift
│ │ ├── Keychain/
│ │ │ ├── KeychainQuery.swift
│ │ │ └── RegistryInfo.swift
│ │ ├── Linux/
│ │ │ ├── Binfmt.swift
│ │ │ ├── Capabilities.swift
│ │ │ └── Epoll.swift
│ │ ├── Mount/
│ │ │ └── Mount.swift
│ │ ├── POSIXError+Helpers.swift
│ │ ├── Path.swift
│ │ ├── Pipe+Close.swift
│ │ ├── README.md
│ │ ├── Reaper.swift
│ │ ├── Signals.swift
│ │ ├── Socket/
│ │ │ ├── BidirectionalRelay.swift
│ │ │ ├── Socket.swift
│ │ │ ├── SocketType.swift
│ │ │ ├── UnixType.swift
│ │ │ └── VsockType.swift
│ │ ├── Syscall.swift
│ │ ├── Sysctl.swift
│ │ ├── Terminal.swift
│ │ ├── URL+Extensions.swift
│ │ └── User.swift
│ ├── Integration/
│ │ ├── ContainerTests.swift
│ │ ├── PodTests.swift
│ │ └── Suite.swift
│ └── cctl/
│ ├── ImageCommand.swift
│ ├── KernelCommand.swift
│ ├── LoginCommand.swift
│ ├── RootfsCommand.swift
│ ├── RunCommand.swift
│ ├── cctl+Utils.swift
│ └── cctl.swift
├── Tests/
│ ├── ContainerizationArchiveTests/
│ │ ├── ArchiveReaderTests.swift
│ │ ├── ArchiveTests.swift
│ │ └── Resources/
│ │ └── test.tar.zst
│ ├── ContainerizationEXT4Tests/
│ │ ├── Resources/
│ │ │ └── content/
│ │ │ └── blobs/
│ │ │ └── sha256/
│ │ │ ├── 48a06049d3738991b011ca8b12473d712b7c40666a1462118dae3c403676afc2
│ │ │ ├── 4f4fb700ef54461cfa02571ae0db9a0dc1e0cdb5577484a6d75e68dc38e8acc1
│ │ │ ├── 8e2eb240a6cd7be1a0d308125afe0060b020e89275ced2e729eda7d4eeff62a2
│ │ │ ├── ad59e9f71edceca7b1ac7c642410858489b743c97233b0a26a5e2098b1443762
│ │ │ └── c6b39de5b33961661dc939b997cc1d30cda01e38005a6c6625fd9c7e748bab44
│ │ ├── TestEXT4ExtendedAttributes.swift
│ │ ├── TestEXT4Format+Create.swift
│ │ ├── TestEXT4Format.swift
│ │ ├── TestEXT4Reader+IO.swift
│ │ ├── TestEXT4Unpacker.swift
│ │ └── TestFormatterUnpack.swift
│ ├── ContainerizationExtrasTests/
│ │ ├── AsyncMutexTests.swift
│ │ ├── ProxyUtilsTests.swift
│ │ ├── TestCIDR.swift
│ │ ├── TestIPAddress.swift
│ │ ├── TestIPv4Address.swift
│ │ ├── TestIPv6Address+Parse.swift
│ │ ├── TestIPv6Address.swift
│ │ ├── TestIPv6IPv4Parsing.swift
│ │ ├── TestMACAddress.swift
│ │ ├── TestNetworkAddress+Allocator.swift
│ │ ├── TestPrefix.swift
│ │ ├── TestTimeout.swift
│ │ └── UInt8+DataBindingTest.swift
│ ├── ContainerizationNetlinkTests/
│ │ ├── MockNetlinkSocket.swift
│ │ ├── NetlinkSessionTest.swift
│ │ └── TypesTest.swift
│ ├── ContainerizationOCITests/
│ │ ├── AuthChallengeTests.swift
│ │ ├── OCIImageTests.swift
│ │ ├── OCIPlatformTests.swift
│ │ ├── OCISpecTests.swift
│ │ ├── ReferenceTests.swift
│ │ └── RegistryClientTests.swift
│ ├── ContainerizationOSTests/
│ │ ├── FileDescriptor+SecurePathTests.swift
│ │ ├── KeychainQueryTests.swift
│ │ ├── SocketTests.swift
│ │ └── UserTests.swift
│ ├── ContainerizationTests/
│ │ ├── ContainerManagerTests.swift
│ │ ├── DNSTests.swift
│ │ ├── HashTests.swift
│ │ ├── HostsTests.swift
│ │ ├── ImageTests/
│ │ │ ├── ContainsAuth.swift
│ │ │ ├── ImageStoreImagePullTests.swift
│ │ │ └── ImageStoreTests.swift
│ │ ├── ImageTests.swift
│ │ ├── KernelTests.swift
│ │ ├── LinuxContainerTests.swift
│ │ └── MountTests.swift
│ └── TestImages/
│ ├── dockermanifestimage/
│ │ └── Dockerfile
│ └── emptyimage/
│ └── Dockerfile
├── examples/
│ ├── README.md
│ └── ctr-example/
│ ├── Makefile
│ ├── Package.resolved
│ ├── Package.swift
│ ├── README.md
│ ├── Sources/
│ │ └── ctr-example/
│ │ └── main.swift
│ ├── ctr-example.entitlements
│ └── lab.md
├── kernel/
│ ├── Makefile
│ ├── README.md
│ ├── build.sh
│ ├── config-arm64
│ └── image/
│ ├── Dockerfile
│ └── sources.list
├── licenserc.toml
├── scripts/
│ ├── check-integration-test-vm-panics.sh
│ ├── cz-header-style.toml
│ ├── ensure-hawkeye-exists.sh
│ ├── install-hawkeye.sh
│ ├── license-header.txt
│ ├── make-docs.sh
│ └── pre-commit.fmt
├── signing/
│ └── vz.entitlements
└── vminitd/
├── .devcontainer/
│ ├── Dockerfile
│ └── devcontainer.json
├── Makefile
├── Package.resolved
├── Package.swift
└── Sources/
├── Cgroup/
│ └── Cgroup2Manager.swift
├── LCShim/
│ ├── include/
│ │ └── syscall.h
│ └── syscall.c
├── vmexec/
│ ├── Console.swift
│ ├── ExecCommand.swift
│ ├── Mount.swift
│ ├── RunCommand.swift
│ └── vmexec.swift
└── vminitd/
├── AgentCommand.swift
├── Application.swift
├── CommandRunner.swift
├── ContainerProcess.swift
├── HostStdio.swift
├── IOCloser+Extensions.swift
├── IOCloser.swift
├── IOPair.swift
├── InitCommand.swift
├── ManagedContainer.swift
├── ManagedProcess.swift
├── MemoryMonitor.swift
├── OSFile+Splice.swift
├── OSFile.swift
├── PauseCommand.swift
├── ProcessSupervisor.swift
├── Runc/
│ ├── ConsoleSocket.swift
│ └── Runc.swift
├── RuncProcess.swift
├── Server+GRPC.swift
├── Server.swift
├── StandardIO.swift
├── TerminalIO.swift
└── VsockProxy.swift
SYMBOL INDEX (501 symbols across 13 files)
FILE: Sources/CShim/capability.c
function CZ_capget (line 24) | int CZ_capget(void *header, void *data) {
function CZ_capset (line 28) | int CZ_capset(void *header, void *data) {
FILE: Sources/CShim/exec_command.c
function mark_cloexec (line 49) | static int mark_cloexec(int fd) {
function cloexec_from (line 58) | static int cloexec_from(int min_fd) {
function exec_command_attrs_init (line 95) | void exec_command_attrs_init(struct exec_command_attrs *attrs) {
function child_handler (line 108) | static void child_handler(const int sync_pipes[2], const char *executable,
function exec_command (line 317) | int exec_command(pid_t *result, const char *executable, char *const args[],
FILE: Sources/CShim/include/exec_command.h
type exec_command_attrs (line 25) | struct exec_command_attrs {
type exec_command_attrs (line 47) | struct exec_command_attrs
type exec_command_attrs (line 53) | struct exec_command_attrs
FILE: Sources/CShim/include/openat2.h
type cz_open_how (line 26) | struct cz_open_how {
type cz_open_how (line 34) | struct cz_open_how
FILE: Sources/CShim/include/socket_helpers.h
type cmsghdr (line 24) | struct cmsghdr
type msghdr (line 24) | struct msghdr
type cmsghdr (line 25) | struct cmsghdr
FILE: Sources/CShim/openat2.c
function CZ_openat2 (line 28) | int CZ_openat2(int dirfd, const char *pathname, struct cz_open_how *how,
FILE: Sources/CShim/prctl.c
function CZ_prctl_set_keepcaps (line 23) | int CZ_prctl_set_keepcaps() {
function CZ_prctl_clear_keepcaps (line 28) | int CZ_prctl_clear_keepcaps() {
function CZ_prctl_capbset_drop (line 33) | int CZ_prctl_capbset_drop(unsigned int capability) {
function CZ_prctl_cap_ambient_clear_all (line 38) | int CZ_prctl_cap_ambient_clear_all() {
function CZ_prctl_cap_ambient_raise (line 43) | int CZ_prctl_cap_ambient_raise(unsigned int capability) {
FILE: Sources/CShim/socket_helpers.c
type cmsghdr (line 19) | struct cmsghdr
type msghdr (line 19) | struct msghdr
type cmsghdr (line 23) | struct cmsghdr
function CZ_CMSG_SPACE (line 27) | size_t CZ_CMSG_SPACE(size_t length) {
function CZ_CMSG_LEN (line 31) | size_t CZ_CMSG_LEN(size_t length) {
FILE: Sources/ContainerizationArchive/CArchive/archive_swift_bridge.c
function archive_set_error_wrapper (line 22) | void archive_set_error_wrapper(struct archive *a, int error_number, cons...
function zstd_decompress_fd (line 26) | int zstd_decompress_fd(int src_fd, int dst_fd) {
FILE: Sources/ContainerizationArchive/CArchive/include/archive.h
type __int64 (line 65) | typedef __int64 la_int64_t;
type la_int64_t (line 69) | typedef long long la_int64_t;
type la_int64_t (line 71) | typedef int64_t la_int64_t;
type la_ssize_t (line 85) | typedef ssize_t la_ssize_t;
type __int64 (line 87) | typedef __int64 la_ssize_t;
type la_ssize_t (line 89) | typedef long la_ssize_t;
type la_ssize_t (line 93) | typedef ssize_t la_ssize_t;
type archive (line 183) | struct archive
type archive_entry (line 184) | struct archive_entry
type la_ssize_t (line 222) | typedef la_ssize_t archive_read_callback(struct archive *,
type la_int64_t (line 230) | typedef la_int64_t archive_skip_callback(struct archive *,
type la_int64_t (line 237) | typedef la_int64_t archive_seek_callback(struct archive *,
type la_ssize_t (line 241) | typedef la_ssize_t archive_write_callback(struct archive *,
type archive (line 245) | struct archive
type archive (line 247) | struct archive
type archive (line 249) | struct archive
type archive (line 255) | struct archive
type archive (line 262) | struct archive
type archive (line 395) | struct archive
type archive (line 397) | struct archive
type archive (line 399) | struct archive
type archive (line 401) | struct archive
type archive (line 403) | struct archive
type archive (line 405) | struct archive
type archive (line 407) | struct archive
type archive (line 409) | struct archive
type archive (line 412) | struct archive
type archive (line 415) | struct archive
type archive (line 417) | struct archive
type archive (line 419) | struct archive
type archive (line 423) | struct archive
type archive (line 424) | struct archive
type archive (line 425) | struct archive
type archive (line 426) | struct archive
type archive (line 427) | struct archive
type archive (line 428) | struct archive
type archive (line 429) | struct archive
type archive (line 430) | struct archive
type archive (line 431) | struct archive
type archive (line 432) | struct archive
type archive (line 433) | struct archive
type archive (line 434) | struct archive
type archive (line 435) | struct archive
type archive (line 438) | struct archive
type archive (line 440) | struct archive
type archive (line 441) | struct archive
type archive (line 442) | struct archive
type archive (line 443) | struct archive
type archive (line 445) | struct archive
type archive (line 446) | struct archive
type archive (line 447) | struct archive
type archive (line 448) | struct archive
type archive (line 449) | struct archive
type archive (line 450) | struct archive
type archive (line 451) | struct archive
type archive (line 452) | struct archive
type archive (line 453) | struct archive
type archive (line 454) | struct archive
type archive (line 455) | struct archive
type archive (line 456) | struct archive
type archive (line 457) | struct archive
type archive (line 458) | struct archive
type archive (line 459) | struct archive
type archive (line 460) | struct archive
type archive (line 461) | struct archive
type archive (line 464) | struct archive
type archive (line 468) | struct archive
type archive (line 470) | struct archive
type archive (line 476) | struct archive
type archive (line 477) | struct archive
type archive (line 478) | struct archive
type archive (line 481) | struct archive
type archive (line 484) | struct archive
type archive (line 486) | struct archive
type archive (line 488) | struct archive
type archive (line 490) | struct archive
type archive (line 492) | struct archive
type archive (line 495) | struct archive
type archive (line 499) | struct archive
type archive (line 501) | struct archive
type archive (line 504) | struct archive
type archive (line 507) | struct archive
type archive (line 509) | struct archive
type archive (line 512) | struct archive
type archive (line 515) | struct archive
type archive (line 518) | struct archive
type archive (line 528) | struct archive
type archive (line 532) | struct archive
type archive (line 534) | struct archive
type archive (line 537) | struct archive
type archive (line 541) | struct archive
type archive (line 544) | struct archive
type archive (line 547) | struct archive
type archive (line 550) | struct archive
type archive (line 554) | struct archive
type archive (line 557) | struct archive
type archive_entry (line 558) | struct archive_entry
type archive (line 561) | struct archive
type archive_entry (line 562) | struct archive_entry
type archive (line 568) | struct archive
type archive (line 588) | struct archive
type archive (line 594) | struct archive
type archive (line 597) | struct archive
type archive (line 601) | struct archive
type archive (line 609) | struct archive
type archive (line 618) | struct archive
type archive (line 619) | struct archive
type archive (line 625) | struct archive
type archive (line 629) | struct archive
type archive (line 633) | struct archive
type archive (line 637) | struct archive
type archive (line 643) | struct archive
type archive (line 644) | struct archive
type archive (line 707) | struct archive
type archive_entry (line 707) | struct archive_entry
type archive (line 709) | struct archive
type archive_entry (line 709) | struct archive_entry
type archive (line 710) | struct archive
type archive (line 711) | struct archive
type archive (line 716) | struct archive
type archive (line 720) | struct archive
type archive (line 723) | struct archive
type archive (line 726) | struct archive
type archive (line 745) | struct archive
type archive (line 747) | struct archive
type archive (line 749) | struct archive
type archive (line 751) | struct archive
type archive (line 755) | struct archive
type archive (line 759) | struct archive
type archive (line 761) | struct archive
type archive (line 763) | struct archive
type archive (line 765) | struct archive
type archive (line 767) | struct archive
type archive (line 769) | struct archive
type archive (line 771) | struct archive
type archive (line 773) | struct archive
type archive (line 778) | struct archive
type archive (line 779) | struct archive
type archive (line 781) | struct archive
type archive (line 782) | struct archive
type archive (line 783) | struct archive
type archive (line 784) | struct archive
type archive (line 785) | struct archive
type archive (line 786) | struct archive
type archive (line 787) | struct archive
type archive (line 788) | struct archive
type archive (line 789) | struct archive
type archive (line 790) | struct archive
type archive (line 791) | struct archive
type archive (line 792) | struct archive
type archive (line 794) | struct archive
type archive (line 795) | struct archive
type archive (line 796) | struct archive
type archive (line 800) | struct archive
type archive (line 801) | struct archive
type archive (line 804) | struct archive
type archive (line 805) | struct archive
type archive (line 806) | struct archive
type archive (line 807) | struct archive
type archive (line 808) | struct archive
type archive (line 809) | struct archive
type archive (line 810) | struct archive
type archive (line 811) | struct archive
type archive (line 812) | struct archive
type archive (line 813) | struct archive
type archive (line 814) | struct archive
type archive (line 815) | struct archive
type archive (line 817) | struct archive
type archive (line 818) | struct archive
type archive (line 819) | struct archive
type archive (line 820) | struct archive
type archive (line 821) | struct archive
type archive (line 822) | struct archive
type archive (line 823) | struct archive
type archive (line 824) | struct archive
type archive (line 825) | struct archive
type archive (line 826) | struct archive
type archive (line 827) | struct archive
type archive (line 828) | struct archive
type archive (line 829) | struct archive
type archive (line 830) | struct archive
type archive (line 832) | struct archive
type archive (line 835) | struct archive
type archive (line 838) | struct archive
type archive (line 839) | struct archive
type archive (line 840) | struct archive
type archive (line 843) | struct archive
type archive (line 845) | struct archive
type archive (line 848) | struct archive
type archive (line 855) | struct archive
type archive_entry (line 856) | struct archive_entry
type archive (line 857) | struct archive
type archive (line 861) | struct archive
type archive (line 864) | struct archive
type archive (line 865) | struct archive
type archive (line 869) | struct archive
type archive (line 872) | struct archive
type archive (line 875) | struct archive
type archive (line 882) | struct archive
type archive (line 886) | struct archive
type archive (line 890) | struct archive
type archive (line 894) | struct archive
type archive (line 900) | struct archive
type archive (line 901) | struct archive
type archive (line 922) | struct archive
type archive (line 926) | struct archive
type archive (line 945) | struct archive
type archive (line 951) | struct archive
type archive (line 955) | struct archive
type archive (line 959) | struct archive
type archive (line 960) | struct archive
type archive (line 971) | struct archive
type archive (line 973) | struct archive
type archive (line 975) | struct archive
type archive (line 977) | struct archive
type archive_entry (line 978) | struct archive_entry
type stat (line 978) | struct stat
type archive (line 981) | struct archive
type archive (line 982) | struct archive
type archive (line 985) | struct archive
type archive (line 987) | struct archive
type archive (line 991) | struct archive
type archive (line 996) | struct archive
type archive (line 997) | struct archive
type archive (line 1005) | struct archive
type archive (line 1006) | struct archive
type archive (line 1007) | struct archive
type archive (line 1008) | struct archive
type archive (line 1009) | struct archive
type archive (line 1011) | struct archive
type archive (line 1034) | struct archive
type archive (line 1043) | struct archive
type archive (line 1044) | struct archive
type archive (line 1045) | struct archive
type archive_entry (line 1045) | struct archive_entry
type archive (line 1047) | struct archive
type archive (line 1048) | struct archive
type archive_entry (line 1049) | struct archive_entry
type archive (line 1053) | struct archive
type archive (line 1064) | struct archive
type archive (line 1065) | struct archive
type archive (line 1066) | struct archive
type archive (line 1067) | struct archive
type archive (line 1073) | struct archive
type archive (line 1076) | struct archive
type archive (line 1079) | struct archive
type archive (line 1082) | struct archive
type archive (line 1086) | struct archive
type archive (line 1087) | struct archive
type archive (line 1088) | struct archive
type archive (line 1089) | struct archive
type archive (line 1090) | struct archive
type archive (line 1091) | struct archive
type archive (line 1093) | struct archive
type archive (line 1094) | struct archive
type archive (line 1095) | struct archive
type archive (line 1101) | struct archive
type archive (line 1109) | struct archive
type archive_entry (line 1110) | struct archive_entry
type archive (line 1115) | struct archive
type archive_entry (line 1116) | struct archive_entry
type archive (line 1118) | struct archive
type archive (line 1120) | struct archive
type archive (line 1121) | struct archive
type archive (line 1124) | struct archive
type archive (line 1126) | struct archive
type archive (line 1129) | struct archive
type archive (line 1130) | struct archive
type archive (line 1133) | struct archive
type archive (line 1135) | struct archive
type archive (line 1141) | struct archive
type archive (line 1145) | struct archive
type archive (line 1147) | struct archive
type archive (line 1153) | struct archive
type archive_entry (line 1154) | struct archive_entry
type archive (line 1171) | struct archive
type archive (line 1174) | struct archive
type archive (line 1176) | struct archive
type archive (line 1179) | struct archive
type archive (line 1181) | struct archive
type archive (line 1184) | struct archive
type archive_entry (line 1185) | struct archive_entry
type archive (line 1191) | struct archive
type archive_entry (line 1192) | struct archive_entry
type archive (line 1194) | struct archive
type archive (line 1195) | struct archive
type archive (line 1196) | struct archive
type archive (line 1197) | struct archive
type archive (line 1199) | struct archive
type archive (line 1200) | struct archive
FILE: Sources/ContainerizationArchive/CArchive/include/archive_bridge.h
type archive (line 8) | struct archive
FILE: Sources/ContainerizationArchive/CArchive/include/archive_entry.h
type __int64 (line 57) | typedef __int64 la_int64_t;
type la_int64_t (line 61) | typedef long long la_int64_t;
type la_int64_t (line 63) | typedef int64_t la_int64_t;
type la_ssize_t (line 77) | typedef ssize_t la_ssize_t;
type __int64 (line 79) | typedef __int64 la_ssize_t;
type la_ssize_t (line 81) | typedef long la_ssize_t;
type la_ssize_t (line 85) | typedef ssize_t la_ssize_t;
type archive (line 156) | struct archive
type archive_entry (line 157) | struct archive_entry
type archive_entry (line 204) | struct archive_entry
type archive_entry (line 206) | struct archive_entry
type archive_entry (line 207) | struct archive_entry
type archive (line 217) | struct archive
type archive_entry (line 238) | struct archive_entry
type archive_entry (line 239) | struct archive_entry
type archive_entry (line 240) | struct archive_entry
type archive_entry (line 241) | struct archive_entry
type archive_entry (line 242) | struct archive_entry
type archive_entry (line 243) | struct archive_entry
type archive_entry (line 244) | struct archive_entry
type archive_entry (line 245) | struct archive_entry
type archive_entry (line 246) | struct archive_entry
type archive_entry (line 247) | struct archive_entry
type archive_entry (line 248) | struct archive_entry
type archive_entry (line 249) | struct archive_entry
type archive_entry (line 250) | struct archive_entry
type archive_entry (line 251) | struct archive_entry
type archive_entry (line 252) | struct archive_entry
type archive_entry (line 253) | struct archive_entry
type archive_entry (line 256) | struct archive_entry
type archive_entry (line 257) | struct archive_entry
type archive_entry (line 258) | struct archive_entry
type archive_entry (line 259) | struct archive_entry
type archive_entry (line 260) | struct archive_entry
type archive_entry (line 261) | struct archive_entry
type archive_entry (line 262) | struct archive_entry
type archive_entry (line 263) | struct archive_entry
type archive_entry (line 264) | struct archive_entry
type archive_entry (line 265) | struct archive_entry
type archive_entry (line 266) | struct archive_entry
type archive_entry (line 267) | struct archive_entry
type archive_entry (line 268) | struct archive_entry
type archive_entry (line 269) | struct archive_entry
type archive_entry (line 270) | struct archive_entry
type archive_entry (line 271) | struct archive_entry
type archive_entry (line 272) | struct archive_entry
type archive_entry (line 273) | struct archive_entry
type archive_entry (line 274) | struct archive_entry
type archive_entry (line 275) | struct archive_entry
type archive_entry (line 276) | struct archive_entry
type archive_entry (line 277) | struct archive_entry
type archive_entry (line 278) | struct archive_entry
type archive_entry (line 279) | struct archive_entry
type archive_entry (line 280) | struct archive_entry
type archive_entry (line 281) | struct archive_entry
type archive_entry (line 282) | struct archive_entry
type archive_entry (line 283) | struct archive_entry
type archive_entry (line 284) | struct archive_entry
type archive_entry (line 285) | struct archive_entry
type archive_entry (line 286) | struct archive_entry
type archive_entry (line 287) | struct archive_entry
type archive_entry (line 288) | struct archive_entry
type archive_entry (line 289) | struct archive_entry
type archive_entry (line 290) | struct archive_entry
type archive_entry (line 291) | struct archive_entry
type archive_entry (line 292) | struct archive_entry
type archive_entry (line 293) | struct archive_entry
type archive_entry (line 294) | struct archive_entry
type archive_entry (line 295) | struct archive_entry
type archive_entry (line 296) | struct archive_entry
type archive_entry (line 297) | struct archive_entry
type archive_entry (line 298) | struct archive_entry
type archive_entry (line 299) | struct archive_entry
type archive_entry (line 300) | struct archive_entry
type archive_entry (line 301) | struct archive_entry
type archive_entry (line 312) | struct archive_entry
type archive_entry (line 313) | struct archive_entry
type archive_entry (line 315) | struct archive_entry
type archive_entry (line 317) | struct archive_entry
type archive_entry (line 318) | struct archive_entry
type archive_entry (line 319) | struct archive_entry
type archive_entry (line 320) | struct archive_entry
type archive_entry (line 321) | struct archive_entry
type archive_entry (line 322) | struct archive_entry
type archive_entry (line 323) | struct archive_entry
type archive_entry (line 324) | struct archive_entry
type archive_entry (line 325) | struct archive_entry
type archive_entry (line 329) | struct archive_entry
type archive_entry (line 331) | struct archive_entry
type archive_entry (line 333) | struct archive_entry
type archive_entry (line 335) | struct archive_entry
type archive_entry (line 336) | struct archive_entry
type archive_entry (line 337) | struct archive_entry
type archive_entry (line 338) | struct archive_entry
type archive_entry (line 339) | struct archive_entry
type archive_entry (line 340) | struct archive_entry
type archive_entry (line 341) | struct archive_entry
type archive_entry (line 342) | struct archive_entry
type archive_entry (line 343) | struct archive_entry
type archive_entry (line 344) | struct archive_entry
type archive_entry (line 345) | struct archive_entry
type archive_entry (line 346) | struct archive_entry
type archive_entry (line 347) | struct archive_entry
type archive_entry (line 348) | struct archive_entry
type archive_entry (line 349) | struct archive_entry
type archive_entry (line 350) | struct archive_entry
type archive_entry (line 351) | struct archive_entry
type archive_entry (line 352) | struct archive_entry
type archive_entry (line 353) | struct archive_entry
type archive_entry (line 354) | struct archive_entry
type archive_entry (line 355) | struct archive_entry
type archive_entry (line 356) | struct archive_entry
type archive_entry (line 357) | struct archive_entry
type archive_entry (line 358) | struct archive_entry
type archive_entry (line 359) | struct archive_entry
type archive_entry (line 360) | struct archive_entry
type archive_entry (line 361) | struct archive_entry
type archive_entry (line 362) | struct archive_entry
type archive_entry (line 363) | struct archive_entry
type archive_entry (line 364) | struct archive_entry
type archive_entry (line 365) | struct archive_entry
type archive_entry (line 366) | struct archive_entry
type archive_entry (line 367) | struct archive_entry
type archive_entry (line 368) | struct archive_entry
type archive_entry (line 369) | struct archive_entry
type archive_entry (line 370) | struct archive_entry
type archive_entry (line 371) | struct archive_entry
type archive_entry (line 372) | struct archive_entry
type archive_entry (line 373) | struct archive_entry
type archive_entry (line 374) | struct archive_entry
type archive_entry (line 375) | struct archive_entry
type archive_entry (line 376) | struct archive_entry
type archive_entry (line 377) | struct archive_entry
type archive_entry (line 378) | struct archive_entry
type archive_entry (line 379) | struct archive_entry
type archive_entry (line 380) | struct archive_entry
type archive_entry (line 381) | struct archive_entry
type archive_entry (line 382) | struct archive_entry
type archive_entry (line 383) | struct archive_entry
type archive_entry (line 396) | struct archive_entry
type archive_entry (line 397) | struct archive_entry
type stat (line 397) | struct stat
type archive_entry (line 406) | struct archive_entry
type archive_entry (line 407) | struct archive_entry
type archive_entry (line 420) | struct archive_entry
type archive_entry (line 533) | struct archive_entry
type archive_entry (line 534) | struct archive_entry
type archive_entry (line 537) | struct archive_entry
type archive_entry (line 546) | struct archive_entry
type archive_entry (line 547) | struct archive_entry
type archive_entry (line 579) | struct archive_entry
type archive_entry (line 581) | struct archive_entry
type archive_entry (line 583) | struct archive_entry
type archive_entry (line 585) | struct archive_entry
type archive_entry (line 593) | struct archive_entry
type archive_entry (line 595) | struct archive_entry
type archive_entry (line 599) | struct archive_entry
type archive_entry (line 602) | struct archive_entry
type archive_acl (line 606) | struct archive_acl
type archive_entry (line 607) | struct archive_entry
type archive_entry (line 613) | struct archive_entry
type archive_entry (line 614) | struct archive_entry
type archive_entry (line 623) | struct archive_entry
type archive_entry (line 624) | struct archive_entry
type archive_entry (line 625) | struct archive_entry
type archive_entry (line 632) | struct archive_entry
type archive_entry (line 633) | struct archive_entry
type archive_entry (line 641) | struct archive_entry
type archive_entry (line 642) | struct archive_entry
type archive_entry (line 643) | struct archive_entry
type archive_entry_linkresolver (line 674) | struct archive_entry_linkresolver
type archive_entry_linkresolver (line 718) | struct archive_entry_linkresolver
type archive_entry_linkresolver (line 719) | struct archive_entry_linkresolver
type archive_entry_linkresolver (line 720) | struct archive_entry_linkresolver
type archive_entry (line 721) | struct archive_entry
type archive_entry (line 721) | struct archive_entry
type archive_entry_linkresolver (line 723) | struct archive_entry_linkresolver
FILE: vminitd/Sources/LCShim/syscall.c
function CZ_pivot_root (line 23) | int CZ_pivot_root(const char *new_root, const char *put_old) {
function CZ_set_sub_reaper (line 27) | int CZ_set_sub_reaper() { return prctl(PR_SET_CHILD_SUBREAPER, 1); }
function CZ_pidfd_open (line 29) | int CZ_pidfd_open(pid_t pid, unsigned int flags) {
function CZ_pidfd_getfd (line 34) | int CZ_pidfd_getfd(int pidfd, int targetfd, unsigned int flags) {
function CZ_prctl_set_no_new_privs (line 39) | int CZ_prctl_set_no_new_privs() {
Condensed preview — 324 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (2,838K chars).
[
{
"path": ".github/ISSUE_TEMPLATE/01-bug.yml",
"chars": 2151,
"preview": "name: Bug report\ndescription: File a bug report.\ntitle: \"[Bug]: \"\ntype: \"Bug\"\nbody:\n - type: markdown\n attributes:\n "
},
{
"path": ".github/ISSUE_TEMPLATE/02-feature.yml",
"chars": 982,
"preview": "name: Feature or enhancement request\ndescription: File a request for a feature or enhancement\ntitle: \"[Request]: \"\ntype:"
},
{
"path": ".github/ISSUE_TEMPLATE/config.yml",
"chars": 193,
"preview": "blank_issues_enabled: false\ncontact_links:\n - name: Containerization community support\n url: https://github.com/appl"
},
{
"path": ".github/workflows/build-test-images.yml",
"chars": 2456,
"preview": "name: Build and publish containerization test images\n\npermissions:\n contents: read\n\non: \n workflow_dispatch: \n inpu"
},
{
"path": ".github/workflows/containerization-build-template.yml",
"chars": 3861,
"preview": "name: Build containerization template\n\npermissions:\n contents: read\n\non: \n workflow_call:\n inputs:\n release:\n "
},
{
"path": ".github/workflows/containerization-build.yml",
"chars": 1667,
"preview": "name: Build containerization\n\npermissions:\n contents: read\n\non:\n pull_request:\n types: [opened, reopened, synchroni"
},
{
"path": ".github/workflows/docs-release.yaml",
"chars": 1178,
"preview": "# Manual workflow for releasing docs ad-hoc. Workflow can only be run for main or release branches. \n# Workflow does NOT"
},
{
"path": ".github/workflows/release.yml",
"chars": 1309,
"preview": "name: Release containerization\n\npermissions:\n contents: read\n\non: \n push: \n tags:\n - \"[0-9]+\\\\.[0-9]+\\\\.[0-9]+"
},
{
"path": ".gitignore",
"chars": 246,
"preview": ".DS_Store\nbin\nlibexec\n.build\n.local\nxcuserdata/\nDerivedData/\n.swiftpm/\n.netrc\n.swiftpm\nworkdir/\ninstaller/\n.venv/\ntest_r"
},
{
"path": ".spi.yml",
"chars": 281,
"preview": "version: 1\nbuilder:\n configs:\n - documentation_targets: [Containerization, ContainerizationEXT4, ContainerizationOS,"
},
{
"path": ".swift-format",
"chars": 2372,
"preview": "{\n \"fileScopedDeclarationPrivacy\" : {\n \"accessLevel\" : \"private\"\n },\n \"indentation\" : {\n \"spaces\" : 4\n },\n \"i"
},
{
"path": ".swift-format-nolint",
"chars": 2395,
"preview": "{\n \"fileScopedDeclarationPrivacy\" : {\n \"accessLevel\" : \"private\"\n },\n \"indentation\" : {\n \"spaces\" : 4\n },\n \"i"
},
{
"path": ".swift-version",
"chars": 23,
"preview": "6.3-snapshot-2026-02-27"
},
{
"path": "CONTRIBUTING.md",
"chars": 6321,
"preview": "# 🌈 📦️ Welcome to the Containerization community! 📦️ 🌈\n\nContributions to Containerization are welcomed and encouraged.\n\n"
},
{
"path": "LICENSE",
"chars": 11357,
"preview": "\n Apache License\n Version 2.0, January 2004\n "
},
{
"path": "MAINTAINERS.txt",
"chars": 622,
"preview": "This file contains a list of maintainers and past maintainers who have made meaningful changes to this repository.\n\n### "
},
{
"path": "Makefile",
"chars": 6664,
"preview": "# Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n#\n# Licensed under the Apache License, Vers"
},
{
"path": "Package.resolved",
"chars": 7063,
"preview": "{\n \"originHash\" : \"8b51a9ec068537ab57ce9b8034b5b84a02a4697e4a6be491954e5fbda7e5783b\",\n \"pins\" : [\n {\n \"identit"
},
{
"path": "Package.swift",
"chars": 11006,
"preview": "// swift-tools-version: 6.2\n//===----------------------------------------------------------------------===//\n// Copyrigh"
},
{
"path": "Protobuf.Makefile",
"chars": 2047,
"preview": "# Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n#\n# Licensed under the Apache License, Vers"
},
{
"path": "README.md",
"chars": 7290,
"preview": "# Containerization\n\nThe Containerization package allows applications to use Linux containers.\nContainerization is writte"
},
{
"path": "SECURITY.md",
"chars": 1489,
"preview": "# Security disclosure process\n\nIf you believe that you have discovered a security or privacy vulnerability in our open s"
},
{
"path": "Sources/CShim/capability.c",
"chars": 957,
"preview": "/*\n * Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n *\n * Licensed under the Apache License"
},
{
"path": "Sources/CShim/exec_command.c",
"chars": 9201,
"preview": "/*\n * Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n *\n * Licensed under the Apache License"
},
{
"path": "Sources/CShim/include/capability.h",
"chars": 843,
"preview": "/*\n * Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n *\n * Licensed under the Apache License"
},
{
"path": "Sources/CShim/include/exec_command.h",
"chars": 1770,
"preview": "/*\n * Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n *\n * Licensed under the Apache License"
},
{
"path": "Sources/CShim/include/openat2.h",
"chars": 1098,
"preview": "/*\n * Copyright © 2026 Apple Inc. and the Containerization project authors.\n *\n * Licensed under the Apache License, Ver"
},
{
"path": "Sources/CShim/include/prctl.h",
"chars": 991,
"preview": "/*\n * Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n *\n * Licensed under the Apache License"
},
{
"path": "Sources/CShim/include/socket_helpers.h",
"chars": 991,
"preview": "/*\n * Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n *\n * Licensed under the Apache License"
},
{
"path": "Sources/CShim/include/vsock.h",
"chars": 909,
"preview": "/*\n * Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n *\n * Licensed under the Apache License"
},
{
"path": "Sources/CShim/openat2.c",
"chars": 954,
"preview": "/*\n * Copyright © 2026 Apple Inc. and the Containerization project authors.\n *\n * Licensed under the Apache License, Ver"
},
{
"path": "Sources/CShim/prctl.c",
"chars": 1431,
"preview": "/*\n * Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n *\n * Licensed under the Apache License"
},
{
"path": "Sources/CShim/socket_helpers.c",
"chars": 974,
"preview": "/*\n * Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n *\n * Licensed under the Apache License"
},
{
"path": "Sources/CShim/vsock.c",
"chars": 736,
"preview": "/*\n * Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n *\n * Licensed under the Apache License"
},
{
"path": "Sources/Containerization/AttachedFilesystem.swift",
"chars": 2110,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/Container.swift",
"chars": 1224,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/ContainerManager.swift",
"chars": 14328,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/ContainerStatistics.swift",
"chars": 8190,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/DNSConfiguration.swift",
"chars": 2257,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/ExitStatus.swift",
"chars": 1349,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/FileMount.swift",
"chars": 8931,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2026 Apple Inc. and the "
},
{
"path": "Sources/Containerization/Hash.swift",
"chars": 1354,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/HostsConfiguration.swift",
"chars": 4587,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/IO/ReaderStream.swift",
"chars": 941,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/IO/Terminal+ReaderStream.swift",
"chars": 1321,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/IO/Writer.swift",
"chars": 956,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/Image/Image.swift",
"chars": 5832,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/Image/ImageStore/ImageStore+Export.swift",
"chars": 7555,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/Image/ImageStore/ImageStore+Import.swift",
"chars": 11890,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/Image/ImageStore/ImageStore+OCILayout.swift",
"chars": 5698,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/Image/ImageStore/ImageStore+ReferenceManager.swift",
"chars": 3416,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/Image/ImageStore/ImageStore.swift",
"chars": 15230,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/Image/InitImage.swift",
"chars": 4337,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/Image/KernelImage.swift",
"chars": 4657,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/Image/Unpacker/EXT4Unpacker.swift",
"chars": 4476,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/Image/Unpacker/Unpacker.swift",
"chars": 1878,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/Interface.swift",
"chars": 1422,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/Kernel.swift",
"chars": 3419,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/LinuxContainer.swift",
"chars": 55691,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/LinuxPod.swift",
"chars": 37466,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/LinuxProcess.swift",
"chars": 15615,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/LinuxProcessConfiguration.swift",
"chars": 15189,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/Mount.swift",
"chars": 7719,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/NATInterface.swift",
"chars": 1275,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/NATNetworkInterface.swift",
"chars": 2884,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/Network.swift",
"chars": 1036,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2026 Apple Inc. and the "
},
{
"path": "Sources/Containerization/SandboxContext/SandboxContext.grpc.swift",
"chars": 149926,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/SandboxContext/SandboxContext.pb.swift",
"chars": 172528,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/SandboxContext/SandboxContext.proto",
"chars": 11126,
"preview": "syntax = \"proto3\";\n\npackage com.apple.containerization.sandbox.v3;\n\nimport \"google/protobuf/timestamp.proto\";\n\n// Contex"
},
{
"path": "Sources/Containerization/SystemPlatform.swift",
"chars": 1666,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/TimeSyncer.swift",
"chars": 2507,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/UnixSocketConfiguration.swift",
"chars": 2748,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/UnixSocketRelay.swift",
"chars": 7377,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/UnixSocketRelayManager.swift",
"chars": 2417,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2026 Apple Inc. and the "
},
{
"path": "Sources/Containerization/VMConfiguration.swift",
"chars": 3812,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/VZVirtualMachine+Helpers.swift",
"chars": 5440,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/VZVirtualMachineInstance.swift",
"chars": 16632,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/VZVirtualMachineManager.swift",
"chars": 2869,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/VirtualMachineAgent+Additions.swift",
"chars": 1101,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/VirtualMachineAgent.swift",
"chars": 4355,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/VirtualMachineInstance.swift",
"chars": 2195,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/VirtualMachineManager.swift",
"chars": 1009,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/Vminitd+Rosetta.swift",
"chars": 1310,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/Vminitd+SocketRelay.swift",
"chars": 2007,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/Vminitd.swift",
"chars": 20908,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/Containerization/VmnetNetwork.swift",
"chars": 7812,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2026 Apple Inc. and the "
},
{
"path": "Sources/Containerization/VsockListener.swift",
"chars": 2442,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationArchive/ArchiveError.swift",
"chars": 4451,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationArchive/ArchiveReader.swift",
"chars": 16411,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2026 Apple Inc. and the "
},
{
"path": "Sources/ContainerizationArchive/ArchiveWriter.swift",
"chars": 12930,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationArchive/ArchiveWriterConfiguration.swift",
"chars": 6985,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationArchive/CArchive/COPYING",
"chars": 3086,
"preview": "The libarchive distribution as a whole is Copyright by Tim Kientzle\nand is subject to the copyright notice reproduced at"
},
{
"path": "Sources/ContainerizationArchive/CArchive/archive_swift_bridge.c",
"chars": 2206,
"preview": "/*\n * Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n *\n * Licensed under the Apache License"
},
{
"path": "Sources/ContainerizationArchive/CArchive/include/archive.h",
"chars": 53839,
"preview": "/*-\n * Copyright (c) 2003-2010 Tim Kientzle\n * All rights reserved.\n *\n * Redistribution and use in source and binary fo"
},
{
"path": "Sources/ContainerizationArchive/CArchive/include/archive_bridge.h",
"chars": 334,
"preview": "//\n\n#pragma once\n\n#include \"archive.h\"\n#include <stdint.h>\n\nvoid archive_set_error_wrapper(struct archive *a, int error_"
},
{
"path": "Sources/ContainerizationArchive/CArchive/include/archive_entry.h",
"chars": 34375,
"preview": "/*-\n * Copyright (c) 2003-2008 Tim Kientzle\n * Copyright (c) 2016 Martin Matuska\n * All rights reserved.\n *\n * Redistrib"
},
{
"path": "Sources/ContainerizationArchive/TempDir.swift",
"chars": 1522,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationArchive/WriteEntry.swift",
"chars": 10088,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationEXT4/Documentation.docc/ext4.md",
"chars": 187,
"preview": "# ``ContainerizationEXT4``\n\n`ContainerizationEXT4` provides functionality to read the superblock of an existing ext4 blo"
},
{
"path": "Sources/ContainerizationEXT4/EXT4+Extensions.swift",
"chars": 3218,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationEXT4/EXT4+FileTree.swift",
"chars": 4197,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationEXT4/EXT4+Formatter.swift",
"chars": 64600,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationEXT4/EXT4+Ptr.swift",
"chars": 2607,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationEXT4/EXT4+Reader.swift",
"chars": 12174,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationEXT4/EXT4+Types.swift",
"chars": 25146,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationEXT4/EXT4+Xattrs.swift",
"chars": 12249,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationEXT4/EXT4.swift",
"chars": 11983,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationEXT4/EXT4Reader+Export.swift",
"chars": 9445,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationEXT4/EXT4Reader+IO.swift",
"chars": 21709,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationEXT4/FilePath+Extensions.swift",
"chars": 3671,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationEXT4/FileTimestamps.swift",
"chars": 1814,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationEXT4/Formatter+Unpack.swift",
"chars": 6856,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationEXT4/Integer+Extensions.swift",
"chars": 3693,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationEXT4/README.md",
"chars": 187,
"preview": "# ``ContainerizationEXT4``\n\n`ContainerizationEXT4` provides functionality to read the superblock of an existing ext4 blo"
},
{
"path": "Sources/ContainerizationEXT4/UnsafeLittleEndianBytes.swift",
"chars": 2625,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationError/ContainerizationError.swift",
"chars": 5276,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationExtras/AddressAllocator.swift",
"chars": 2653,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationExtras/AddressError.swift",
"chars": 1970,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2026 Apple Inc. and the "
},
{
"path": "Sources/ContainerizationExtras/AsyncLock.swift",
"chars": 2308,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationExtras/AsyncMutex.swift",
"chars": 2206,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationExtras/CIDR.swift",
"chars": 5165,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationExtras/CIDRv4.swift",
"chars": 4281,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationExtras/CIDRv6.swift",
"chars": 4437,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationExtras/FileManager+Temporary.swift",
"chars": 1288,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationExtras/IPAddress.swift",
"chars": 4327,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationExtras/IPv4Address.swift",
"chars": 8838,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationExtras/IPv6Address+Parse.swift",
"chars": 13436,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationExtras/IPv6Address.swift",
"chars": 9445,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationExtras/IndexedAddressAllocator.swift",
"chars": 4484,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationExtras/MACAddress.swift",
"chars": 9853,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2026 Apple Inc. and the "
},
{
"path": "Sources/ContainerizationExtras/NetworkAddress+Allocator.swift",
"chars": 4109,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationExtras/Prefix.swift",
"chars": 3046,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationExtras/ProgressEvent.swift",
"chars": 1974,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationExtras/ProxyUtils.swift",
"chars": 3160,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationExtras/RotatingAddressAllocator.swift",
"chars": 4186,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationExtras/TLSUtils.swift",
"chars": 1485,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationExtras/Timeout.swift",
"chars": 2356,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationExtras/UInt8+DataBinding.swift",
"chars": 3635,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationIO/ReadStream.swift",
"chars": 4499,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationNetlink/NetlinkSession.swift",
"chars": 26034,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationNetlink/NetlinkSocket.swift",
"chars": 4738,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationNetlink/Types.swift",
"chars": 34346,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOCI/AnnotationKeys.swift",
"chars": 1382,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOCI/Bundle.swift",
"chars": 4903,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOCI/Client/Authentication.swift",
"chars": 1975,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOCI/Client/KeychainHelper.swift",
"chars": 5643,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOCI/Client/LocalOCILayoutClient.swift",
"chars": 9787,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOCI/Client/RegistryClient+Catalog.swift",
"chars": 3741,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2026 Apple Inc. and the "
},
{
"path": "Sources/ContainerizationOCI/Client/RegistryClient+Error.swift",
"chars": 2707,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOCI/Client/RegistryClient+Fetch.swift",
"chars": 9729,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOCI/Client/RegistryClient+Push.swift",
"chars": 8039,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOCI/Client/RegistryClient+Referrers.swift",
"chars": 3948,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2026 Apple Inc. and the "
},
{
"path": "Sources/ContainerizationOCI/Client/RegistryClient+Token.swift",
"chars": 8701,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOCI/Client/RegistryClient.swift",
"chars": 12105,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOCI/Content/AsyncTypes.swift",
"chars": 1568,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOCI/Content/Content.swift",
"chars": 2071,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOCI/Content/ContentStoreProtocol.swift",
"chars": 3416,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOCI/Content/ContentWriter.swift",
"chars": 2958,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOCI/Content/LocalContent.swift",
"chars": 2639,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOCI/Content/LocalContentStore.swift",
"chars": 8719,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOCI/Content/SHA256+Extensions.swift",
"chars": 1226,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOCI/Content/String+Extension.swift",
"chars": 1073,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOCI/Content/URL+Extensions.swift",
"chars": 1309,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOCI/Descriptor.swift",
"chars": 2484,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOCI/FileManager+Size.swift",
"chars": 1179,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOCI/ImageConfig.swift",
"chars": 6547,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOCI/Index.swift",
"chars": 3032,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOCI/Manifest.swift",
"chars": 2539,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOCI/MediaType.swift",
"chars": 3571,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOCI/Platform.swift",
"chars": 13101,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOCI/Reference.swift",
"chars": 11021,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOCI/Spec.swift",
"chars": 30259,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOCI/State.swift",
"chars": 2546,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOCI/Version.swift",
"chars": 1223,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOS/AsyncSignalHandler.swift",
"chars": 3472,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOS/BinaryInteger+Extensions.swift",
"chars": 1708,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOS/Command.swift",
"chars": 10109,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOS/File.swift",
"chars": 3582,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOS/FileDescriptor+SecurePath.swift",
"chars": 8970,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2026 Apple Inc. and the "
},
{
"path": "Sources/ContainerizationOS/Keychain/KeychainQuery.swift",
"chars": 10320,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOS/Keychain/RegistryInfo.swift",
"chars": 1251,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2026 Apple Inc. and the "
},
{
"path": "Sources/ContainerizationOS/Linux/Binfmt.swift",
"chars": 3751,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOS/Linux/Capabilities.swift",
"chars": 23120,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOS/Linux/Epoll.swift",
"chars": 5463,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOS/Mount/Mount.swift",
"chars": 15247,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOS/POSIXError+Helpers.swift",
"chars": 1079,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOS/Path.swift",
"chars": 2992,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOS/Pipe+Close.swift",
"chars": 1573,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOS/README.md",
"chars": 78,
"preview": "## OS\n\nThis target contains general useful OS related definitions or wrappers."
},
{
"path": "Sources/ContainerizationOS/Reaper.swift",
"chars": 1751,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOS/Signals.swift",
"chars": 4044,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOS/Socket/BidirectionalRelay.swift",
"chars": 9842,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2026 Apple Inc. and the "
},
{
"path": "Sources/ContainerizationOS/Socket/Socket.swift",
"chars": 15881,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOS/Socket/SocketType.swift",
"chars": 1901,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOS/Socket/UnixType.swift",
"chars": 5266,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOS/Socket/VsockType.swift",
"chars": 3669,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOS/Syscall.swift",
"chars": 1348,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOS/Sysctl.swift",
"chars": 1240,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOS/Terminal.swift",
"chars": 6714,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
},
{
"path": "Sources/ContainerizationOS/URL+Extensions.swift",
"chars": 2137,
"preview": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and"
}
]
// ... and 124 more files (download for full content)
About this extraction
This page contains the full source code of the apple/containerization GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 324 files (2.6 MB), approximately 696.3k tokens, and a symbol index with 501 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.