├── docs └── assets │ └── landing-movie.gif ├── .github ├── labeler.yml ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── 02-feature.yml │ └── 01-bug.yml ├── pull_request_template.md └── workflows │ ├── pr-build.yml │ ├── merge-build.yml │ ├── pr-label-analysis.yml │ ├── docs-release.yml │ ├── release.yml │ ├── release-build.yml │ └── pr-label-apply.yml ├── CONTRIBUTING.md ├── MAINTAINERS.txt ├── signing ├── container-network-vmnet.entitlements └── container-runtime-linux.entitlements ├── .spi.yml ├── .gitignore ├── config ├── container-network-vmnet-config.json ├── container-runtime-linux-config.json └── container-core-images-config.json ├── scripts ├── container-header-style.toml ├── license-header.txt ├── ensure-hawkeye-exists.sh ├── install-hawkeye.sh ├── ensure-container-stopped.sh ├── install-init.sh └── make-docs.sh ├── licenserc.toml ├── Sources ├── CAuditToken │ ├── AuditToken.c │ └── include │ │ └── AuditToken.h ├── ContainerClient │ ├── Core │ │ ├── Constants.swift │ │ ├── ContainerCreateOptions.swift │ │ ├── RuntimeStatus.swift │ │ ├── ContainerStopOptions.swift │ │ ├── ImageDescription.swift │ │ ├── SystemHealth.swift │ │ ├── PublishSocket.swift │ │ ├── ContainerSnapshot.swift │ │ ├── ClientDiskUsage.swift │ │ ├── DiskUsage.swift │ │ └── ContainerStats.swift │ ├── Array+Dedupe.swift │ ├── Arch.swift │ ├── SandboxSnapshot.swift │ ├── String+Extensions.swift │ ├── ContainerizationProgressAdapter.swift │ ├── SandboxRoutes.swift │ ├── TableOutput.swift │ └── SignalThreshold.swift ├── SocketForwarder │ ├── SocketForwarder.swift │ ├── SocketForwarderResult.swift │ └── TCPForwarder.swift ├── Services │ ├── ContainerNetworkService │ │ ├── NetworkKeys.swift │ │ ├── Network.swift │ │ ├── NetworkMode.swift │ │ ├── NetworkRoutes.swift │ │ ├── Attachment.swift │ │ ├── AttachmentConfiguration.swift │ │ ├── NetworkState.swift │ │ └── AttachmentAllocator.swift │ ├── ContainerImagesService │ │ └── Client │ │ │ └── ImageServiceXPCRoutes.swift │ ├── ContainerSandboxService │ │ └── InterfaceStrategy.swift │ └── ContainerAPIService │ │ ├── DiskUsage │ │ └── DiskUsageHarness.swift │ │ └── HealthCheck │ │ └── HealthCheckHarness.swift ├── TerminalProgress │ ├── StandardError.swift │ ├── ProgressTheme.swift │ ├── Int64+Formatted.swift │ ├── ProgressUpdate.swift │ └── Int+Formatted.swift ├── ContainerCommands │ ├── Codable+JSON.swift │ ├── Container │ │ ├── ProcessUtils.swift │ │ └── ContainerInspect.swift │ ├── System │ │ ├── SystemKernel.swift │ │ ├── SystemDNS.swift │ │ ├── SystemProperty.swift │ │ ├── SystemCommand.swift │ │ ├── Property │ │ │ ├── PropertyClear.swift │ │ │ └── PropertyGet.swift │ │ └── DNS │ │ │ ├── DNSDelete.swift │ │ │ ├── DNSCreate.swift │ │ │ └── DNSList.swift │ ├── Registry │ │ ├── RegistryCommand.swift │ │ └── Logout.swift │ ├── Network │ │ ├── NetworkCommand.swift │ │ ├── NetworkInspect.swift │ │ └── NetworkCreate.swift │ ├── Builder │ │ ├── Builder.swift │ │ ├── BuilderStop.swift │ │ └── BuilderDelete.swift │ ├── Volume │ │ ├── VolumeCommand.swift │ │ ├── VolumeInspect.swift │ │ └── VolumeCreate.swift │ └── Image │ │ ├── ImageCommand.swift │ │ ├── ImageTag.swift │ │ └── ImageInspect.swift ├── CVersion │ ├── Version.c │ └── include │ │ └── Version.h ├── DNSServer │ ├── DNSHandler.swift │ ├── Handlers │ │ ├── CompositeResolver.swift │ │ ├── NxDomainResolver.swift │ │ └── StandardQueryValidator.swift │ └── Types.swift ├── ContainerVersion │ ├── Bundle+AppBundle.swift │ ├── CommandLine+Executable.swift │ └── ReleaseVersion.swift ├── CLI │ └── ContainerCLI.swift ├── ContainerPlugin │ ├── ApplicationRoot.swift │ └── InstallRoot.swift ├── Helpers │ ├── RuntimeLinux │ │ ├── IsolatedInterfaceStrategy.swift │ │ ├── RuntimeLinuxHelper.swift │ │ └── NonisolatedInterfaceStrategy.swift │ ├── APIServer │ │ └── APIServer.swift │ └── NetworkVmnet │ │ └── NetworkVmnetHelper.swift ├── ContainerBuild │ ├── TerminalCommand.swift │ ├── BuildFile.swift │ └── BuildStdio.swift └── ContainerLog │ └── StderrLogHandler.swift ├── Tests ├── ContainerPluginTests │ ├── CommandLine+ExecutableTest.swift │ └── MockPluginFactory.swift ├── SocketForwarderTests │ ├── UDPEchoHandler.swift │ ├── TCPEchoHandler.swift │ ├── UDPEchoServer.swift │ ├── TCPEchoServer.swift │ └── LRUCacheTest.swift ├── CLITests │ ├── Subcommands │ │ ├── Build │ │ │ ├── CLIBuilderLifecycleTest.swift │ │ │ └── TestCLITermIO.swift │ │ └── Plugins │ │ │ └── TestCLIPluginErrors.swift │ └── TestCLINoParallelCases.swift └── DNSServerTests │ ├── NxDomainResolverTest.swift │ ├── MockHandlers.swift │ └── CompositeResolverTest.swift ├── SECURITY.md ├── .swift-format └── Protobuf.Makefile /docs/assets/landing-movie.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apple/container/HEAD/docs/assets/landing-movie.gif -------------------------------------------------------------------------------- /.github/labeler.yml: -------------------------------------------------------------------------------- 1 | cli: 2 | - changed-files: 3 | - any-glob-to-any-file: 4 | - 'Sources/CLI/**' 5 | - 'Sources/ContainerCommands/**' -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are welcome and encouraged! Read our [main contributing guide](https://github.com/apple/containerization/blob/main/CONTRIBUTING.md) to get started. 4 | -------------------------------------------------------------------------------- /MAINTAINERS.txt: -------------------------------------------------------------------------------- 1 | # Maintainers 2 | 3 | See [MAINTAINERS](https://github.com/apple/containerization/blob/main/MAINTAINERS.txt) for the list of current and former maintainers of this project. Thank you for all your contributions! 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Container community support 4 | url: https://github.com/apple/container/discussions 5 | about: Please ask and answer questions here. 6 | -------------------------------------------------------------------------------- /signing/container-network-vmnet.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.virtualization 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /signing/container-runtime-linux.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.virtualization 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Type of Change 2 | - [ ] Bug fix 3 | - [ ] New feature 4 | - [ ] Breaking change 5 | - [ ] Documentation update 6 | 7 | ## Motivation and Context 8 | [Why is this change needed?] 9 | 10 | ## Testing 11 | - [ ] Tested locally 12 | - [ ] Added/updated tests 13 | - [ ] Added/updated docs 14 | -------------------------------------------------------------------------------- /.spi.yml: -------------------------------------------------------------------------------- 1 | version: 1 2 | builder: 3 | configs: 4 | - 5 | documentation_targets: 6 | - ContainerSandboxService 7 | - ContainerNetworkService 8 | - ContainerImagesService 9 | - ContainerClient 10 | - ContainerLog 11 | - ContainerPlugin 12 | - ContainerXPC 13 | - TerminalProgress 14 | swift_version: '6.2' 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | bin 3 | libexec 4 | .build 5 | .local 6 | xcuserdata/ 7 | DerivedData/ 8 | Packages/ 9 | .swiftpm/ 10 | .netrc 11 | .swiftpm 12 | api-docs/ 13 | workdir/ 14 | installer/ 15 | .venv/ 16 | .claude/ 17 | .clitests/ 18 | test_results/ 19 | *.pid 20 | *.log 21 | *.zip 22 | *.o 23 | *.ext4 24 | *.pkg 25 | *.swp 26 | 27 | # API docs for local preview only. 28 | _site/ 29 | _serve/ 30 | -------------------------------------------------------------------------------- /config/container-network-vmnet-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "abstract" : "vmnet network management plugin", 3 | "version": "0.1", 4 | "author": "Apple", 5 | "servicesConfig" : { 6 | "loadAtBoot" : false, 7 | "runAtLoad" : true, 8 | "services" : [ 9 | { 10 | "type" : "network" 11 | } 12 | ], 13 | "defaultArguments": [] 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /config/container-runtime-linux-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "abstract" : "Linux container runtime plugin", 3 | "version": "0.1", 4 | "author": "Apple", 5 | "servicesConfig" : { 6 | "loadAtBoot" : false, 7 | "runAtLoad" : false, 8 | "services" : [ 9 | { 10 | "type" : "runtime" 11 | } 12 | ], 13 | "defaultArguments": [] 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.github/workflows/pr-build.yml: -------------------------------------------------------------------------------- 1 | name: container project - PR build 2 | 3 | permissions: 4 | contents: read 5 | 6 | on: 7 | pull_request: 8 | types: [opened, reopened, synchronize] 9 | 10 | jobs: 11 | build: 12 | name: Invoke build 13 | uses: ./.github/workflows/common.yml 14 | with: 15 | release: false 16 | secrets: inherit 17 | permissions: 18 | contents: read 19 | packages: read 20 | pages: write 21 | -------------------------------------------------------------------------------- /.github/workflows/merge-build.yml: -------------------------------------------------------------------------------- 1 | name: container project - merge build 2 | 3 | permissions: 4 | contents: read 5 | 6 | on: 7 | push: 8 | branches: 9 | - main 10 | - release/* 11 | 12 | jobs: 13 | build: 14 | name: Invoke build 15 | uses: ./.github/workflows/common.yml 16 | with: 17 | release: true 18 | secrets: inherit 19 | permissions: 20 | contents: read 21 | packages: read 22 | pages: write 23 | -------------------------------------------------------------------------------- /scripts/container-header-style.toml: -------------------------------------------------------------------------------- 1 | [SWIFT_STYLE] 2 | firstLine = '//===----------------------------------------------------------------------===//' 3 | endLine = "//===----------------------------------------------------------------------===//\n" 4 | beforeEachLine = '// ' 5 | afterEachLine = '' 6 | allowBlankLines = false 7 | multipleLines = true 8 | padLines = false 9 | firstLineDetectionPattern = '//\s?===' 10 | lastLineDetectionPattern = '//\s?===' 11 | skipLinePattern = '// swift-tools-version' 12 | -------------------------------------------------------------------------------- /config/container-core-images-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "abstract" : "Core image management plugin", 3 | "version": "0.1", 4 | "author": "Apple", 5 | "servicesConfig" : { 6 | "loadAtBoot" : true, 7 | "runAtLoad" : false, 8 | "services" : [ 9 | { 10 | "type" : "core", 11 | "description": "Provide an XPC interface to interact with an image store." 12 | } 13 | ], 14 | "defaultArguments": ["start"] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /licenserc.toml: -------------------------------------------------------------------------------- 1 | additionalHeaders = ["scripts/container-header-style.toml"] 2 | 3 | headerPath = "scripts/license-header.txt" 4 | 5 | includes = [ 6 | "Makefile", 7 | "*.Makefile", 8 | "*.swift", 9 | "*.h", 10 | "*.cpp", 11 | "*.c", 12 | "*.sh", 13 | ] 14 | 15 | excludes = [] 16 | 17 | [git] 18 | attrs = 'enable' 19 | ignore = 'enable' 20 | 21 | [properties] 22 | copyrightOwner = "Apple Inc. and the container project authors" 23 | 24 | [mapping.SWIFT_STYLE] 25 | extensions = ["swift", "c", "h"] 26 | -------------------------------------------------------------------------------- /.github/workflows/pr-label-analysis.yml: -------------------------------------------------------------------------------- 1 | name: PR Label Analysis 2 | 3 | on: 4 | pull_request: 5 | types: [opened] 6 | 7 | permissions: 8 | contents: read 9 | 10 | jobs: 11 | analyze: 12 | name: Analyze PR for labeling 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - name: Save PR metadata 17 | run: | 18 | mkdir -p ./pr-metadata 19 | echo "${{ github.event.pull_request.number }}" > ./pr-metadata/pr-number.txt 20 | 21 | - name: Upload PR metadata as artifact 22 | uses: actions/upload-artifact@v4 23 | with: 24 | name: pr-metadata-${{ github.event.pull_request.number }} 25 | path: pr-metadata/ 26 | retention-days: 1 -------------------------------------------------------------------------------- /scripts/license-header.txt: -------------------------------------------------------------------------------- 1 | Copyright ©{{ " " }}{%- set created = attrs.git_file_created_year or attrs.disk_file_created_year -%}{%- set modified = attrs.git_file_modified_year or created -%}{%- if created != modified -%} {{created}}-{{modified}}{%- else -%}{{created}}{%- endif -%}{{ " " }}{{ props["copyrightOwner"] }}. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | https://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /Sources/CAuditToken/AuditToken.c: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | // This file is required for Xcode to generate `CAuditToken.o`. 18 | -------------------------------------------------------------------------------- /Sources/ContainerClient/Core/Constants.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | public enum Constants { 18 | public static let keychainID = "com.apple.container.registry" 19 | } 20 | -------------------------------------------------------------------------------- /Sources/CAuditToken/include/AuditToken.h: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | #include 18 | #include 19 | 20 | void xpc_dictionary_get_audit_token(xpc_object_t xdict, audit_token_t *token); 21 | -------------------------------------------------------------------------------- /scripts/ensure-hawkeye-exists.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright © 2025 Apple Inc. and the container project authors. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # https://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | echo "Checking existence of hawkeye..." 17 | 18 | if command -v .local/bin/hawkeye >/dev/null 2>&1; then 19 | echo "hawkeye found!" 20 | else 21 | echo "hawkeye not found in PATH" 22 | echo "please install hawkeye. For convenience, you can run scripts/install-hawkeye.sh" 23 | exit 1 24 | fi 25 | -------------------------------------------------------------------------------- /Sources/SocketForwarder/SocketForwarder.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import NIO 18 | 19 | public protocol SocketForwarder: Sendable { 20 | func run() throws -> EventLoopFuture 21 | } 22 | -------------------------------------------------------------------------------- /scripts/install-hawkeye.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright © 2025 Apple Inc. and the container project authors. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # https://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | if command -v .local/bin/hawkeye >/dev/null 2>&1; then 17 | echo "hawkeye already installed" 18 | else 19 | echo "Installing hawkeye" 20 | export VERSION=v6.1.0 21 | curl --proto '=https' --tlsv1.2 -LsSf https://github.com/korandoru/hawkeye/releases/download/${VERSION}/hawkeye-installer.sh | CARGO_HOME=.local sh -s -- --no-modify-path 22 | fi 23 | -------------------------------------------------------------------------------- /Sources/ContainerClient/Array+Dedupe.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | extension Array where Element: Hashable { 18 | func dedupe() -> [Element] { 19 | var elems = Set() 20 | return filter { elems.insert($0).inserted } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/02-feature.yml: -------------------------------------------------------------------------------- 1 | name: Feature or enhancement request 2 | description: File a request for a feature or enhancement 3 | title: "[Request]: " 4 | type: "Feature" 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | Thanks for contributing to the container project! 10 | - type: textarea 11 | id: request 12 | attributes: 13 | label: Feature or enhancement request details 14 | 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. 15 | validations: 16 | required: true 17 | - type: checkboxes 18 | id: terms 19 | attributes: 20 | label: Code of Conduct 21 | description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/apple/.github/blob/main/CODE_OF_CONDUCT.md). 22 | options: 23 | - label: I agree to follow this project's Code of Conduct 24 | required: true 25 | -------------------------------------------------------------------------------- /Sources/Services/ContainerNetworkService/NetworkKeys.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | public enum NetworkKeys: String { 18 | case additionalData 19 | case allocatorDisabled 20 | case attachment 21 | case hostname 22 | case macAddress 23 | case network 24 | case state 25 | } 26 | -------------------------------------------------------------------------------- /Sources/TerminalProgress/StandardError.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import Foundation 18 | 19 | struct StandardError { 20 | func write(_ string: String) { 21 | if let data = string.data(using: .utf8) { 22 | FileHandle.standardError.write(data) 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Sources/ContainerCommands/Codable+JSON.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import Foundation 18 | 19 | extension [any Codable] { 20 | func jsonArray() throws -> String { 21 | "[\(try self.map { String(data: try JSONEncoder().encode($0), encoding: .utf8)! }.joined(separator: ","))]" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Sources/ContainerClient/Arch.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | public enum Arch: String { 18 | case arm64, amd64 19 | 20 | public static func hostArchitecture() -> Arch { 21 | #if arch(arm64) 22 | return .arm64 23 | #elseif arch(x86_64) 24 | return .amd64 25 | #endif 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Sources/ContainerClient/Core/ContainerCreateOptions.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | public struct ContainerCreateOptions: Codable, Sendable { 18 | public let autoRemove: Bool 19 | 20 | public init(autoRemove: Bool) { 21 | self.autoRemove = autoRemove 22 | } 23 | 24 | public static let `default` = ContainerCreateOptions(autoRemove: false) 25 | 26 | } 27 | -------------------------------------------------------------------------------- /Tests/ContainerPluginTests/CommandLine+ExecutableTest.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import Foundation 18 | import Testing 19 | 20 | @testable import ContainerPlugin 21 | 22 | struct CommandLineExecutableTest { 23 | @Test 24 | func testCLIPluginConfigLoad() async throws { 25 | #expect(CommandLine.executablePathUrl.lastPathComponent == "swiftpm-testing-helper") 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Sources/CVersion/Version.c: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | #include "Version.h" 18 | 19 | const char* get_git_commit() { 20 | return GIT_COMMIT; 21 | } 22 | 23 | const char* get_release_version() { 24 | return RELEASE_VERSION; 25 | } 26 | 27 | const char* get_swift_containerization_version() { 28 | return CZ_VERSION; 29 | } 30 | 31 | const char* get_container_builder_shim_version() { 32 | return BUILDER_SHIM_VERSION; 33 | } 34 | -------------------------------------------------------------------------------- /scripts/ensure-container-stopped.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash -f 2 | # Copyright © 2025 Apple Inc. and the container project authors. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # https://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | domain_string="" 17 | 18 | launchd_domain=$(launchctl managername) 19 | 20 | if [[ "$launchd_domain" == "System" ]]; then 21 | domain_string="system" 22 | elif [[ "$launchd_domain" == "Aqua" ]]; then 23 | domain_string="gui/$(id -u)" 24 | elif [[ "$launchd_domain" == "Background" ]]; then 25 | domain_string="user/$(id -u)" 26 | else 27 | echo "Unsupported launchd domain. Exiting" 28 | exit 1 29 | fi 30 | 31 | launchctl list | grep -e 'com\.apple\.container\W' | awk '{print $3}' | xargs -I % launchctl bootout $domain_string/% 32 | -------------------------------------------------------------------------------- /Tests/SocketForwarderTests/UDPEchoHandler.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import NIO 18 | 19 | final class UDPEchoHandler: ChannelInboundHandler { 20 | 21 | typealias InboundIn = AddressedEnvelope 22 | 23 | func channelRead(context: ChannelHandlerContext, data: NIOAny) { 24 | context.writeAndFlush(data, promise: nil) 25 | } 26 | 27 | func errorCaught(context: ChannelHandlerContext, error: Error) { 28 | context.close(promise: nil) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /.github/workflows/docs-release.yml: -------------------------------------------------------------------------------- 1 | # Manual workflow for releasing docs ad-hoc. Workflow can only be run for main or release branches. 2 | # Workflow does NOT publish a release of container. 3 | name: Deploy application website 4 | 5 | permissions: 6 | contents: read 7 | 8 | on: 9 | workflow_dispatch: 10 | 11 | jobs: 12 | checkBranch: 13 | runs-on: ubuntu-latest 14 | if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags') || startsWith(github.ref, 'refs/heads/release') 15 | steps: 16 | - name: Branch validation 17 | run: echo "Branch ${{ github.ref_name }} is allowed" 18 | 19 | buildSite: 20 | name: Build application website 21 | needs: checkBranch 22 | uses: ./.github/workflows/common.yml 23 | secrets: inherit 24 | permissions: 25 | contents: read 26 | packages: write 27 | pages: write 28 | 29 | deployDocs: 30 | runs-on: ubuntu-latest 31 | needs: [checkBranch, buildSite] 32 | permissions: 33 | contents: read 34 | pages: write 35 | id-token: write 36 | 37 | environment: 38 | name: github-pages 39 | url: ${{ steps.deployment.outputs.page_url }} 40 | 41 | steps: 42 | - name: Deploy to GitHub Pages 43 | id: deployment 44 | uses: actions/deploy-pages@v4 45 | -------------------------------------------------------------------------------- /Sources/ContainerClient/Core/RuntimeStatus.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import Foundation 18 | 19 | /// Runtime status for a sandbox or container. 20 | public enum RuntimeStatus: String, CaseIterable, Sendable, Codable { 21 | /// The object is in an unknown status. 22 | case unknown 23 | /// The object is currently stopped. 24 | case stopped 25 | /// The object is currently running. 26 | case running 27 | /// The object is currently stopping. 28 | case stopping 29 | } 30 | -------------------------------------------------------------------------------- /Sources/DNSServer/DNSHandler.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | /// Protocol for implementing custom DNS handlers. 18 | public protocol DNSHandler { 19 | /// Attempt to answer a DNS query 20 | /// - Parameter query: the query message 21 | /// - Throws: a server failure occurred during the query 22 | /// - Returns: The response message for the query, or nil if the request 23 | /// is not within the scope of the handler. 24 | func answer(query: Message) async throws -> Message? 25 | } 26 | -------------------------------------------------------------------------------- /Sources/ContainerCommands/Container/ProcessUtils.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ContainerClient 18 | import Containerization 19 | import ContainerizationError 20 | import ContainerizationOS 21 | import Foundation 22 | 23 | extension Application { 24 | static func ensureRunning(container: ClientContainer) throws { 25 | if container.status != .running { 26 | throw ContainerizationError(.invalidState, message: "container \(container.id) is not running") 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Sources/ContainerCommands/System/SystemKernel.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ArgumentParser 18 | 19 | extension Application { 20 | public struct SystemKernel: AsyncParsableCommand { 21 | public init() {} 22 | public static let configuration = CommandConfiguration( 23 | commandName: "kernel", 24 | abstract: "Manage the default kernel configuration", 25 | subcommands: [ 26 | KernelSet.self 27 | ] 28 | ) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Tests/SocketForwarderTests/TCPEchoHandler.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import NIO 18 | 19 | final class TCPEchoHandler: ChannelInboundHandler { 20 | 21 | typealias InboundIn = ByteBuffer 22 | typealias OutboundOut = ByteBuffer 23 | 24 | func channelRead(context: ChannelHandlerContext, data: NIOAny) { 25 | context.writeAndFlush(data, promise: nil) 26 | } 27 | 28 | func errorCaught(context: ChannelHandlerContext, error: Error) { 29 | context.close(promise: nil) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Sources/ContainerClient/Core/ContainerStopOptions.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import Foundation 18 | 19 | public struct ContainerStopOptions: Sendable, Codable { 20 | public let timeoutInSeconds: Int32 21 | public let signal: Int32 22 | 23 | public static let `default` = ContainerStopOptions( 24 | timeoutInSeconds: 5, 25 | signal: SIGTERM 26 | ) 27 | 28 | public init(timeoutInSeconds: Int32, signal: Int32) { 29 | self.timeoutInSeconds = timeoutInSeconds 30 | self.signal = signal 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Sources/Services/ContainerNetworkService/Network.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ContainerXPC 18 | 19 | /// Defines common characteristics and operations for a network. 20 | public protocol Network: Sendable { 21 | // Contains network attributes while the network is running 22 | var state: NetworkState { get async } 23 | 24 | // Use implementation-dependent network attributes 25 | nonisolated func withAdditionalData(_ handler: (XPCMessage?) throws -> Void) throws 26 | 27 | // Start the network 28 | func start() async throws 29 | } 30 | -------------------------------------------------------------------------------- /Sources/ContainerCommands/Registry/RegistryCommand.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ArgumentParser 18 | 19 | extension Application { 20 | public struct RegistryCommand: AsyncParsableCommand { 21 | public static let configuration = CommandConfiguration( 22 | commandName: "registry", 23 | abstract: "Manage registry logins", 24 | subcommands: [ 25 | Login.self, 26 | Logout.self, 27 | ], 28 | aliases: ["r"] 29 | ) 30 | 31 | public init() {} 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Sources/CVersion/include/Version.h: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | #ifndef CZ_VERSION 18 | #define CZ_VERSION "latest" 19 | #endif 20 | 21 | #ifndef GIT_COMMIT 22 | #define GIT_COMMIT "unspecified" 23 | #endif 24 | 25 | #ifndef RELEASE_VERSION 26 | #define RELEASE_VERSION "0.0.0" 27 | #endif 28 | 29 | #ifndef BUILDER_SHIM_VERSION 30 | #define BUILDER_SHIM_VERSION "0.0.0" 31 | #endif 32 | 33 | const char* get_git_commit(); 34 | 35 | const char* get_release_version(); 36 | 37 | const char* get_swift_containerization_version(); 38 | 39 | const char* get_container_builder_shim_version(); 40 | -------------------------------------------------------------------------------- /Sources/ContainerCommands/System/SystemDNS.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ArgumentParser 18 | import ContainerizationError 19 | import Foundation 20 | 21 | extension Application { 22 | public struct SystemDNS: AsyncParsableCommand { 23 | public init() {} 24 | public static let configuration = CommandConfiguration( 25 | commandName: "dns", 26 | abstract: "Manage local DNS domains", 27 | subcommands: [ 28 | DNSCreate.self, 29 | DNSDelete.self, 30 | DNSList.self, 31 | ] 32 | ) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/SocketForwarder/SocketForwarderResult.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import NIO 18 | 19 | public struct SocketForwarderResult: Sendable { 20 | private let channel: any Channel 21 | 22 | public init(channel: Channel) { 23 | self.channel = channel 24 | } 25 | 26 | public var proxyAddress: SocketAddress? { self.channel.localAddress } 27 | 28 | public func close() { 29 | self.channel.eventLoop.execute { 30 | _ = channel.close() 31 | } 32 | } 33 | 34 | public func wait() async throws { 35 | try await self.channel.closeFuture.get() 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Sources/DNSServer/Handlers/CompositeResolver.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | /// Delegates a query sequentially to handlers until one provides a response. 18 | public struct CompositeResolver: DNSHandler { 19 | private let handlers: [DNSHandler] 20 | 21 | public init(handlers: [DNSHandler]) { 22 | self.handlers = handlers 23 | } 24 | 25 | public func answer(query: Message) async throws -> Message? { 26 | for handler in self.handlers { 27 | if let response = try await handler.answer(query: query) { 28 | return response 29 | } 30 | } 31 | 32 | return nil 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/ContainerCommands/Network/NetworkCommand.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ArgumentParser 18 | 19 | extension Application { 20 | public struct NetworkCommand: AsyncParsableCommand { 21 | public static let configuration = CommandConfiguration( 22 | commandName: "network", 23 | abstract: "Manage container networks", 24 | subcommands: [ 25 | NetworkCreate.self, 26 | NetworkDelete.self, 27 | NetworkList.self, 28 | NetworkInspect.self, 29 | ], 30 | aliases: ["n"] 31 | ) 32 | 33 | public init() {} 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Sources/ContainerCommands/Builder/Builder.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ArgumentParser 18 | 19 | extension Application { 20 | public struct BuilderCommand: AsyncParsableCommand { 21 | public init() {} 22 | 23 | public static let builderResourceDir = "builder" 24 | public static let configuration = CommandConfiguration( 25 | commandName: "builder", 26 | abstract: "Manage an image builder instance", 27 | subcommands: [ 28 | BuilderStart.self, 29 | BuilderStatus.self, 30 | BuilderStop.self, 31 | BuilderDelete.self, 32 | ]) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/ContainerCommands/Volume/VolumeCommand.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ArgumentParser 18 | 19 | extension Application { 20 | public struct VolumeCommand: AsyncParsableCommand { 21 | public static let configuration = CommandConfiguration( 22 | commandName: "volume", 23 | abstract: "Manage container volumes", 24 | subcommands: [ 25 | VolumeCreate.self, 26 | VolumeDelete.self, 27 | VolumeList.self, 28 | VolumeInspect.self, 29 | VolumePrune.self, 30 | ], 31 | aliases: ["v"] 32 | ) 33 | 34 | public init() {} 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Sources/ContainerVersion/Bundle+AppBundle.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import Foundation 18 | 19 | /// Retrieve the application bundle for a path that refers to a macOS executable. 20 | extension Bundle { 21 | public static func appBundle(executableURL: URL) -> Bundle? { 22 | let resolvedURL = executableURL.resolvingSymlinksInPath() 23 | let macOSURL = resolvedURL.deletingLastPathComponent() 24 | let contentsURL = macOSURL.deletingLastPathComponent() 25 | let bundleURL = contentsURL.deletingLastPathComponent() 26 | if bundleURL.pathExtension == "app" { 27 | return Bundle(url: bundleURL) 28 | } 29 | return nil 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Sources/Services/ContainerNetworkService/NetworkMode.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | /// Networking mode that applies to client containers. 18 | public enum NetworkMode: String, Codable, Sendable { 19 | /// NAT networking mode. 20 | /// Containers do not have routable IPs, and the host performs network 21 | /// address translation to allow containers to reach external services. 22 | case nat = "nat" 23 | } 24 | 25 | extension NetworkMode { 26 | public init() { 27 | self = .nat 28 | } 29 | 30 | public init?(_ value: String) { 31 | switch value.lowercased() { 32 | case "nat": self = .nat 33 | default: return nil 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security disclosure process 2 | 3 | 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/container/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. 4 | 5 | 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. 6 | 7 | Keep these additional guidelines in mind when submitting your report: 8 | 9 | * Reports concerning known, publicly disclosed CVEs can be submitted as normal issues to this project. 10 | * Output from automated security scans or fuzzers MUST include additional context demonstrating the vulnerability with a proof of concept or working exploit. 11 | * 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. 12 | 13 | While we welcome reports for open source software projects, they are not eligible for Apple Security Bounties. 14 | -------------------------------------------------------------------------------- /Sources/CLI/ContainerCLI.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ArgumentParser 18 | import ContainerClient 19 | import ContainerCommands 20 | 21 | @main 22 | public struct ContainerCLI: AsyncParsableCommand { 23 | public init() {} 24 | 25 | @Argument(parsing: .captureForPassthrough) 26 | var arguments: [String] = [] 27 | 28 | public static let configuration = Application.configuration 29 | 30 | public static func main() async throws { 31 | try await Application.main() 32 | } 33 | 34 | public func run() async throws { 35 | var application = try Application.parse(arguments) 36 | try application.validate() 37 | try application.run() 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Sources/ContainerCommands/System/SystemProperty.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ArgumentParser 18 | import ContainerPersistence 19 | import ContainerizationError 20 | import Foundation 21 | 22 | extension Application { 23 | public struct SystemProperty: AsyncParsableCommand { 24 | public init() {} 25 | 26 | public static let configuration = CommandConfiguration( 27 | commandName: "property", 28 | abstract: "Manage system property values", 29 | subcommands: [ 30 | PropertyClear.self, 31 | PropertyGet.self, 32 | PropertyList.self, 33 | PropertySet.self, 34 | ] 35 | ) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Sources/ContainerPlugin/ApplicationRoot.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import Foundation 18 | 19 | /// Provides the application data root path. 20 | public struct ApplicationRoot { 21 | public static let environmentName = "CONTAINER_APP_ROOT" 22 | 23 | public static let defaultURL = FileManager.default.urls( 24 | for: .applicationSupportDirectory, 25 | in: .userDomainMask 26 | ).first!.appendingPathComponent("com.apple.container") 27 | 28 | private static let envPath = ProcessInfo.processInfo.environment[Self.environmentName] 29 | 30 | public static let url = envPath.map { URL(fileURLWithPath: $0) } ?? defaultURL 31 | 32 | public static let path = url.path(percentEncoded: false) 33 | } 34 | -------------------------------------------------------------------------------- /Sources/ContainerPlugin/InstallRoot.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ContainerVersion 18 | import Foundation 19 | 20 | /// Provides the application installation root path. 21 | public struct InstallRoot { 22 | public static let environmentName = "CONTAINER_INSTALL_ROOT" 23 | 24 | public static let defaultURL = CommandLine.executablePathUrl 25 | .deletingLastPathComponent() 26 | .appendingPathComponent("..") 27 | .standardized 28 | 29 | private static let envPath = ProcessInfo.processInfo.environment[Self.environmentName] 30 | 31 | public static let url = envPath.map { URL(fileURLWithPath: $0) } ?? defaultURL 32 | 33 | public static let path = url.path(percentEncoded: false) 34 | } 35 | -------------------------------------------------------------------------------- /Sources/ContainerClient/Core/ImageDescription.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ContainerizationError 18 | import ContainerizationOCI 19 | 20 | /// A type that represents an OCI image that can be used with sandboxes or containers. 21 | public struct ImageDescription: Sendable, Codable { 22 | /// The public reference/name of the image. 23 | public let reference: String 24 | /// The descriptor of the image. 25 | public let descriptor: Descriptor 26 | 27 | public var digest: String { descriptor.digest } 28 | public var mediaType: String { descriptor.mediaType } 29 | 30 | public init(reference: String, descriptor: Descriptor) { 31 | self.reference = reference 32 | self.descriptor = descriptor 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/Services/ContainerNetworkService/NetworkRoutes.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | public enum NetworkRoutes: String { 18 | /// Return the current state of the network. 19 | case state = "com.apple.container.network/state" 20 | /// Allocates parameters for attaching a sandbox to the network. 21 | case allocate = "com.apple.container.network/allocate" 22 | /// Deallocates parameters for attaching a sandbox to the network. 23 | case deallocate = "com.apple.container.network/deallocate" 24 | /// Disables the allocator if no sandboxes are attached. 25 | case disableAllocator = "com.apple.container.network/disableAllocator" 26 | /// Retrieves the allocation for a hostname. 27 | case lookup = "com.apple.container.network/lookup" 28 | } 29 | -------------------------------------------------------------------------------- /Sources/ContainerClient/SandboxSnapshot.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ContainerNetworkService 18 | 19 | /// A snapshot of a sandbox and its resources. 20 | public struct SandboxSnapshot: Codable, Sendable { 21 | /// The runtime status of the sandbox. 22 | public var status: RuntimeStatus 23 | /// Network attachments for the sandbox. 24 | public var networks: [Attachment] 25 | /// Containers placed in the sandbox. 26 | public var containers: [ContainerSnapshot] 27 | 28 | public init( 29 | status: RuntimeStatus, 30 | networks: [Attachment], 31 | containers: [ContainerSnapshot] 32 | ) { 33 | self.status = status 34 | self.networks = networks 35 | self.containers = containers 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Sources/Helpers/RuntimeLinux/IsolatedInterfaceStrategy.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ContainerNetworkService 18 | import ContainerSandboxService 19 | import ContainerXPC 20 | import Containerization 21 | 22 | /// Isolated container network interface strategy. This strategy prohibits 23 | /// container to container networking, but it is the only approach that 24 | /// works for macOS Sequoia. 25 | struct IsolatedInterfaceStrategy: InterfaceStrategy { 26 | public func toInterface(attachment: Attachment, interfaceIndex: Int, additionalData: XPCMessage?) -> Interface { 27 | let gateway = interfaceIndex == 0 ? attachment.gateway : nil 28 | return NATInterface(address: attachment.address, gateway: gateway, macAddress: attachment.macAddress) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Sources/TerminalProgress/ProgressTheme.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | /// A theme for progress bar. 18 | public protocol ProgressTheme: Sendable { 19 | /// The icons used to represent a spinner. 20 | var spinner: [String] { get } 21 | /// The icon used to represent a progress bar. 22 | var bar: String { get } 23 | /// The icon used to indicate that a progress bar finished. 24 | var done: String { get } 25 | } 26 | 27 | public struct DefaultProgressTheme: ProgressTheme { 28 | public let spinner = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"] 29 | public let bar = "█" 30 | public let done = "✔" 31 | } 32 | 33 | extension ProgressTheme { 34 | func getSpinnerIcon(_ iteration: Int) -> String { 35 | spinner[iteration % spinner.count] 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Sources/ContainerCommands/Image/ImageCommand.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ArgumentParser 18 | 19 | extension Application { 20 | public struct ImageCommand: AsyncParsableCommand { 21 | public init() {} 22 | 23 | public static let configuration = CommandConfiguration( 24 | commandName: "image", 25 | abstract: "Manage images", 26 | subcommands: [ 27 | ImageDelete.self, 28 | ImageInspect.self, 29 | ImageList.self, 30 | ImageLoad.self, 31 | ImagePrune.self, 32 | ImagePull.self, 33 | ImagePush.self, 34 | ImageSave.self, 35 | ImageTag.self, 36 | ], 37 | aliases: ["i"] 38 | ) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Sources/ContainerCommands/Registry/Logout.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ArgumentParser 18 | import ContainerClient 19 | import Containerization 20 | import ContainerizationOCI 21 | 22 | extension Application { 23 | public struct Logout: AsyncParsableCommand { 24 | public init() {} 25 | public static let configuration = CommandConfiguration( 26 | abstract: "Log out from a registry") 27 | 28 | @OptionGroup 29 | var global: Flags.Global 30 | 31 | @Argument(help: "Registry server name") 32 | var registry: String 33 | 34 | public func run() async throws { 35 | let keychain = KeychainHelper(id: Constants.keychainID) 36 | let r = Reference.resolveDomain(domain: registry) 37 | try keychain.delete(domain: r) 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Sources/ContainerCommands/System/SystemCommand.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ArgumentParser 18 | 19 | extension Application { 20 | public struct SystemCommand: AsyncParsableCommand { 21 | public init() {} 22 | public static let configuration = CommandConfiguration( 23 | commandName: "system", 24 | abstract: "Manage system components", 25 | subcommands: [ 26 | SystemDF.self, 27 | SystemDNS.self, 28 | SystemKernel.self, 29 | SystemLogs.self, 30 | SystemProperty.self, 31 | SystemStart.self, 32 | SystemStatus.self, 33 | SystemStop.self, 34 | SystemVersion.self, 35 | ], 36 | aliases: ["s"] 37 | ) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Sources/ContainerClient/Core/SystemHealth.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import Foundation 18 | 19 | /// Snapshot of the health of container services and resources 20 | public struct SystemHealth: Sendable, Codable { 21 | /// The full pathname of the application data root. 22 | public let appRoot: URL 23 | 24 | /// The full pathname of the application install root. 25 | public let installRoot: URL 26 | 27 | /// The release version of the container services. 28 | public let apiServerVersion: String 29 | 30 | /// The Git commit ID for the container services. 31 | public let apiServerCommit: String 32 | 33 | /// The build type of the API server (debug|release). 34 | public let apiServerBuild: String 35 | 36 | /// The app name label returned by the server. 37 | public let apiServerAppName: String 38 | } 39 | -------------------------------------------------------------------------------- /Sources/ContainerClient/Core/PublishSocket.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import Foundation 18 | import SystemPackage 19 | 20 | /// Represents a socket that should be published from container to host. 21 | public struct PublishSocket: Sendable, Codable { 22 | /// The path to the socket in the container. 23 | public var containerPath: URL 24 | 25 | /// The path where the socket should appear on the host. 26 | public var hostPath: URL 27 | 28 | /// File permissions for the socket on the host. 29 | public var permissions: FilePermissions? 30 | 31 | public init( 32 | containerPath: URL, 33 | hostPath: URL, 34 | permissions: FilePermissions? = nil 35 | ) { 36 | self.containerPath = containerPath 37 | self.hostPath = hostPath 38 | self.permissions = permissions 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: container project - release build 2 | 3 | permissions: 4 | contents: read 5 | 6 | on: 7 | push: 8 | tags: 9 | - "[0-9]+\\.[0-9]+\\.[0-9]+" 10 | 11 | jobs: 12 | build: 13 | name: Invoke build and release 14 | uses: ./.github/workflows/common.yml 15 | with: 16 | release: true 17 | secrets: inherit 18 | permissions: 19 | contents: read 20 | packages: read 21 | pages: write 22 | 23 | release: 24 | if: startsWith(github.ref, 'refs/tags/') 25 | name: Publish release 26 | timeout-minutes: 30 27 | needs: build 28 | runs-on: ubuntu-latest 29 | permissions: 30 | contents: write 31 | packages: read 32 | pages: write 33 | steps: 34 | - name: Download artifacts 35 | uses: actions/download-artifact@v4 36 | with: 37 | path: outputs 38 | 39 | - name: Verify artifacts exist 40 | run: | 41 | echo "Checking for expected artifacts..." 42 | ls -la outputs/container-package/ 43 | test -e outputs/container-package/*.zip || (echo "Missing .zip file!" && exit 1) 44 | test -e outputs/container-package/*.pkg || (echo "Missing .pkg file!" && exit 1) 45 | 46 | - name: Create release 47 | uses: softprops/action-gh-release@v2 48 | with: 49 | token: ${{ github.token }} 50 | name: ${{ github.ref_name }}-prerelease 51 | draft: true 52 | make_latest: false 53 | prerelease: true 54 | fail_on_unmatched_files: true 55 | files: | 56 | outputs/container-package/*.zip 57 | outputs/container-package/*.pkg 58 | -------------------------------------------------------------------------------- /.github/workflows/release-build.yml: -------------------------------------------------------------------------------- 1 | name: container project - release build 2 | 3 | permissions: 4 | contents: read 5 | 6 | on: 7 | push: 8 | tags: 9 | - "[0-9]+\\.[0-9]+\\.[0-9]+" 10 | 11 | jobs: 12 | build: 13 | name: Invoke build and release 14 | uses: ./.github/workflows/common.yml 15 | with: 16 | release: true 17 | secrets: inherit 18 | permissions: 19 | contents: read 20 | packages: read 21 | pages: write 22 | 23 | release: 24 | if: startsWith(github.ref, 'refs/tags/') 25 | name: Publish release 26 | timeout-minutes: 30 27 | needs: build 28 | runs-on: ubuntu-latest 29 | permissions: 30 | contents: write 31 | packages: read 32 | pages: write 33 | steps: 34 | - name: Download artifacts 35 | uses: actions/download-artifact@v4 36 | with: 37 | path: outputs 38 | 39 | - name: Verify artifacts exist 40 | run: | 41 | echo "Checking for expected artifacts..." 42 | ls -la outputs/container-package/ 43 | test -e outputs/container-package/*.zip || (echo "Missing .zip file!" && exit 1) 44 | test -e outputs/container-package/*.pkg || (echo "Missing .pkg file!" && exit 1) 45 | 46 | - name: Create release 47 | uses: softprops/action-gh-release@v2 48 | with: 49 | token: ${{ github.token }} 50 | name: ${{ github.ref_name }}-prerelease 51 | draft: true 52 | make_latest: false 53 | prerelease: true 54 | fail_on_unmatched_files: true 55 | files: | 56 | outputs/container-package/*.zip 57 | outputs/container-package/*.pkg 58 | -------------------------------------------------------------------------------- /Sources/TerminalProgress/Int64+Formatted.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import Foundation 18 | 19 | extension Int64 { 20 | func formattedSize() -> String { 21 | let formattedSize = ByteCountFormatter.string(fromByteCount: self, countStyle: .binary) 22 | return formattedSize 23 | } 24 | 25 | func formattedSizeSpeed(from startTime: DispatchTime) -> String { 26 | let elapsedTimeNanoseconds = DispatchTime.now().uptimeNanoseconds - startTime.uptimeNanoseconds 27 | let elapsedTimeSeconds = Double(elapsedTimeNanoseconds) / 1_000_000_000 28 | guard elapsedTimeSeconds > 0 else { 29 | return "0 B/s" 30 | } 31 | 32 | let speed = Double(self) / elapsedTimeSeconds 33 | let formattedSpeed = ByteCountFormatter.string(fromByteCount: Int64(speed), countStyle: .binary) 34 | return "\(formattedSpeed)/s" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Sources/ContainerClient/Core/ContainerSnapshot.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ContainerNetworkService 18 | 19 | /// A snapshot of a container along with its configuration 20 | /// and any runtime state information. 21 | public struct ContainerSnapshot: Codable, Sendable { 22 | /// The configuration of the container. 23 | public var configuration: ContainerConfiguration 24 | /// The runtime status of the container. 25 | public var status: RuntimeStatus 26 | /// Network interfaces attached to the sandbox that are provided to the container. 27 | public var networks: [Attachment] 28 | 29 | public init( 30 | configuration: ContainerConfiguration, 31 | status: RuntimeStatus, 32 | networks: [Attachment] 33 | ) { 34 | self.configuration = configuration 35 | self.status = status 36 | self.networks = networks 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Sources/Services/ContainerImagesService/Client/ImageServiceXPCRoutes.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | #if os(macOS) 18 | import Foundation 19 | import ContainerXPC 20 | 21 | public enum ImagesServiceXPCRoute: String { 22 | case imageList 23 | case imagePull 24 | case imagePush 25 | case imageTag 26 | case imageBuild 27 | case imageDelete 28 | case imageSave 29 | case imageLoad 30 | case imageCleanupOrphanedBlobs 31 | case imageDiskUsage 32 | 33 | case contentGet 34 | case contentDelete 35 | case contentClean 36 | case contentIngestStart 37 | case contentIngestComplete 38 | case contentIngestCancel 39 | 40 | case imageUnpack 41 | case snapshotDelete 42 | case snapshotGet 43 | } 44 | 45 | extension XPCMessage { 46 | public init(route: ImagesServiceXPCRoute) { 47 | self.init(route: route.rawValue) 48 | } 49 | } 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /Sources/TerminalProgress/ProgressUpdate.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | public enum ProgressUpdateEvent: Sendable { 18 | case setDescription(String) 19 | case setSubDescription(String) 20 | case setItemsName(String) 21 | case addTasks(Int) 22 | case setTasks(Int) 23 | case addTotalTasks(Int) 24 | case setTotalTasks(Int) 25 | case addItems(Int) 26 | case setItems(Int) 27 | case addTotalItems(Int) 28 | case setTotalItems(Int) 29 | case addSize(Int64) 30 | case setSize(Int64) 31 | case addTotalSize(Int64) 32 | case setTotalSize(Int64) 33 | case custom(String) 34 | } 35 | 36 | public typealias ProgressUpdateHandler = @Sendable (_ events: [ProgressUpdateEvent]) async -> Void 37 | 38 | public protocol ProgressAdapter { 39 | associatedtype T 40 | static func handler(from progressUpdate: ProgressUpdateHandler?) -> (@Sendable ([T]) async -> Void)? 41 | } 42 | -------------------------------------------------------------------------------- /.github/workflows/pr-label-apply.yml: -------------------------------------------------------------------------------- 1 | name: PR Label Apply 2 | 3 | on: 4 | workflow_run: 5 | workflows: ["PR Label Analysis"] 6 | types: 7 | - completed 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | apply-labels: 14 | name: Apply labels to PR 15 | runs-on: ubuntu-latest 16 | if: ${{ github.event.workflow_run.conclusion == 'success' }} 17 | permissions: 18 | contents: read 19 | pull-requests: write 20 | 21 | steps: 22 | - name: Checkout repository 23 | uses: actions/checkout@v4 24 | 25 | - name: Download PR metadata artifact 26 | uses: actions/download-artifact@v4 27 | with: 28 | github-token: ${{ secrets.GITHUB_TOKEN }} 29 | run-id: ${{ github.event.workflow_run.id }} 30 | pattern: pr-metadata-* 31 | merge-multiple: false 32 | continue-on-error: true 33 | id: download-artifact 34 | 35 | - name: Read PR number 36 | id: pr-number 37 | run: | 38 | METADATA_DIR=$(find . -type d -name "pr-metadata-*" 2>/dev/null | head -n 1) 39 | 40 | if [ -z "$METADATA_DIR" ]; then 41 | echo "No metadata found" 42 | exit 1 43 | fi 44 | 45 | PR_NUMBER=$(cat "${METADATA_DIR}/pr-number.txt") 46 | echo "number=${PR_NUMBER}" >> $GITHUB_OUTPUT 47 | echo "PR Number: ${PR_NUMBER}" 48 | 49 | - name: Apply labels using labeler 50 | uses: actions/labeler@v5 51 | with: 52 | pr-number: ${{ steps.pr-number.outputs.number }} 53 | repo-token: ${{ secrets.GITHUB_TOKEN }} 54 | configuration-path: .github/labeler.yml 55 | sync-labels: true -------------------------------------------------------------------------------- /Tests/CLITests/Subcommands/Build/CLIBuilderLifecycleTest.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import Foundation 18 | import Testing 19 | 20 | extension TestCLIBuildBase { 21 | class CLIBuilderLifecycleTest: TestCLIBuildBase { 22 | override init() throws {} 23 | @Test func testBuilderStartStopCommand() throws { 24 | #expect(throws: Never.self) { 25 | try self.builderStart() 26 | try self.waitForBuilderRunning() 27 | let status = try self.getContainerStatus("buildkit") 28 | #expect(status == "running", "BuildKit container is not running") 29 | } 30 | #expect(throws: Never.self) { 31 | try self.builderStop() 32 | let status = try self.getContainerStatus("buildkit") 33 | #expect(status == "stopped", "BuildKit container is not stopped") 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Sources/Helpers/APIServer/APIServer.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ArgumentParser 18 | import ContainerLog 19 | import ContainerVersion 20 | import Logging 21 | 22 | @main 23 | struct APIServer: AsyncParsableCommand { 24 | static let configuration = CommandConfiguration( 25 | commandName: "container-apiserver", 26 | abstract: "Container management API server", 27 | version: ReleaseVersion.singleLine(appName: "container-apiserver"), 28 | subcommands: [Start.self], 29 | ) 30 | 31 | static func setupLogger(debug: Bool) -> Logger { 32 | LoggingSystem.bootstrap { label in 33 | OSLogHandler( 34 | label: label, 35 | category: "APIServer" 36 | ) 37 | } 38 | var log = Logger(label: "com.apple.container") 39 | if debug { 40 | log.logLevel = .debug 41 | } 42 | return log 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Sources/ContainerVersion/CommandLine+Executable.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import Foundation 18 | 19 | extension CommandLine { 20 | public static var executablePathUrl: URL { 21 | /// _NSGetExecutablePath with a zero-length buffer returns the needed buffer length 22 | var bufferSize: Int32 = 0 23 | var buffer = [CChar](repeating: 0, count: Int(bufferSize)) 24 | _ = _NSGetExecutablePath(&buffer, &bufferSize) 25 | 26 | /// Create the buffer and get the path 27 | buffer = [CChar](repeating: 0, count: Int(bufferSize)) 28 | guard _NSGetExecutablePath(&buffer, &bufferSize) == 0 else { 29 | fatalError("unexpected: failed to get executable path") 30 | } 31 | 32 | /// Return the path with the executable file component removed the last component and 33 | let executablePath = String(cString: &buffer) 34 | return URL(filePath: executablePath) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /scripts/install-init.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash -e 2 | # Copyright © 2025 Apple Inc. and the container project authors. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # https://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | SWIFT="/usr/bin/swift" 17 | IMAGE_NAME="vminit:latest" 18 | DEST_DIR="${1:-$(git rev-parse --show-toplevel)/bin}" 19 | mkdir -p "${DEST_DIR}" 20 | 21 | CONTAINERIZATION_VERSION="$(${SWIFT} package show-dependencies --format json | jq -r '.dependencies[] | select(.identity == "containerization") | .version')" 22 | if [ "${CONTAINERIZATION_VERSION}" == "unspecified" ] ; then 23 | CONTAINERIZATION_PATH="$(${SWIFT} package show-dependencies --format json | jq -r '.dependencies[] | select(.identity == "containerization") | .path')" 24 | if [ ! -d "${CONTAINERIZATION_PATH}" ] ; then 25 | echo "editable containerization directory at ${CONTAINERIZATION_PATH} does not exist" 26 | exit 1 27 | fi 28 | echo "Creating InitImage" 29 | make -C ${CONTAINERIZATION_PATH} init 30 | ${CONTAINERIZATION_PATH}/bin/cctl images save -o /tmp/init.tar ${IMAGE_NAME} 31 | # Sleep because commands after stop and start are racy. 32 | bin/container system stop && sleep 3 && bin/container system start && sleep 3 33 | bin/container i load -i /tmp/init.tar 34 | rm /tmp/init.tar 35 | fi 36 | -------------------------------------------------------------------------------- /Sources/Services/ContainerSandboxService/InterfaceStrategy.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ContainerNetworkService 18 | import ContainerXPC 19 | import Containerization 20 | 21 | /// A strategy for mapping network attachment information to a network interface. 22 | public protocol InterfaceStrategy: Sendable { 23 | /// Map a client network attachment request to a network interface specification. 24 | /// 25 | /// - Parameters: 26 | /// - attachment: General attachment information that is common 27 | /// for all networks. 28 | /// - interfaceIndex: The zero-based index of the interface. 29 | /// - additionalData: If present, attachment information that is 30 | /// specific for the network to which the container will attach. 31 | /// 32 | /// - Returns: An XPC message with no parameters. 33 | func toInterface(attachment: Attachment, interfaceIndex: Int, additionalData: XPCMessage?) throws -> Interface 34 | } 35 | -------------------------------------------------------------------------------- /Tests/SocketForwarderTests/UDPEchoServer.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import NIO 18 | 19 | struct UDPEchoServer: Sendable { 20 | private let serverAddress: SocketAddress 21 | 22 | private let eventLoopGroup: MultiThreadedEventLoopGroup 23 | 24 | public init(serverAddress: SocketAddress, eventLoopGroup: MultiThreadedEventLoopGroup) { 25 | self.serverAddress = serverAddress 26 | self.eventLoopGroup = eventLoopGroup 27 | } 28 | 29 | public func run() throws -> EventLoopFuture { 30 | let bootstrap = DatagramBootstrap(group: self.eventLoopGroup) 31 | .channelInitializer { channel in 32 | channel.eventLoop.makeCompletedFuture { 33 | try channel.pipeline.syncOperations.addHandler( 34 | UDPEchoHandler() 35 | ) 36 | } 37 | } 38 | 39 | return bootstrap.bind(to: self.serverAddress) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Tests/CLITests/Subcommands/Plugins/TestCLIPluginErrors.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import Testing 18 | 19 | struct TestCLIPluginErrors { 20 | @Test 21 | func testHelpfulMessageWhenPluginsUnavailable() throws { 22 | // Intentionally invoke an unknown plugin command. In CI this should run 23 | // without the APIServer started, so DefaultCommand will fail to create 24 | // a PluginLoader and emit the improved guidance. 25 | let cli = try CLITest() 26 | let (_, _, stderr, status) = try cli.run(arguments: ["nosuchplugin"]) // non-existent plugin name 27 | 28 | #expect(status != 0) 29 | #expect(stderr.contains("container system start")) 30 | #expect(stderr.contains("Plugins are unavailable") || stderr.contains("Plugin 'container-")) 31 | // Should include at least one computed plugin search path hint 32 | #expect(stderr.contains("container-plugins") || stderr.contains("container/plugins")) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/ContainerCommands/System/Property/PropertyClear.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ArgumentParser 18 | import ContainerClient 19 | import ContainerPersistence 20 | import ContainerizationError 21 | import Foundation 22 | 23 | extension Application { 24 | public struct PropertyClear: AsyncParsableCommand { 25 | public static let configuration = CommandConfiguration( 26 | commandName: "clear", 27 | abstract: "Clear a property value" 28 | ) 29 | 30 | @OptionGroup 31 | var global: Flags.Global 32 | 33 | @Argument(help: "The property ID") 34 | var id: String 35 | 36 | public init() {} 37 | 38 | public func run() async throws { 39 | guard let key = DefaultsStore.Keys(rawValue: id) else { 40 | throw ContainerizationError(.invalidArgument, message: "invalid property ID: \(id)") 41 | } 42 | 43 | DefaultsStore.unset(key: key) 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Sources/Services/ContainerNetworkService/Attachment.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | /// A snapshot of a network interface allocated to a sandbox. 18 | public struct Attachment: Codable, Sendable { 19 | /// The network ID associated with the attachment. 20 | public let network: String 21 | /// The hostname associated with the attachment. 22 | public let hostname: String 23 | /// The subnet CIDR, where the address is the container interface IPv4 address. 24 | public let address: String 25 | /// The IPv4 gateway address. 26 | public let gateway: String 27 | /// The MAC address associated with the attachment (optional). 28 | public let macAddress: String? 29 | 30 | public init(network: String, hostname: String, address: String, gateway: String, macAddress: String? = nil) { 31 | self.network = network 32 | self.hostname = hostname 33 | self.address = address 34 | self.gateway = gateway 35 | self.macAddress = macAddress 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Sources/ContainerClient/Core/ClientDiskUsage.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ContainerXPC 18 | import ContainerizationError 19 | import Foundation 20 | 21 | /// Client API for disk usage operations 22 | public struct ClientDiskUsage { 23 | static let serviceIdentifier = "com.apple.container.apiserver" 24 | 25 | /// Get disk usage statistics for all resource types 26 | public static func get() async throws -> DiskUsageStats { 27 | let client = XPCClient(service: serviceIdentifier) 28 | let message = XPCMessage(route: .systemDiskUsage) 29 | let reply = try await client.send(message) 30 | 31 | guard let responseData = reply.dataNoCopy(key: .diskUsageStats) else { 32 | throw ContainerizationError( 33 | .internalError, 34 | message: "invalid response from server: missing disk usage data" 35 | ) 36 | } 37 | 38 | return try JSONDecoder().decode(DiskUsageStats.self, from: responseData) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Sources/ContainerBuild/TerminalCommand.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import Foundation 18 | 19 | struct TerminalCommand: Codable { 20 | let commandType: String 21 | let code: String 22 | let rows: UInt16 23 | let cols: UInt16 24 | 25 | enum CodingKeys: String, CodingKey { 26 | case commandType = "command_type" 27 | case code 28 | case rows 29 | case cols 30 | } 31 | 32 | init(rows: UInt16, cols: UInt16) { 33 | self.commandType = "terminal" 34 | self.code = "winch" 35 | self.rows = rows 36 | self.cols = cols 37 | } 38 | 39 | init() { 40 | self.commandType = "terminal" 41 | self.code = "ack" 42 | self.rows = 0 43 | self.cols = 0 44 | } 45 | 46 | func json() throws -> String? { 47 | let encoder = JSONEncoder() 48 | let data = try encoder.encode(self) 49 | return data.base64EncodedString().trimmingCharacters(in: CharacterSet(charactersIn: "=")) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/ContainerCommands/Container/ContainerInspect.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ArgumentParser 18 | import ContainerClient 19 | import Foundation 20 | import SwiftProtobuf 21 | 22 | extension Application { 23 | public struct ContainerInspect: AsyncParsableCommand { 24 | public init() {} 25 | 26 | public static let configuration = CommandConfiguration( 27 | commandName: "inspect", 28 | abstract: "Display information about one or more containers") 29 | 30 | @OptionGroup 31 | var global: Flags.Global 32 | 33 | @Argument(help: "Container IDs to inspect") 34 | var containerIds: [String] 35 | 36 | public func run() async throws { 37 | let objects: [any Codable] = try await ClientContainer.list().filter { 38 | containerIds.contains($0.id) 39 | }.map { 40 | PrintableContainer($0) 41 | } 42 | print(try objects.jsonArray()) 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Sources/ContainerCommands/Image/ImageTag.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ArgumentParser 18 | import ContainerClient 19 | 20 | extension Application { 21 | public struct ImageTag: AsyncParsableCommand { 22 | public init() {} 23 | public static let configuration = CommandConfiguration( 24 | commandName: "tag", 25 | abstract: "Create a new reference for an existing image") 26 | 27 | @Argument(help: "The existing image reference (format: image-name[:tag])") 28 | var source: String 29 | 30 | @Argument(help: "The new image reference") 31 | var target: String 32 | 33 | @OptionGroup 34 | var global: Flags.Global 35 | 36 | public func run() async throws { 37 | let existing = try await ClientImage.get(reference: source) 38 | let targetReference = try ClientImage.normalizeReference(target) 39 | try await existing.tag(new: targetReference) 40 | print(target) 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Sources/ContainerCommands/Network/NetworkInspect.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ArgumentParser 18 | import ContainerClient 19 | import ContainerNetworkService 20 | import Foundation 21 | import SwiftProtobuf 22 | 23 | extension Application { 24 | public struct NetworkInspect: AsyncParsableCommand { 25 | public static let configuration = CommandConfiguration( 26 | commandName: "inspect", 27 | abstract: "Display information about one or more networks") 28 | 29 | @Argument(help: "Networks to inspect") 30 | var networks: [String] 31 | 32 | @OptionGroup 33 | var global: Flags.Global 34 | 35 | public init() {} 36 | 37 | public func run() async throws { 38 | let objects: [any Codable] = try await ClientNetwork.list().filter { 39 | networks.contains($0.id) 40 | }.map { 41 | PrintableNetwork($0) 42 | } 43 | print(try objects.jsonArray()) 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Sources/Helpers/NetworkVmnet/NetworkVmnetHelper.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ArgumentParser 18 | import ContainerLog 19 | import ContainerVersion 20 | import Logging 21 | 22 | @main 23 | struct NetworkVmnetHelper: AsyncParsableCommand { 24 | static let configuration = CommandConfiguration( 25 | commandName: "container-network-vmnet", 26 | abstract: "XPC service for managing a vmnet network", 27 | version: ReleaseVersion.singleLine(appName: "container-network-vmnet"), 28 | subcommands: [ 29 | Start.self 30 | ] 31 | ) 32 | 33 | static func setupLogger(id: String, debug: Bool) -> Logger { 34 | LoggingSystem.bootstrap { label in 35 | OSLogHandler( 36 | label: label, 37 | category: "NetworkVmnetHelper" 38 | ) 39 | } 40 | var log = Logger(label: "com.apple.container") 41 | if debug { 42 | log.logLevel = .debug 43 | } 44 | log[metadataKey: "id"] = "\(id)" 45 | return log 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Sources/Services/ContainerNetworkService/AttachmentConfiguration.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | /// Configuration information for attaching a container network interface to a network. 18 | public struct AttachmentConfiguration: Codable, Sendable { 19 | /// The network ID associated with the attachment. 20 | public let network: String 21 | 22 | /// The option information for the attachment 23 | public let options: AttachmentOptions 24 | 25 | public init(network: String, options: AttachmentOptions) { 26 | self.network = network 27 | self.options = options 28 | } 29 | } 30 | 31 | // Option information for a network attachment. 32 | public struct AttachmentOptions: Codable, Sendable { 33 | /// The hostname associated with the attachment. 34 | public let hostname: String 35 | 36 | /// The MAC address associated with the attachment (optional). 37 | public let macAddress: String? 38 | 39 | public init(hostname: String, macAddress: String? = nil) { 40 | self.hostname = hostname 41 | self.macAddress = macAddress 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /scripts/make-docs.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash -e 2 | # Copyright © 2025 Apple Inc. and the container project authors. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # https://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | opts=() 17 | opts+=("--allow-writing-to-directory" "$1") 18 | opts+=("generate-documentation") 19 | opts+=("--target" "ContainerSandboxService") 20 | opts+=("--target" "ContainerNetworkService") 21 | opts+=("--target" "ContainerImagesService") 22 | opts+=("--target" "ContainerClient") 23 | opts+=("--target" "ContainerLog") 24 | opts+=("--target" "ContainerPlugin") 25 | opts+=("--target" "ContainerXPC") 26 | opts+=("--target" "TerminalProgress") 27 | opts+=("--output-path" "$1") 28 | opts+=("--disable-indexing") 29 | opts+=("--transform-for-static-hosting") 30 | opts+=("--enable-experimental-combined-documentation") 31 | opts+=("--experimental-documentation-coverage") 32 | 33 | if [ ! -z "$2" ] ; then 34 | opts+=("--hosting-base-path" "$2") 35 | fi 36 | 37 | /usr/bin/swift package ${opts[@]} 38 | 39 | echo '{}' > "$1/theme-settings.json" 40 | 41 | cat > "$1/index.html" <<'EOF' 42 | 43 | 44 | 45 | 46 | Redirecting... 47 | 48 | 49 | 50 |

If you are not redirected automatically, click here.

51 | 52 | 53 | EOF 54 | -------------------------------------------------------------------------------- /Sources/ContainerCommands/System/Property/PropertyGet.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ArgumentParser 18 | import ContainerClient 19 | import ContainerPersistence 20 | import ContainerizationError 21 | import Foundation 22 | 23 | extension Application { 24 | public struct PropertyGet: AsyncParsableCommand { 25 | public static let configuration = CommandConfiguration( 26 | commandName: "get", 27 | abstract: "Retrieve a property value" 28 | ) 29 | 30 | @OptionGroup 31 | var global: Flags.Global 32 | 33 | @Argument(help: "The property ID") 34 | var id: String 35 | 36 | public init() {} 37 | 38 | public func run() async throws { 39 | let value = DefaultsStore.allValues() 40 | .filter { id == $0.id } 41 | .first 42 | guard let value else { 43 | throw ContainerizationError(.invalidArgument, message: "property ID \(id) not found") 44 | } 45 | 46 | guard let val = value.value?.description else { 47 | return 48 | } 49 | 50 | print(val) 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Sources/Helpers/RuntimeLinux/RuntimeLinuxHelper.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ArgumentParser 18 | import ContainerLog 19 | import ContainerVersion 20 | import Logging 21 | import OSLog 22 | 23 | @main 24 | struct RuntimeLinuxHelper: AsyncParsableCommand { 25 | static let configuration = CommandConfiguration( 26 | commandName: "container-runtime-linux", 27 | abstract: "XPC Service for managing a Linux sandbox", 28 | version: ReleaseVersion.singleLine(appName: "container-runtime-linux"), 29 | subcommands: [ 30 | Start.self 31 | ] 32 | ) 33 | 34 | package static func setupLogger(debug: Bool, metadata: [String: Logging.Logger.Metadata.Value] = [:]) -> Logging.Logger { 35 | LoggingSystem.bootstrap { label in 36 | OSLogHandler( 37 | label: label, 38 | category: "RuntimeLinuxHelper" 39 | ) 40 | } 41 | 42 | var log = Logger(label: "com.apple.container") 43 | if debug { 44 | log.logLevel = .debug 45 | } 46 | 47 | for (key, val) in metadata { 48 | log[metadataKey: key] = val 49 | } 50 | 51 | return log 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Sources/Services/ContainerAPIService/DiskUsage/DiskUsageHarness.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ContainerClient 18 | import ContainerXPC 19 | import ContainerizationError 20 | import Foundation 21 | import Logging 22 | 23 | /// XPC harness for disk usage operations 24 | public struct DiskUsageHarness: Sendable { 25 | let log: Logger 26 | let service: DiskUsageService 27 | 28 | public init(service: DiskUsageService, log: Logger) { 29 | self.log = log 30 | self.service = service 31 | } 32 | 33 | @Sendable 34 | public func get(_ message: XPCMessage) async throws -> XPCMessage { 35 | do { 36 | let stats = try await service.calculateDiskUsage() 37 | let data = try JSONEncoder().encode(stats) 38 | 39 | let reply = message.reply() 40 | reply.set(key: .diskUsageStats, value: data) 41 | return reply 42 | } catch { 43 | log.error("failed to get disk usage", metadata: ["error": "\(error)"]) 44 | throw ContainerizationError( 45 | .internalError, 46 | message: "failed to get disk usage", 47 | cause: error 48 | ) 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/ContainerCommands/Builder/BuilderStop.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ArgumentParser 18 | import ContainerClient 19 | import ContainerizationError 20 | import Foundation 21 | 22 | extension Application { 23 | public struct BuilderStop: AsyncParsableCommand { 24 | public static var configuration: CommandConfiguration { 25 | var config = CommandConfiguration() 26 | config.commandName = "stop" 27 | config.abstract = "Stop the builder container" 28 | return config 29 | } 30 | 31 | @OptionGroup 32 | var global: Flags.Global 33 | 34 | public init() {} 35 | 36 | public func run() async throws { 37 | do { 38 | let container = try await ClientContainer.get(id: "buildkit") 39 | try await container.stop() 40 | } catch { 41 | if error is ContainerizationError { 42 | if (error as? ContainerizationError)?.code == .notFound { 43 | print("builder is not running") 44 | return 45 | } 46 | } 47 | throw error 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/ContainerVersion/ReleaseVersion.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import CVersion 18 | import Foundation 19 | 20 | public struct ReleaseVersion { 21 | public static func singleLine(appName: String) -> String { 22 | var versionDetails: [String: String] = ["build": buildType()] 23 | versionDetails["commit"] = gitCommit().map { String($0.prefix(7)) } ?? "unspecified" 24 | let extras: String = versionDetails.map { "\($0): \($1)" }.sorted().joined(separator: ", ") 25 | 26 | return "\(appName) version \(version()) (\(extras))" 27 | } 28 | 29 | public static func buildType() -> String { 30 | #if DEBUG 31 | return "debug" 32 | #else 33 | return "release" 34 | #endif 35 | } 36 | 37 | public static func version() -> String { 38 | let appBundle = Bundle.appBundle(executableURL: CommandLine.executablePathUrl) 39 | let bundleVersion = appBundle?.infoDictionary?["CFBundleShortVersionString"] as? String 40 | return bundleVersion ?? get_release_version().map { String(cString: $0) } ?? "0.0.0" 41 | } 42 | 43 | public static func gitCommit() -> String? { 44 | get_git_commit().map { String(cString: $0) } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Sources/ContainerCommands/Volume/VolumeInspect.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ArgumentParser 18 | import ContainerClient 19 | import Foundation 20 | 21 | extension Application.VolumeCommand { 22 | public struct VolumeInspect: AsyncParsableCommand { 23 | public static let configuration = CommandConfiguration( 24 | commandName: "inspect", 25 | abstract: "Display information about one or more volumes" 26 | ) 27 | 28 | @OptionGroup 29 | var global: Flags.Global 30 | 31 | @Argument(help: "Volumes to inspect") 32 | var names: [String] 33 | 34 | public init() {} 35 | 36 | public func run() async throws { 37 | var volumes: [Volume] = [] 38 | 39 | for name in names { 40 | let volume = try await ClientVolume.inspect(name) 41 | volumes.append(volume) 42 | } 43 | 44 | let encoder = JSONEncoder() 45 | encoder.outputFormatting = [.prettyPrinted, .sortedKeys] 46 | encoder.dateEncodingStrategy = .iso8601 47 | 48 | let data = try encoder.encode(volumes) 49 | print(String(data: data, encoding: .utf8)!) 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Sources/ContainerLog/StderrLogHandler.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import Foundation 18 | import Logging 19 | 20 | /// Basic log handler for where simple message output is needed, 21 | /// such as CLI commands. 22 | public struct StderrLogHandler: LogHandler { 23 | public var logLevel: Logger.Level = .info 24 | public var metadata: Logger.Metadata = [:] 25 | 26 | public subscript(metadataKey metadataKey: String) -> Logger.Metadata.Value? { 27 | get { 28 | self.metadata[metadataKey] 29 | } 30 | set { 31 | self.metadata[metadataKey] = newValue 32 | } 33 | } 34 | 35 | public init() {} 36 | 37 | public func log( 38 | level: Logger.Level, 39 | message: Logger.Message, 40 | metadata: Logger.Metadata?, 41 | source: String, 42 | file: String, 43 | function: String, 44 | line: UInt 45 | ) { 46 | let messageText = message.description 47 | let data = messageText.data(using: .utf8) ?? Data() 48 | 49 | // Use a single write call for atomicity 50 | var output = data 51 | output.append("\n".data(using: .utf8)!) 52 | FileHandle.standardError.write(output) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Sources/ContainerBuild/BuildFile.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import Foundation 18 | import Logging 19 | 20 | public struct BuildFile { 21 | /// Tries to resolve either a Dockerfile or Containerfile relative to contextDir. 22 | /// Checks for Dockerfile, then falls back to Containerfile. 23 | public static func resolvePath(contextDir: String, log: Logger? = nil) throws -> String? { 24 | // Check for Dockerfile then Containerfile in context directory 25 | let dockerfilePath = URL(filePath: contextDir).appendingPathComponent("Dockerfile").path 26 | let containerfilePath = URL(filePath: contextDir).appendingPathComponent("Containerfile").path 27 | 28 | let dockerfileExists = FileManager.default.fileExists(atPath: dockerfilePath) 29 | let containerfileExists = FileManager.default.fileExists(atPath: containerfilePath) 30 | 31 | if dockerfileExists && containerfileExists { 32 | log?.info("Detected both Dockerfile and Containerfile, choosing Dockerfile") 33 | return dockerfilePath 34 | } 35 | 36 | if dockerfileExists { 37 | return dockerfilePath 38 | } 39 | 40 | if containerfileExists { 41 | return containerfilePath 42 | } 43 | 44 | return nil 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Sources/TerminalProgress/Int+Formatted.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import Foundation 18 | 19 | extension Int { 20 | func formattedTime() -> String { 21 | let secondsInMinute = 60 22 | let secondsInHour = secondsInMinute * 60 23 | let secondsInDay = secondsInHour * 24 24 | 25 | let days = self / secondsInDay 26 | let hours = (self % secondsInDay) / secondsInHour 27 | let minutes = (self % secondsInHour) / secondsInMinute 28 | let seconds = self % secondsInMinute 29 | 30 | var components = [String]() 31 | if days > 0 { 32 | components.append("\(days)d") 33 | } 34 | if hours > 0 || days > 0 { 35 | components.append("\(hours)h") 36 | } 37 | if minutes > 0 || hours > 0 || days > 0 { 38 | components.append("\(minutes)m") 39 | } 40 | components.append("\(seconds)s") 41 | return components.joined(separator: " ") 42 | } 43 | 44 | func formattedNumber() -> String { 45 | let formatter = NumberFormatter() 46 | formatter.numberStyle = .decimal 47 | guard let formattedNumber = formatter.string(from: NSNumber(value: self)) else { 48 | return "" 49 | } 50 | return formattedNumber 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Sources/ContainerCommands/Network/NetworkCreate.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ArgumentParser 18 | import ContainerClient 19 | import ContainerNetworkService 20 | import ContainerizationError 21 | import Foundation 22 | import TerminalProgress 23 | 24 | extension Application { 25 | public struct NetworkCreate: AsyncParsableCommand { 26 | public static let configuration = CommandConfiguration( 27 | commandName: "create", 28 | abstract: "Create a new network") 29 | 30 | @Option(name: .customLong("label"), help: "Set metadata for a network") 31 | var labels: [String] = [] 32 | 33 | @Option(name: .customLong("subnet"), help: "Set subnet for a network") 34 | var subnet: String? = nil 35 | 36 | @OptionGroup 37 | var global: Flags.Global 38 | 39 | @Argument(help: "Network name") 40 | var name: String 41 | 42 | public init() {} 43 | 44 | public func run() async throws { 45 | let parsedLabels = Utility.parseKeyValuePairs(labels) 46 | let config = try NetworkConfiguration(id: self.name, mode: .nat, subnet: subnet, labels: parsedLabels) 47 | let state = try await ClientNetwork.create(configuration: config) 48 | print(state.id) 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Tests/SocketForwarderTests/TCPEchoServer.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import NIO 18 | 19 | struct TCPEchoServer: Sendable { 20 | private let serverAddress: SocketAddress 21 | 22 | private let eventLoopGroup: MultiThreadedEventLoopGroup 23 | 24 | public init(serverAddress: SocketAddress, eventLoopGroup: MultiThreadedEventLoopGroup) { 25 | self.serverAddress = serverAddress 26 | self.eventLoopGroup = eventLoopGroup 27 | } 28 | 29 | public func run() throws -> EventLoopFuture { 30 | let bootstrap = ServerBootstrap(group: self.eventLoopGroup) 31 | .serverChannelOption(ChannelOptions.socket(.init(SOL_SOCKET), .init(SO_REUSEADDR)), value: 1) 32 | .childChannelOption(ChannelOptions.socket(.init(SOL_SOCKET), .init(SO_REUSEADDR)), value: 1) 33 | .childChannelInitializer { channel in 34 | channel.eventLoop.makeCompletedFuture { 35 | try channel.pipeline.syncOperations.addHandler( 36 | BackPressureHandler() 37 | ) 38 | try channel.pipeline.syncOperations.addHandler( 39 | TCPEchoHandler() 40 | ) 41 | } 42 | } 43 | 44 | return bootstrap.bind(to: self.serverAddress) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Sources/Services/ContainerAPIService/HealthCheck/HealthCheckHarness.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import CVersion 18 | import ContainerClient 19 | import ContainerVersion 20 | import ContainerXPC 21 | import Containerization 22 | import Foundation 23 | import Logging 24 | 25 | public actor HealthCheckHarness { 26 | private let appRoot: URL 27 | private let installRoot: URL 28 | private let log: Logger 29 | 30 | public init(appRoot: URL, installRoot: URL, log: Logger) { 31 | self.appRoot = appRoot 32 | self.installRoot = installRoot 33 | self.log = log 34 | } 35 | 36 | @Sendable 37 | public func ping(_ message: XPCMessage) async -> XPCMessage { 38 | let reply = message.reply() 39 | reply.set(key: .appRoot, value: appRoot.absoluteString) 40 | reply.set(key: .installRoot, value: installRoot.absoluteString) 41 | reply.set(key: .apiServerVersion, value: ReleaseVersion.singleLine(appName: "container-apiserver")) 42 | reply.set(key: .apiServerCommit, value: get_git_commit().map { String(cString: $0) } ?? "unspecified") 43 | // Extra optional fields for richer client display 44 | reply.set(key: .apiServerBuild, value: ReleaseVersion.buildType()) 45 | reply.set(key: .apiServerAppName, value: "container API Server") 46 | return reply 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Tests/SocketForwarderTests/LRUCacheTest.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import Testing 18 | 19 | @testable import SocketForwarder 20 | 21 | struct LRUCacheTest { 22 | @Test 23 | func testLRUCache() throws { 24 | let cache = LRUCache(size: 3) 25 | #expect(cache.count == 0) 26 | 27 | #expect(cache.put(key: "foo", value: "1") == nil) 28 | #expect(cache.count == 1) 29 | 30 | #expect(cache.put(key: "bar", value: "2") == nil) 31 | #expect(cache.count == 2) 32 | 33 | #expect(cache.put(key: "baz", value: "3") == nil) 34 | #expect(cache.count == 3) 35 | 36 | let replaced = try #require(cache.put(key: "bar", value: "4")) 37 | #expect(replaced == ("bar", "2")) 38 | #expect(cache.count == 3) 39 | 40 | let firstEvicted = try #require(cache.put(key: "qux", value: "5")) 41 | #expect(firstEvicted == ("foo", "1")) 42 | #expect(cache.count == 3) 43 | 44 | let secondEvicted = try #require(cache.put(key: "quux", value: "6")) 45 | #expect(secondEvicted == ("baz", "3")) 46 | #expect(cache.count == 3) 47 | 48 | #expect(cache.get("foo") == nil) 49 | #expect(cache.get("bar") == "4") 50 | #expect(cache.get("baz") == nil) 51 | #expect(cache.get("qux") == "5") 52 | #expect(cache.get("quux") == "6") 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Sources/ContainerClient/Core/DiskUsage.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import Foundation 18 | 19 | /// Disk usage statistics for all resource types 20 | public struct DiskUsageStats: Sendable, Codable { 21 | /// Disk usage for images 22 | public var images: ResourceUsage 23 | 24 | /// Disk usage for containers 25 | public var containers: ResourceUsage 26 | 27 | /// Disk usage for volumes 28 | public var volumes: ResourceUsage 29 | 30 | public init(images: ResourceUsage, containers: ResourceUsage, volumes: ResourceUsage) { 31 | self.images = images 32 | self.containers = containers 33 | self.volumes = volumes 34 | } 35 | } 36 | 37 | /// Disk usage statistics for a specific resource type 38 | public struct ResourceUsage: Sendable, Codable { 39 | /// Total number of resources 40 | public var total: Int 41 | 42 | /// Number of active/running resources 43 | public var active: Int 44 | 45 | /// Total size in bytes 46 | public var sizeInBytes: UInt64 47 | 48 | /// Reclaimable size in bytes (from unused/inactive resources) 49 | public var reclaimable: UInt64 50 | 51 | public init(total: Int, active: Int, sizeInBytes: UInt64, reclaimable: UInt64) { 52 | self.total = total 53 | self.active = active 54 | self.sizeInBytes = sizeInBytes 55 | self.reclaimable = reclaimable 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Sources/ContainerCommands/System/DNS/DNSDelete.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ArgumentParser 18 | import ContainerClient 19 | import ContainerizationError 20 | import Foundation 21 | 22 | extension Application { 23 | public struct DNSDelete: AsyncParsableCommand { 24 | public static let configuration = CommandConfiguration( 25 | commandName: "delete", 26 | abstract: "Delete a local DNS domain (must run as an administrator)", 27 | aliases: ["rm"] 28 | ) 29 | 30 | @OptionGroup 31 | var global: Flags.Global 32 | 33 | @Argument(help: "The local domain name") 34 | var domainName: String 35 | 36 | public init() {} 37 | 38 | public func run() async throws { 39 | let resolver = HostDNSResolver() 40 | do { 41 | try resolver.deleteDomain(name: domainName) 42 | print(domainName) 43 | } catch { 44 | throw ContainerizationError(.invalidState, message: "cannot delete domain (try sudo?)") 45 | } 46 | 47 | do { 48 | try HostDNSResolver.reinitialize() 49 | } catch { 50 | throw ContainerizationError(.invalidState, message: "mDNSResponder restart failed, run `sudo killall -HUP mDNSResponder` to deactivate domain") 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Sources/DNSServer/Types.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import DNS 18 | import Foundation 19 | 20 | public typealias Message = DNS.Message 21 | public typealias ResourceRecord = DNS.ResourceRecord 22 | public typealias HostRecord = DNS.HostRecord 23 | public typealias IPv4 = DNS.IPv4 24 | public typealias IPv6 = DNS.IPv6 25 | public typealias ReturnCode = DNS.ReturnCode 26 | 27 | public enum DNSResolverError: Swift.Error, CustomStringConvertible { 28 | case serverError(_ msg: String) 29 | case invalidHandlerSpec(_ spec: String) 30 | case unsupportedHandlerType(_ t: String) 31 | case invalidIP(_ v: String) 32 | case invalidHandlerOption(_ v: String) 33 | case handlerConfigError(_ msg: String) 34 | 35 | public var description: String { 36 | switch self { 37 | case .serverError(let msg): 38 | return "server error: \(msg)" 39 | case .invalidHandlerSpec(let msg): 40 | return "invalid handler spec: \(msg)" 41 | case .unsupportedHandlerType(let t): 42 | return "unsupported handler type specified: \(t)" 43 | case .invalidIP(let ip): 44 | return "invalid IP specified: \(ip)" 45 | case .invalidHandlerOption(let v): 46 | return "invalid handler option specified: \(v)" 47 | case .handlerConfigError(let msg): 48 | return "error configuring handler: \(msg)" 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/ContainerClient/String+Extensions.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import Foundation 18 | 19 | extension String { 20 | public func fromISO8601DateString(to: String) -> String? { 21 | if let date = fromISO8601Date() { 22 | let dateformatTo = DateFormatter() 23 | dateformatTo.dateFormat = to 24 | return dateformatTo.string(from: date) 25 | } 26 | return nil 27 | } 28 | 29 | public func fromISO8601Date() -> Date? { 30 | let iso8601DateFormatter = ISO8601DateFormatter() 31 | iso8601DateFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds] 32 | return iso8601DateFormatter.date(from: self) 33 | } 34 | 35 | public func isAbsolutePath() -> Bool { 36 | self.starts(with: "/") 37 | } 38 | 39 | /// Trim all `char` characters from the left side of the string. Stops when encountering a character that 40 | /// doesn't match `char`. 41 | mutating public func trimLeft(char: Character) { 42 | if self.isEmpty { 43 | return 44 | } 45 | var trimTo = 0 46 | for c in self { 47 | if char != c { 48 | break 49 | } 50 | trimTo += 1 51 | } 52 | if trimTo != 0 { 53 | let index = self.index(self.startIndex, offsetBy: trimTo) 54 | self = String(self[index...]) 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Sources/ContainerCommands/Image/ImageInspect.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ArgumentParser 18 | import ContainerClient 19 | import ContainerizationError 20 | import Foundation 21 | import SwiftProtobuf 22 | 23 | extension Application { 24 | public struct ImageInspect: AsyncParsableCommand { 25 | public static let configuration = CommandConfiguration( 26 | commandName: "inspect", 27 | abstract: "Display information about one or more images") 28 | 29 | @OptionGroup 30 | var global: Flags.Global 31 | 32 | @Argument(help: "Images to inspect") 33 | var images: [String] 34 | 35 | public init() {} 36 | 37 | public func run() async throws { 38 | var printable = [any Codable]() 39 | let result = try await ClientImage.get(names: images) 40 | let notFound = result.error 41 | for image in result.images { 42 | guard !Utility.isInfraImage(name: image.reference) else { 43 | continue 44 | } 45 | printable.append(try await image.details()) 46 | } 47 | if printable.count > 0 { 48 | print(try printable.jsonArray()) 49 | } 50 | if notFound.count > 0 { 51 | throw ContainerizationError(.notFound, message: "images: \(notFound.joined(separator: "\n"))") 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Sources/ContainerCommands/System/DNS/DNSCreate.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ArgumentParser 18 | import ContainerClient 19 | import ContainerizationError 20 | import ContainerizationExtras 21 | import Foundation 22 | 23 | extension Application { 24 | public struct DNSCreate: AsyncParsableCommand { 25 | public static let configuration = CommandConfiguration( 26 | commandName: "create", 27 | abstract: "Create a local DNS domain for containers (must run as an administrator)" 28 | ) 29 | 30 | @OptionGroup 31 | var global: Flags.Global 32 | 33 | @Argument(help: "The local domain name") 34 | var domainName: String 35 | 36 | public init() {} 37 | 38 | public func run() async throws { 39 | let resolver: HostDNSResolver = HostDNSResolver() 40 | do { 41 | try resolver.createDomain(name: domainName) 42 | print(domainName) 43 | } catch let error as ContainerizationError { 44 | throw error 45 | } catch { 46 | throw ContainerizationError(.invalidState, message: "cannot create domain (try sudo?)") 47 | } 48 | 49 | do { 50 | try HostDNSResolver.reinitialize() 51 | } catch { 52 | throw ContainerizationError(.invalidState, message: "mDNSResponder restart failed, run `sudo killall -HUP mDNSResponder` to deactivate domain") 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Tests/DNSServerTests/NxDomainResolverTest.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import DNS 18 | import Testing 19 | 20 | @testable import DNSServer 21 | 22 | struct NxDomainResolverTest { 23 | @Test func testUnsupportedQuestionType() async throws { 24 | let handler: NxDomainResolver = NxDomainResolver() 25 | 26 | let query = Message( 27 | id: UInt16(1), 28 | type: .query, 29 | questions: [ 30 | Question(name: "foo", type: .host6) 31 | ]) 32 | 33 | let response = try await handler.answer(query: query) 34 | 35 | #expect(.notImplemented == response?.returnCode) 36 | #expect(1 == response?.id) 37 | #expect(.response == response?.type) 38 | #expect(1 == response?.questions.count) 39 | #expect(0 == response?.answers.count) 40 | } 41 | 42 | @Test func testHostNotPresent() async throws { 43 | let handler: NxDomainResolver = NxDomainResolver() 44 | 45 | let query = Message( 46 | id: UInt16(1), 47 | type: .query, 48 | questions: [ 49 | Question(name: "bar", type: .host) 50 | ]) 51 | 52 | let response = try await handler.answer(query: query) 53 | 54 | #expect(.nonExistentDomain == response?.returnCode) 55 | #expect(1 == response?.id) 56 | #expect(.response == response?.type) 57 | #expect(1 == response?.questions.count) 58 | #expect(0 == response?.answers.count) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Sources/ContainerClient/ContainerizationProgressAdapter.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ContainerizationExtras 18 | import TerminalProgress 19 | 20 | public enum ContainerizationProgressAdapter: ProgressAdapter { 21 | public static func handler(from progressUpdate: ProgressUpdateHandler?) -> ProgressHandler? { 22 | guard let progressUpdate else { 23 | return nil 24 | } 25 | return { events in 26 | var updateEvents = [ProgressUpdateEvent]() 27 | for event in events { 28 | if event.event == "add-items" { 29 | if let items = event.value as? Int { 30 | updateEvents.append(.addItems(items)) 31 | } 32 | } else if event.event == "add-total-items" { 33 | if let totalItems = event.value as? Int { 34 | updateEvents.append(.addTotalItems(totalItems)) 35 | } 36 | } else if event.event == "add-size" { 37 | if let size = event.value as? Int64 { 38 | updateEvents.append(.addSize(size)) 39 | } 40 | } else if event.event == "add-total-size" { 41 | if let totalSize = event.value as? Int64 { 42 | updateEvents.append(.addTotalSize(totalSize)) 43 | } 44 | } 45 | } 46 | await progressUpdate(updateEvents) 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Sources/ContainerClient/SandboxRoutes.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | public enum SandboxRoutes: String { 18 | /// Create an xpc endpoint to the sandbox instance. 19 | case createEndpoint = "com.apple.container.sandbox/createEndpoint" 20 | /// Bootstrap the sandbox instance and create the init process. 21 | case bootstrap = "com.apple.container.sandbox/bootstrap" 22 | /// Create a process in the sandbox. 23 | case createProcess = "com.apple.container.sandbox/createProcess" 24 | /// Start a process in the sandbox. 25 | case start = "com.apple.container.sandbox/start" 26 | /// Stop the sandbox. 27 | case stop = "com.apple.container.sandbox/stop" 28 | /// Return the current state of the sandbox. 29 | case state = "com.apple.container.sandbox/state" 30 | /// Kill a process in the sandbox. 31 | case kill = "com.apple.container.sandbox/kill" 32 | /// Resize the pty of a process in the sandbox. 33 | case resize = "com.apple.container.sandbox/resize" 34 | /// Wait on a process in the sandbox. 35 | case wait = "com.apple.container.sandbox/wait" 36 | /// Execute a new process in the sandbox. 37 | case exec = "com.apple.container.sandbox/exec" 38 | /// Dial a vsock port in the sandbox. 39 | case dial = "com.apple.container.sandbox/dial" 40 | /// Shutdown the sandbox service process. 41 | case shutdown = "com.apple.container.sandbox/shutdown" 42 | /// Get statistics for the sandbox. 43 | case statistics = "com.apple.container.sandbox/statistics" 44 | } 45 | -------------------------------------------------------------------------------- /Sources/Helpers/RuntimeLinux/NonisolatedInterfaceStrategy.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ContainerNetworkService 18 | import ContainerSandboxService 19 | import ContainerXPC 20 | import Containerization 21 | import ContainerizationError 22 | import Logging 23 | import Virtualization 24 | import vmnet 25 | 26 | /// Interface strategy for containers that use macOS's custom network feature. 27 | @available(macOS 26, *) 28 | struct NonisolatedInterfaceStrategy: InterfaceStrategy { 29 | private let log: Logger 30 | 31 | public init(log: Logger) { 32 | self.log = log 33 | } 34 | 35 | public func toInterface(attachment: Attachment, interfaceIndex: Int, additionalData: XPCMessage?) throws -> Interface { 36 | guard let additionalData else { 37 | throw ContainerizationError(.invalidState, message: "network state does not contain custom network reference") 38 | } 39 | 40 | var status: vmnet_return_t = .VMNET_SUCCESS 41 | guard let networkRef = vmnet_network_create_with_serialization(additionalData.underlying, &status) else { 42 | throw ContainerizationError(.invalidState, message: "cannot deserialize custom network reference, status \(status)") 43 | } 44 | 45 | log.info("creating NATNetworkInterface with network reference") 46 | let gateway = interfaceIndex == 0 ? attachment.gateway : nil 47 | return NATNetworkInterface(address: attachment.address, gateway: gateway, reference: networkRef, macAddress: attachment.macAddress) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Tests/ContainerPluginTests/MockPluginFactory.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ContainerPlugin 18 | import Foundation 19 | import Testing 20 | 21 | struct MockPluginError: Error {} 22 | 23 | struct MockPluginFactory: PluginFactory { 24 | public static let throwSuffix = "throw" 25 | 26 | private let plugins: [URL: Plugin] 27 | 28 | private let throwingURL: URL 29 | 30 | public init(tempURL: URL, plugins: [String: Plugin?]) throws { 31 | let fm = FileManager.default 32 | var prefixedPlugins: [URL: Plugin] = [:] 33 | for (suffix, plugin) in plugins { 34 | let url = tempURL.appending(path: suffix) 35 | try fm.createDirectory(at: url, withIntermediateDirectories: true) 36 | prefixedPlugins[url.standardizedFileURL] = plugin 37 | } 38 | self.plugins = prefixedPlugins 39 | self.throwingURL = tempURL.appending(path: Self.throwSuffix).standardizedFileURL 40 | } 41 | 42 | public func create(installURL: URL) throws -> Plugin? { 43 | let url = installURL.standardizedFileURL 44 | guard url != self.throwingURL else { 45 | throw MockPluginError() 46 | } 47 | return plugins[url] 48 | } 49 | 50 | public func create(parentURL: URL, name: String) throws -> Plugin? { 51 | let url = parentURL.appendingPathComponent(name).standardizedFileURL 52 | guard url != self.throwingURL else { 53 | throw MockPluginError() 54 | } 55 | return plugins[url] 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /Sources/Services/ContainerNetworkService/NetworkState.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import Foundation 18 | 19 | public struct NetworkStatus: Codable, Sendable { 20 | /// The address allocated for the network if no subnet was specified at 21 | /// creation time; otherwise, the subnet from the configuration. 22 | public let address: String 23 | /// The gateway IPv4 address. 24 | public let gateway: String 25 | 26 | public init( 27 | address: String, 28 | gateway: String 29 | ) { 30 | self.address = address 31 | self.gateway = gateway 32 | } 33 | 34 | } 35 | 36 | /// The configuration and runtime attributes for a network. 37 | public enum NetworkState: Codable, Sendable { 38 | // The network has been configured. 39 | case created(NetworkConfiguration) 40 | // The network is running. 41 | case running(NetworkConfiguration, NetworkStatus) 42 | 43 | public var state: String { 44 | switch self { 45 | case .created: "created" 46 | case .running: "running" 47 | } 48 | } 49 | 50 | public var id: String { 51 | switch self { 52 | case .created(let configuration): configuration.id 53 | case .running(let configuration, _): configuration.id 54 | } 55 | } 56 | 57 | public var creationDate: Date { 58 | switch self { 59 | case .created(let configuration): configuration.creationDate 60 | case .running(let configuration, _): configuration.creationDate 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Tests/DNSServerTests/MockHandlers.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import DNS 18 | import Testing 19 | 20 | @testable import DNSServer 21 | 22 | struct FooHandler: DNSHandler { 23 | public func answer(query: Message) async throws -> Message? { 24 | if query.questions[0].name == "foo" { 25 | guard let ip = IPv4("1.2.3.4") else { 26 | throw DNSResolverError.serverError("cannot create IP address in test") 27 | } 28 | return Message( 29 | id: query.id, 30 | type: .response, 31 | returnCode: .noError, 32 | questions: query.questions, 33 | answers: [HostRecord(name: query.questions[0].name, ttl: 0, ip: ip)] 34 | ) 35 | } 36 | return nil 37 | } 38 | } 39 | 40 | struct BarHandler: DNSHandler { 41 | public func answer(query: Message) async throws -> Message? { 42 | let question = query.questions[0] 43 | if question.name == "foo" || question.name == "bar" { 44 | guard let ip = IPv4("5.6.7.8") else { 45 | throw DNSResolverError.serverError("cannot create IP address in test") 46 | } 47 | return Message( 48 | id: query.id, 49 | type: .response, 50 | returnCode: .noError, 51 | questions: query.questions, 52 | answers: [HostRecord(name: query.questions[0].name, ttl: 0, ip: ip)] 53 | ) 54 | } 55 | return nil 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Tests/CLITests/TestCLINoParallelCases.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ContainerClient 18 | import ContainerizationOCI 19 | import Foundation 20 | import Testing 21 | 22 | /// Tests that need total control over environment to avoid conflicts. 23 | class TestCLINoParallelCases: CLITest { 24 | @Test func testImageSingleConcurrentDownload() throws { 25 | // removing this image during parallel tests breaks stuff! 26 | _ = try? run(arguments: ["image", "rm", alpine]) 27 | do { 28 | try doPull(imageName: alpine, args: ["--max-concurrent-downloads", "1"]) 29 | let imagePresent = try isImagePresent(targetImage: alpine) 30 | #expect(imagePresent, "Expected image to be pulled with maxConcurrentDownloads=1") 31 | } catch { 32 | Issue.record("failed to pull image with maxConcurrentDownloads flag: \(error)") 33 | return 34 | } 35 | } 36 | 37 | @Test func testImageManyConcurrentDownloads() throws { 38 | // removing this image during parallel tests breaks stuff! 39 | _ = try? run(arguments: ["image", "rm", alpine]) 40 | do { 41 | try doPull(imageName: alpine, args: ["--max-concurrent-downloads", "64"]) 42 | let imagePresent = try isImagePresent(targetImage: alpine) 43 | #expect(imagePresent, "Expected image to be pulled with maxConcurrentDownloads=64") 44 | } catch { 45 | Issue.record("failed to pull image with maxConcurrentDownloads flag: \(error)") 46 | return 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Sources/Services/ContainerNetworkService/AttachmentAllocator.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ContainerizationError 18 | import ContainerizationExtras 19 | 20 | actor AttachmentAllocator { 21 | private let allocator: any AddressAllocator 22 | private var hostnames: [String: UInt32] = [:] 23 | 24 | init(lower: UInt32, size: Int) throws { 25 | allocator = try UInt32.rotatingAllocator( 26 | lower: lower, 27 | size: UInt32(size) 28 | ) 29 | } 30 | 31 | /// Allocate a network address for a host. 32 | func allocate(hostname: String) async throws -> UInt32 { 33 | // Client is responsible for ensuring two containers don't use same hostname, so provide existing IP if hostname exists 34 | if let index = hostnames[hostname] { 35 | return index 36 | } 37 | 38 | let index = try allocator.allocate() 39 | hostnames[hostname] = index 40 | 41 | return index 42 | } 43 | 44 | /// Free an allocated network address by hostname. 45 | func deallocate(hostname: String) async throws { 46 | if let index = hostnames.removeValue(forKey: hostname) { 47 | try allocator.release(index) 48 | } 49 | } 50 | 51 | /// If no addresses are allocated, prevent future allocations and return true. 52 | func disableAllocator() async -> Bool { 53 | allocator.disableAllocator() 54 | } 55 | 56 | /// Retrieve the allocator index for a hostname. 57 | func lookup(hostname: String) async throws -> UInt32? { 58 | hostnames[hostname] 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Sources/ContainerClient/TableOutput.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import Foundation 18 | 19 | public struct TableOutput { 20 | private let rows: [[String]] 21 | private let spacing: Int 22 | 23 | public init( 24 | rows: [[String]], 25 | spacing: Int = 2 26 | ) { 27 | self.rows = rows 28 | self.spacing = spacing 29 | } 30 | 31 | public func format() -> String { 32 | var output = "" 33 | let maxLengths = self.maxLength() 34 | 35 | for rowIndex in 0.. [Int: Int] { 51 | var output: [Int: Int] = [:] 52 | for row in self.rows { 53 | for (i, column) in row.enumerated() { 54 | let currentMax = output[i] ?? 0 55 | output[i] = (column.count > currentMax) ? column.count : currentMax 56 | } 57 | } 58 | return output 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/01-bug.yml: -------------------------------------------------------------------------------- 1 | name: Bug report 2 | description: File a bug report. 3 | title: "[Bug]: " 4 | type: "Bug" 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | Thanks for taking the time to fill out this bug report! 10 | - type: checkboxes 11 | id: prereqs 12 | attributes: 13 | label: I have done the following 14 | description: Select that you have completed the following prerequisites. 15 | options: 16 | - label: I have searched the existing issues 17 | required: true 18 | - label: If possible, I've reproduced the issue using the 'main' branch of this project 19 | required: false 20 | - type: textarea 21 | id: reproduce 22 | attributes: 23 | label: Steps to reproduce 24 | description: Explain how to reproduce the incorrect behavior. 25 | validations: 26 | required: true 27 | - type: textarea 28 | id: what-happened 29 | attributes: 30 | label: Current behavior 31 | description: A concise description of what you're experiencing. 32 | validations: 33 | required: true 34 | - type: textarea 35 | id: expected 36 | attributes: 37 | label: Expected behavior 38 | description: A concise description of what you expected to happen. 39 | validations: 40 | required: true 41 | - type: textarea 42 | attributes: 43 | label: Environment 44 | description: | 45 | Examples: 46 | - **OS**: macOS 26.0 (25A354) 47 | - **Xcode**: Version 26.0 (17A324) 48 | - **Container**: Container CLI version 0.1.0 49 | value: | 50 | - OS: 51 | - Xcode: 52 | - Container: 53 | render: markdown 54 | validations: 55 | required: true 56 | - type: textarea 57 | id: logs 58 | attributes: 59 | label: Relevant log output 60 | description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. 61 | value: | 62 | N/A 63 | render: shell 64 | - type: checkboxes 65 | id: terms 66 | attributes: 67 | label: Code of Conduct 68 | description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/apple/.github/blob/main/CODE_OF_CONDUCT.md). 69 | options: 70 | - label: I agree to follow this project's Code of Conduct 71 | required: true 72 | -------------------------------------------------------------------------------- /Sources/ContainerCommands/Builder/BuilderDelete.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ArgumentParser 18 | import ContainerClient 19 | import ContainerizationError 20 | import Foundation 21 | 22 | extension Application { 23 | public struct BuilderDelete: AsyncParsableCommand { 24 | public static var configuration: CommandConfiguration { 25 | var config = CommandConfiguration() 26 | config.commandName = "delete" 27 | config.aliases = ["rm"] 28 | config.abstract = "Delete the builder container" 29 | return config 30 | } 31 | 32 | @Flag(name: .shortAndLong, help: "Delete the builder even if it is running") 33 | var force = false 34 | 35 | @OptionGroup 36 | var global: Flags.Global 37 | 38 | public init() {} 39 | 40 | public func run() async throws { 41 | do { 42 | let container = try await ClientContainer.get(id: "buildkit") 43 | if container.status != .stopped { 44 | guard force else { 45 | throw ContainerizationError(.invalidState, message: "BuildKit container is not stopped, use --force to override") 46 | } 47 | try await container.stop() 48 | } 49 | try await container.delete() 50 | } catch { 51 | if error is ContainerizationError { 52 | if (error as? ContainerizationError)?.code == .notFound { 53 | return 54 | } 55 | } 56 | throw error 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Tests/CLITests/Subcommands/Build/TestCLITermIO.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ContainerizationOS 18 | import Foundation 19 | import Testing 20 | 21 | extension TestCLIRunBase { 22 | class TestCLITermIO: TestCLIRunBase { 23 | override var containerImage: String { 24 | "ghcr.io/linuxcontainers/alpine:3.20" 25 | } 26 | 27 | override var interactive: Bool { 28 | true 29 | } 30 | 31 | override var tty: Bool { 32 | true 33 | } 34 | 35 | override var command: [String]? { 36 | ["/bin/sh"] 37 | } 38 | 39 | override var progress: String { 40 | "none" 41 | } 42 | 43 | @Test func testTermIODoesNotPanic() async throws { 44 | let uniqMessage = UUID().uuidString 45 | let stdin: [String] = [ 46 | "echo \(uniqMessage)", 47 | "exit", 48 | ] 49 | do { 50 | guard case let statusBefore = try getContainerStatus(containerName), statusBefore == "running" else { 51 | Issue.record("test container is not running") 52 | return 53 | } 54 | let found = try await containerRun(stdin: stdin, findMessage: uniqMessage) 55 | if !found { 56 | Issue.record("did not find stdout line") 57 | return 58 | } 59 | } catch { 60 | Issue.record( 61 | "failed to start test container \(error)" 62 | ) 63 | return 64 | } 65 | } 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /Sources/ContainerCommands/Volume/VolumeCreate.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ArgumentParser 18 | import ContainerClient 19 | import Foundation 20 | 21 | extension Application.VolumeCommand { 22 | public struct VolumeCreate: AsyncParsableCommand { 23 | public static let configuration = CommandConfiguration( 24 | commandName: "create", 25 | abstract: "Create a new volume" 26 | ) 27 | 28 | @Option(name: .customLong("label"), help: "Set metadata for a volume") 29 | var labels: [String] = [] 30 | 31 | @Option(name: .customLong("opt"), help: "Set driver specific options") 32 | var driverOpts: [String] = [] 33 | 34 | @Option(name: .short, help: "Size of the volume in bytes, with optional K, M, G, T, or P suffix") 35 | var size: String? 36 | 37 | @OptionGroup 38 | var global: Flags.Global 39 | 40 | @Argument(help: "Volume name") 41 | var name: String 42 | 43 | public init() {} 44 | 45 | public func run() async throws { 46 | var parsedDriverOpts = Utility.parseKeyValuePairs(driverOpts) 47 | let parsedLabels = Utility.parseKeyValuePairs(labels) 48 | 49 | // If --size is specified, add it to driver options 50 | if let size = size { 51 | parsedDriverOpts["size"] = size 52 | } 53 | 54 | let volume = try await ClientVolume.create( 55 | name: name, 56 | driver: "local", 57 | driverOpts: parsedDriverOpts, 58 | labels: parsedLabels 59 | ) 60 | print(volume.name) 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Sources/SocketForwarder/TCPForwarder.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import Foundation 18 | import Logging 19 | import NIO 20 | import NIOFoundationCompat 21 | 22 | public struct TCPForwarder: SocketForwarder { 23 | private let proxyAddress: SocketAddress 24 | 25 | private let serverAddress: SocketAddress 26 | 27 | private let eventLoopGroup: any EventLoopGroup 28 | 29 | private let log: Logger? 30 | 31 | public init( 32 | proxyAddress: SocketAddress, 33 | serverAddress: SocketAddress, 34 | eventLoopGroup: any EventLoopGroup, 35 | log: Logger? = nil 36 | ) throws { 37 | self.proxyAddress = proxyAddress 38 | self.serverAddress = serverAddress 39 | self.eventLoopGroup = eventLoopGroup 40 | self.log = log 41 | } 42 | 43 | public func run() throws -> EventLoopFuture { 44 | self.log?.trace("frontend - creating listener") 45 | 46 | let bootstrap = ServerBootstrap(group: self.eventLoopGroup) 47 | .serverChannelOption(ChannelOptions.socket(.init(SOL_SOCKET), .init(SO_REUSEADDR)), value: 1) 48 | .childChannelOption(ChannelOptions.socket(.init(SOL_SOCKET), .init(SO_REUSEADDR)), value: 1) 49 | .childChannelInitializer { channel in 50 | channel.eventLoop.makeCompletedFuture { 51 | try channel.pipeline.syncOperations.addHandler( 52 | ConnectHandler(serverAddress: self.serverAddress, log: log) 53 | ) 54 | } 55 | } 56 | 57 | return 58 | bootstrap 59 | .bind(to: self.proxyAddress) 60 | .map { SocketForwarderResult(channel: $0) } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Sources/ContainerClient/Core/ContainerStats.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import Foundation 18 | 19 | /// Statistics for a container suitable for CLI display. 20 | public struct ContainerStats: Sendable, Codable { 21 | /// Container ID 22 | public var id: String 23 | /// Physical memory usage in bytes 24 | public var memoryUsageBytes: UInt64 25 | /// Memory limit in bytes 26 | public var memoryLimitBytes: UInt64 27 | /// CPU usage in microseconds 28 | public var cpuUsageUsec: UInt64 29 | /// Network received bytes (sum of all interfaces) 30 | public var networkRxBytes: UInt64 31 | /// Network transmitted bytes (sum of all interfaces) 32 | public var networkTxBytes: UInt64 33 | /// Block I/O read bytes (sum of all devices) 34 | public var blockReadBytes: UInt64 35 | /// Block I/O write bytes (sum of all devices) 36 | public var blockWriteBytes: UInt64 37 | /// Number of processes in the container 38 | public var numProcesses: UInt64 39 | 40 | public init( 41 | id: String, 42 | memoryUsageBytes: UInt64, 43 | memoryLimitBytes: UInt64, 44 | cpuUsageUsec: UInt64, 45 | networkRxBytes: UInt64, 46 | networkTxBytes: UInt64, 47 | blockReadBytes: UInt64, 48 | blockWriteBytes: UInt64, 49 | numProcesses: UInt64 50 | ) { 51 | self.id = id 52 | self.memoryUsageBytes = memoryUsageBytes 53 | self.memoryLimitBytes = memoryLimitBytes 54 | self.cpuUsageUsec = cpuUsageUsec 55 | self.networkRxBytes = networkRxBytes 56 | self.networkTxBytes = networkTxBytes 57 | self.blockReadBytes = blockReadBytes 58 | self.blockWriteBytes = blockWriteBytes 59 | self.numProcesses = numProcesses 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Sources/ContainerBuild/BuildStdio.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ContainerizationOS 18 | import Foundation 19 | import GRPC 20 | import NIO 21 | 22 | actor BuildStdio: BuildPipelineHandler { 23 | public let quiet: Bool 24 | public let handle: FileHandle 25 | 26 | init(quiet: Bool = false, output: FileHandle = FileHandle.standardError) throws { 27 | self.quiet = quiet 28 | self.handle = output 29 | } 30 | 31 | nonisolated func accept(_ packet: ServerStream) throws -> Bool { 32 | guard let _ = packet.getIO() else { 33 | return false 34 | } 35 | return true 36 | } 37 | 38 | func handle(_ sender: AsyncStream.Continuation, _ packet: ServerStream) async throws { 39 | guard !quiet else { 40 | return 41 | } 42 | guard let io = packet.getIO() else { 43 | throw Error.ioMissing 44 | } 45 | if let cmdString = try TerminalCommand().json() { 46 | var response = ClientStream() 47 | response.buildID = packet.buildID 48 | response.command = .init() 49 | response.command.id = packet.buildID 50 | response.command.command = cmdString 51 | sender.yield(response) 52 | } 53 | handle.write(io.data) 54 | } 55 | } 56 | 57 | extension BuildStdio { 58 | enum Error: Swift.Error, CustomStringConvertible { 59 | case ioMissing 60 | case invalidContinuation 61 | var description: String { 62 | switch self { 63 | case .ioMissing: 64 | return "io field missing in packet" 65 | case .invalidContinuation: 66 | return "continuation could not created" 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Sources/ContainerClient/SignalThreshold.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ContainerizationOS 18 | 19 | // For a lot of programs, they don't install their own signal handlers for 20 | // SIGINT/SIGTERM which poses a somewhat fun problem for containers. Because 21 | // they're pid 1 (doesn't matter that it isn't in the "root" pid namespace) 22 | // the default actions for SIGINT and SIGTERM now are nops. So this type gives 23 | // us an opportunity to set a threshold for a certain number of signals received 24 | // so we can have an escape hatch for users to escape their horrific mistake 25 | // of cat'ing /dev/urandom by exit(1)'ing :) 26 | public struct SignalThreshold { 27 | private let threshold: Int 28 | private let signals: [Int32] 29 | private var t: Task<(), Never>? 30 | 31 | public init( 32 | threshold: Int, 33 | signals: [Int32], 34 | ) { 35 | self.threshold = threshold 36 | self.signals = signals 37 | } 38 | 39 | // Start kicks off the signal watching. The passed in handler will 40 | // run only once upon passing the threshold number passed in the constructor. 41 | mutating public func start(handler: @Sendable @escaping () -> Void) { 42 | let signals = self.signals 43 | let threshold = self.threshold 44 | self.t = Task { 45 | var received = 0 46 | let signalHandler = AsyncSignalHandler.create(notify: signals) 47 | for await _ in signalHandler.signals { 48 | received += 1 49 | if received == threshold { 50 | handler() 51 | signalHandler.cancel() 52 | return 53 | } 54 | } 55 | } 56 | } 57 | 58 | public func stop() { 59 | self.t?.cancel() 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /.swift-format: -------------------------------------------------------------------------------- 1 | { 2 | "fileScopedDeclarationPrivacy" : { 3 | "accessLevel" : "private" 4 | }, 5 | "indentation" : { 6 | "spaces" : 4 7 | }, 8 | "indentConditionalCompilationBlocks" : false, 9 | "indentSwitchCaseLabels" : false, 10 | "lineBreakAroundMultilineExpressionChainComponents" : false, 11 | "lineBreakBeforeControlFlowKeywords" : false, 12 | "lineBreakBeforeEachArgument" : false, 13 | "lineBreakBeforeEachGenericRequirement" : false, 14 | "lineLength" : 180, 15 | "maximumBlankLines" : 1, 16 | "multiElementCollectionTrailingCommas" : true, 17 | "noAssignmentInExpressions" : { 18 | "allowedFunctions" : [ 19 | "XCTAssertNoThrow" 20 | ] 21 | }, 22 | "prioritizeKeepingFunctionOutputTogether" : false, 23 | "respectsExistingLineBreaks" : true, 24 | "rules" : { 25 | "AllPublicDeclarationsHaveDocumentation" : false, 26 | "AlwaysUseLowerCamelCase" : true, 27 | "AmbiguousTrailingClosureOverload" : false, 28 | "BeginDocumentationCommentWithOneLineSummary" : false, 29 | "DoNotUseSemicolons" : true, 30 | "DontRepeatTypeInStaticProperties" : true, 31 | "FileScopedDeclarationPrivacy" : true, 32 | "FullyIndirectEnum" : true, 33 | "GroupNumericLiterals" : true, 34 | "IdentifiersMustBeASCII" : true, 35 | "NeverForceUnwrap" : true, 36 | "NeverUseForceTry" : true, 37 | "NeverUseImplicitlyUnwrappedOptionals" : true, 38 | "NoAccessLevelOnExtensionDeclaration" : true, 39 | "NoAssignmentInExpressions" : true, 40 | "NoBlockComments" : false, 41 | "NoCasesWithOnlyFallthrough" : true, 42 | "NoEmptyTrailingClosureParentheses" : true, 43 | "NoLabelsInCasePatterns" : true, 44 | "NoLeadingUnderscores" : false, 45 | "NoParensAroundConditions" : true, 46 | "NoPlaygroundLiterals" : true, 47 | "NoVoidReturnOnFunctionSignature" : true, 48 | "OmitExplicitReturns" : true, 49 | "OneCasePerLine" : true, 50 | "OneVariableDeclarationPerLine" : true, 51 | "OnlyOneTrailingClosureArgument" : true, 52 | "OrderedImports" : true, 53 | "ReplaceForEachWithForLoop" : true, 54 | "ReturnVoidInsteadOfEmptyTuple" : true, 55 | "TypeNamesShouldBeCapitalized" : true, 56 | "UseEarlyExits" : true, 57 | "UseLetInEveryBoundCaseVariable" : true, 58 | "UseShorthandTypeNames" : true, 59 | "UseSingleLinePropertyGetter" : true, 60 | "UseSynthesizedInitializer" : true, 61 | "UseTripleSlashForDocumentationComments" : true, 62 | "UseWhereClausesInForLoops" : false, 63 | "ValidateDocumentationComments" : true 64 | }, 65 | "spacesAroundRangeFormationOperators" : false, 66 | "tabWidth" : 2, 67 | "version" : 1 68 | } 69 | -------------------------------------------------------------------------------- /Sources/DNSServer/Handlers/NxDomainResolver.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import DNS 18 | 19 | /// Handler that returns NXDOMAIN for all hostnames. 20 | public struct NxDomainResolver: DNSHandler { 21 | private let ttl: UInt32 22 | 23 | public init(ttl: UInt32 = 300) { 24 | self.ttl = ttl 25 | } 26 | 27 | public func answer(query: Message) async throws -> Message? { 28 | let question = query.questions[0] 29 | switch question.type { 30 | case ResourceRecordType.host: 31 | return Message( 32 | id: query.id, 33 | type: .response, 34 | returnCode: .nonExistentDomain, 35 | questions: query.questions, 36 | answers: [] 37 | ) 38 | case ResourceRecordType.nameServer, 39 | ResourceRecordType.alias, 40 | ResourceRecordType.startOfAuthority, 41 | ResourceRecordType.pointer, 42 | ResourceRecordType.mailExchange, 43 | ResourceRecordType.text, 44 | ResourceRecordType.host6, 45 | ResourceRecordType.service, 46 | ResourceRecordType.incrementalZoneTransfer, 47 | ResourceRecordType.standardZoneTransfer, 48 | ResourceRecordType.all: 49 | return Message( 50 | id: query.id, 51 | type: .response, 52 | returnCode: .notImplemented, 53 | questions: query.questions, 54 | answers: [] 55 | ) 56 | default: 57 | return Message( 58 | id: query.id, 59 | type: .response, 60 | returnCode: .formatError, 61 | questions: query.questions, 62 | answers: [] 63 | ) 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Tests/DNSServerTests/CompositeResolverTest.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import DNS 18 | import Testing 19 | 20 | @testable import DNSServer 21 | 22 | struct CompositeResolverTest { 23 | @Test func testCompositeResolver() async throws { 24 | let foo = FooHandler() 25 | let bar = BarHandler() 26 | let resolver = CompositeResolver(handlers: [foo, bar]) 27 | 28 | let fooQuery = Message( 29 | id: UInt16(1), 30 | type: .query, 31 | questions: [ 32 | Question(name: "foo", type: .host) 33 | ]) 34 | 35 | let fooResponse = try await resolver.answer(query: fooQuery) 36 | #expect(.noError == fooResponse?.returnCode) 37 | #expect(1 == fooResponse?.id) 38 | #expect(1 == fooResponse?.answers.count) 39 | let fooAnswer = fooResponse?.answers[0] as? HostRecord 40 | #expect(IPv4("1.2.3.4") == fooAnswer?.ip) 41 | 42 | let barQuery = Message( 43 | id: UInt16(1), 44 | type: .query, 45 | questions: [ 46 | Question(name: "bar", type: .host) 47 | ]) 48 | 49 | let barResponse = try await resolver.answer(query: barQuery) 50 | #expect(.noError == barResponse?.returnCode) 51 | #expect(1 == barResponse?.id) 52 | #expect(1 == barResponse?.answers.count) 53 | let barAnswer = barResponse?.answers[0] as? HostRecord 54 | #expect(IPv4("5.6.7.8") == barAnswer?.ip) 55 | 56 | let otherQuery = Message( 57 | id: UInt16(1), 58 | type: .query, 59 | questions: [ 60 | Question(name: "other", type: .host) 61 | ]) 62 | 63 | let otherResponse = try await resolver.answer(query: otherQuery) 64 | #expect(nil == otherResponse) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Sources/DNSServer/Handlers/StandardQueryValidator.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | /// Pass standard queries to a delegate handler. 18 | public struct StandardQueryValidator: DNSHandler { 19 | private let handler: DNSHandler 20 | 21 | /// Create the handler. 22 | /// - Parameter delegate: the handler that receives valid queries 23 | public init(handler: DNSHandler) { 24 | self.handler = handler 25 | } 26 | 27 | /// Ensures the query is valid before forwarding it to the delegate. 28 | /// - Parameter msg: the query message 29 | /// - Returns: the delegate response if the query is valid, and an 30 | /// error response otherwise 31 | public func answer(query: Message) async throws -> Message? { 32 | // Reject response messages. 33 | guard query.type == .query else { 34 | return Message( 35 | id: query.id, 36 | type: .response, 37 | returnCode: .formatError, 38 | questions: query.questions 39 | ) 40 | } 41 | 42 | // Standard DNS servers handle only query operations. 43 | guard query.operationCode == .query else { 44 | return Message( 45 | id: query.id, 46 | type: .response, 47 | returnCode: .notImplemented, 48 | questions: query.questions 49 | ) 50 | } 51 | 52 | // Standard DNS servers only handle messages with exactly one question. 53 | guard query.questions.count == 1 else { 54 | return Message( 55 | id: query.id, 56 | type: .response, 57 | returnCode: .formatError, 58 | questions: query.questions 59 | ) 60 | } 61 | 62 | return try await handler.answer(query: query) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Sources/ContainerCommands/System/DNS/DNSList.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container project authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import ArgumentParser 18 | import ContainerClient 19 | import Foundation 20 | 21 | extension Application { 22 | public struct DNSList: AsyncParsableCommand { 23 | public static let configuration = CommandConfiguration( 24 | commandName: "list", 25 | abstract: "List local DNS domains", 26 | aliases: ["ls"] 27 | ) 28 | 29 | @Option(name: .long, help: "Format of the output") 30 | var format: ListFormat = .table 31 | 32 | @Flag(name: .shortAndLong, help: "Only output the domain") 33 | var quiet = false 34 | 35 | @OptionGroup 36 | var global: Flags.Global 37 | 38 | public init() {} 39 | 40 | public func run() async throws { 41 | let resolver: HostDNSResolver = HostDNSResolver() 42 | let domains = resolver.listDomains() 43 | try printDomains(domains: domains, format: format) 44 | } 45 | 46 | private func createHeader() -> [[String]] { 47 | [["DOMAIN"]] 48 | } 49 | 50 | func printDomains(domains: [String], format: ListFormat) throws { 51 | if format == .json { 52 | let data = try JSONEncoder().encode(domains) 53 | print(String(data: data, encoding: .utf8)!) 54 | 55 | return 56 | } 57 | 58 | if self.quiet { 59 | domains.forEach { domain in 60 | print(domain) 61 | } 62 | return 63 | } 64 | 65 | var rows = createHeader() 66 | for domain in domains { 67 | rows.append([domain]) 68 | } 69 | 70 | let formatter = TableOutput(rows: rows) 71 | print(formatter.format()) 72 | } 73 | 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Protobuf.Makefile: -------------------------------------------------------------------------------- 1 | # Copyright © 2025 Apple Inc. and the container project authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | ROOT_DIR := $(shell git rev-parse --show-toplevel) 16 | LOCAL_DIR := $(ROOT_DIR)/.local 17 | LOCAL_BIN_DIR := $(LOCAL_DIR)/bin 18 | 19 | BUILDER_SHIM_REPO ?= https://github.com/apple/container-builder-shim.git 20 | 21 | # Versions 22 | BUILDER_SHIM_VERSION ?= $(shell sed -n 's/let builderShimVersion *= *"\(.*\)"/\1/p' Package.swift) 23 | PROTOC_VERSION := 26.1 24 | 25 | # Protoc binary installation 26 | PROTOC_ZIP := protoc-$(PROTOC_VERSION)-osx-universal_binary.zip 27 | PROTOC := $(LOCAL_BIN_DIR)/protoc@$(PROTOC_VERSION)/protoc 28 | $(PROTOC): 29 | @echo Downloading protocol buffers... 30 | @mkdir -p $(LOCAL_DIR) 31 | @curl -OL https://github.com/protocolbuffers/protobuf/releases/download/v$(PROTOC_VERSION)/$(PROTOC_ZIP) 32 | @mkdir -p $(dir $@) 33 | @unzip -jo $(PROTOC_ZIP) bin/protoc -d $(dir $@) 34 | @unzip -o $(PROTOC_ZIP) 'include/*' -d $(dir $@) 35 | @rm -f $(PROTOC_ZIP) 36 | 37 | .PHONY: protoc-gen-swift 38 | protoc-gen-swift: 39 | @$(SWIFT) build --product protoc-gen-swift 40 | @$(SWIFT) build --product protoc-gen-grpc-swift 41 | 42 | .PHONY: protos 43 | protos: $(PROTOC) protoc-gen-swift 44 | @echo Generating protocol buffers source code... 45 | @mkdir -p $(LOCAL_DIR) 46 | @if [ ! -d "$(LOCAL_DIR)/container-builder-shim" ]; then \ 47 | cd $(LOCAL_DIR) && git clone --branch $(BUILDER_SHIM_VERSION) --depth 1 $(BUILDER_SHIM_REPO); \ 48 | fi 49 | @$(PROTOC) $(LOCAL_DIR)/container-builder-shim/pkg/api/Builder.proto \ 50 | --plugin=protoc-gen-grpc-swift=$(BUILD_BIN_DIR)/protoc-gen-grpc-swift \ 51 | --plugin=protoc-gen-swift=$(BUILD_BIN_DIR)/protoc-gen-swift \ 52 | --proto_path=$(LOCAL_DIR)/container-builder-shim/pkg/api \ 53 | --grpc-swift_out="Sources/ContainerBuild" \ 54 | --grpc-swift_opt=Visibility=Public \ 55 | --swift_out="Sources/ContainerBuild" \ 56 | --swift_opt=Visibility=Public \ 57 | -I. 58 | @"$(MAKE)" update-licenses 59 | 60 | .PHONY: clean-proto-tools 61 | clean-proto-tools: 62 | @echo Cleaning proto tools... 63 | @rm -rf $(LOCAL_DIR)/bin/protoc* 64 | @rm -rf $(LOCAL_DIR)/container-builder-shim 65 | --------------------------------------------------------------------------------