54 Commits

Author SHA1 Message Date
Vitor Hugo 4f6df44c2a fix(Platform): make arm64 nil-variant and v8 hash identically (#764)
## Summary

`Platform.==` treats `arm64` with `nil` variant as equal to `arm64/v8`,
but `hash(into:)` used `description` which serializes them differently
(`linux/arm64` vs `linux/arm64/v8`). This violates the `Hashable`
contract — equal values must produce the same hash.

### Root cause

```swift
// == returns true for these two
let a = Platform(arch: "arm64", os: "linux", variant: nil)
let b = Platform(arch: "arm64", os: "linux", variant: "v8")
a == b // true ✓

// but hash was different — broken
a.hashValue == b.hashValue // false ✗ (before this fix)
```

This mismatch caused `Set<Platform>` and `Dictionary<Platform, ...>`
lookups to silently miss entries when one platform was decoded from JSON
(no `variant` field in the manifest) and another was created via
`Platform(from:)` or `Platform.current` (which both set `variant =
"v8"`).

### Practical consequence

In `apple/container`, this manifests as inconsistent platform-string
normalization across stages of a single `container build` — some stages
log `linux/arm64`, others `linux/arm64/v8` — which can cause `COPY
--from=<stage>` to fail to resolve the source stage under concurrent
builds. See apple/container#1542.

### Fix

`hash(into:)` now normalizes `arm64` with `nil` variant to `"v8"` before
hashing, matching the existing `==` behavior.
2026-06-16 10:06:06 -04:00
Yibo Zhuang 72043f90ac ContentStore: Fix totalAllocatedSize on Linux (#761)
`.totalFileAllocatedSizeKey` returns nil for directories on Darwin but
on Linux it returns `st_blocks * st_blksize` (4 KB each) in Foundation.
The empty-store test summed three directory inodes on Linux and failed
with `#expect(size == 0)`. This change adds filter on the enumerator to
regular files only so the totals are content-only and will work for both
Darwin and Linux.
2026-06-01 17:06:09 -07:00
Raj a2a1add6c7 Add totalAllocatedSize to ContentStore (#760)
Release containerization / deployDocs (push) Has been cancelled
Release containerization / Publish release (push) Has been cancelled
Release containerization / containerization (push) Successful in 1s
This PR adds `totalAllocatedSize()` to the `ContentStore` protocol so it
can be used to get the on-disk footprint without reaching past the
abstraction. `LocalContentStore` implements it by walking its base path,
covering both committed blobs and active ingest sessions.
2026-06-01 14:22:04 -07:00
ChengHao Yang de075fff8a Fix: bad request will remove header and retry login (#711)
If you're requesting on `/v2/` with basic auth, AWS ECR will return 400
Bad Request and won't provide `www-authenticate` information.

Retry the request after removing the Authorization header.

Continue PR #429

Fixed apple/container#847

Signed-off-by: ChengHao Yang <17496418+tico88612@users.noreply.github.com>
2026-04-29 15:29:54 -04:00
Danny Canter 0891bf399d OCI: Change hook to hooks (#702) 2026-04-27 11:47:36 -07:00
Danny Canter c0f8fb423a ContainerizationOCI: dedupe platform normalizing logic (#683)
We had a couple spots we were using this logic, probably better to shove
it in a function. The other thing this fixes is today .current was only
checking for arm64 which on a linux host uname with return aarch64, so
we'll fatalError trying to use it today..
2026-04-14 14:27:14 -04:00
Aditya Ramani f8c943b44d Write file data in chunks in ContentWriter (#634)
Update the `ContentWriter.create(from url: URL)` method to not load the
whole file in memory when computing its hash and writing its contents
2026-04-03 12:20:12 -07:00
Danny Canter c5c1500eac Remove some unneeded Foundation imports (#613) 2026-03-30 17:00:58 -07:00
Dmitry Kovba 8df7ce78d7 Enforce progress event value types (#579)
Enforces progress event value types.
2026-03-17 09:23:45 -07:00
J Logan eff935e35f Adds access group to keychain helper APIs. (#553)
- Will be used by the fix for apple/container#1253.
- We use a common keychain ID between the `container CLI and
`container-core-images`, but right now keychain entries created by the
CLI must be opted-in for `container-core-images`.
- Using an access group with a notarized application should give both
apps automatic access to the keychain.
2026-03-04 16:48:27 -08:00
Raj 636eef0eff Add catalog listing to RegistryClient (#564)
Release containerization / deployDocs (push) Has been cancelled
Release containerization / Publish release (push) Has been cancelled
Release containerization / containerization (push) Successful in 2s
Adds a `catalog(prefix:)` method to `RegistryClient` that implements
`GET /v2/_catalog` with pagination and prefix-based filtering.
2026-02-27 17:17:57 -08:00
Raj 38d3f2b3d7 Fall back to the referrers tag schema when the referrers API is not available (#560)
Release containerization / deployDocs (push) Has been cancelled
Release containerization / Publish release (push) Has been cancelled
Release containerization / containerization (push) Successful in 1s
Fall back to the referrers tag schema when the referrers API is not
available
2026-02-25 18:11:23 -08:00
Raj e6a5acdff2 Add OCI 1.1 subject, artifactType, and referrers API (#546)
This PR adds OCI Image Spec v1.1 artifact support. It extends Manifest,
Index, and Descriptor with the `subject` and `artifactType` fields, adds
a `referrers()` method to RegistryClient implementing the OCI
Distribution Spec v1.1 referrers API. Also, I've added some unit unit
tests for backward compatibility and roundtrip encpding of all new
fields
2026-02-23 12:09:16 -08:00
Maxime Grenu 86171acece ContainerizationOCI: raise Reference name limit to 255 to match OCI spec (#541)
## What

Raise the constant from 127 to 255 in `Reference.swift` and update the
derived `referenceTotalLengthMax` accordingly.

## Why

Closes #453.

The [OCI distribution
spec](https://github.com/opencontainers/distribution-spec/blob/main/spec.md)
states that the **name** component of an image reference (registry host
+ repository path) may be at most 255 bytes. The previous hard-coded
limit of 127 characters incorrectly rejected valid references that used
long registry hostnames or deep path hierarchies.

The old `referenceTotalLengthMax = 255` was also inconsistent: with a
127-char name cap, a 255-char total reference would only allow a very
short tag. The new value is derived explicitly as `nameTotalLengthMax
(255) + separator (1) + tagLengthMax (128) = 384`.

## How

- `nameTotalLengthMax`: 127 → 255
- `tagLengthMax`: new constant (128) documenting the maximum tag length
already enforced by the tag regex (`{0,127}` + leading char = 128
chars).
- `referenceTotalLengthMax`: computed from the two constants above (384)
rather than hard-coded to 255.

## Testing

Added three new cases in `ReferenceTests.swift`:
-  Name of 128 characters (registry + path) — previously rejected, now
accepted
-  Name of exactly 255 characters — at the OCI spec maximum, accepted
-  Name of 256 characters — one over the limit, rejected
2026-02-20 11:37:42 -08:00
Saehej Kang 3f4eee7d2a [keychain]: add list function + update variable/parameter names (#502)
Release containerization / deployDocs (push) Has been cancelled
Release containerization / Publish release (push) Has been cancelled
Release containerization / containerization (push) Successful in 1s
- Add a `list` function that is needed for the `container registry list`
command
- Updates to `KeychainHelper` and `KeychainQuery` variable/parameter
names
- Updates to test cases
2026-02-04 18:15:30 -08:00
J Logan dadfdcefae Adds opt-in precommit hook to check formatting. (#483) 2026-01-21 11:34:16 -08:00
Kathryn Baldauf ec2ee3e94d Update license header on all files to include the current year (#470)
Related to https://github.com/apple/container/pull/1024

Signed-off-by: Kathryn Baldauf <k_baldauf@apple.com>
2026-01-05 13:08:48 -08:00
Sebastian Moßburger 02bd878212 feat(registry): Add custom ca certificate override (#402)
Closely related to https://github.com/apple/container/issues/305 I would
like to override the used SSL TrustRoots via standard env variables.

This here would add this configuration and would give an entrypoint for
an implementation of 305 to provide CLI flags or similar.


This has no tests yet, as this would require setting up something like a
MITM proxy when testing against a registry.
As I am unfamiliar with the codebase, I would be willing to do this, but
would require a first nudge on where to best implement this.

To actually use this, we would need to add the allowed env variables to
the `container system start` command env filter.
2025-12-23 07:14:32 -05:00
Yibo Zhuang 5ac406601e Add custom decoder to Linux struct for optional fields (#455)
Allows decoding of minimal OCI specs with empty linux objects by
providing default values for missing fields. Also added unit test to
ensure that empty Linux struct {} works correctly with the fix.
2025-12-19 01:28:02 -08:00
Danny Canter 7962dae643 Add capabilities support (#444)
Closes https://github.com/apple/containerization/issues/442

This adds capabilities support to LinuxContainer via a new surface in
ContainerizationOS + some C wrappers.
2025-12-11 15:07:17 -05:00
Dmitry Kovba 96d37e2e21 Fix multi-sentence error messages (#443)
Fixes multi-sentence error messages, where they start from a lowercased
letter.
2025-12-09 12:32:09 -08:00
Dmitry Kovba bb0cd39177 Lowercase error messages (#440)
For consistency, all error messages are lowercased.
2025-12-09 05:38:48 -08:00
Danny Canter 836b699a91 Wire up experimental OCI runtime support (#416) 2025-11-21 12:16:58 -08:00
RahulThennarasu 4d47c58a3d Fix: Allow OCI archives without manifest annotations (#397)
Fixes #369

Per the OCI Image Spec, manifest descriptor annotations are optional.
Previously, archives without annotations would fail to import with
"Failed to import image".

**Changes:**
- Modified `getImageReferencefromDescriptor` to return digest-based
references (`untagged@sha256:...`) when annotations are missing
- Removed guard that skipped manifests without annotations
- Added test case with `scratch_no_annotations.tar`

**Testing:**
All 167 tests pass, including new test for images without annotations.
2025-11-15 00:32:15 -03:00
Danny Canter 4855310dca OCI: Don't umount the rootfs path if it's not a mountpoint (#387)
Given this package is generic, and we use it in the guest where today we
actually umount via an rpc, just skip this if it's not a mountpoint.
2025-11-07 14:25:34 -08:00
Mark Baseggio d7814e4421 Make Index.mediaType optional to comply with OCI spec (#368)
The `mediaType` field in the `Index` struct was defined as a required
field, but according to the [OCI Image Index
Specification](https://github.com/opencontainers/image-spec/blob/main/image-index.md),
this field is optional.

This caused failures when loading OCI archives where the `index.json`
omits the top-level `mediaType` field, which is valid per the spec.
Tools like skopeo can generate such archives.

## Error before fix
```
keyNotFound(CodingKeys(stringValue: "mediaType", intValue: nil))
```

## Changes
- Changed `Index.mediaType` from `String` to `String?`
- Updated initializer to accept optional `mediaType` parameter
- Added comment documenting that field is optional per OCI spec

## Testing
Verified that OCI archives without a top-level `mediaType` field in
`index.json` now load successfully.

Fixes https://github.com/apple/container/issues/330
2025-10-29 01:46:16 -07:00
Kathryn Baldauf a27fefbb59 Add custom decoder inits for various oci types that use omitempty (#347)
Many fields on the various OCI types use "omitempty" for encoding and
decoding the json representation in golang. This PR adds custom json
decoder functions to allow for behavior similar to "omitempty".

---------

Signed-off-by: Kathryn Baldauf <k_baldauf@apple.com>
2025-10-24 15:05:40 -07:00
Danny Canter 40727e49ba Vminitd: Write cgroup limits (#322)
Fixes #320

Today we don't actually setup any cgroup limits, as because there's a
1-1 mapping from container<->vm we can just use the VMs resources as the
limit (can't use more than 1GB if that's all the guest sees :) ).
However, if we ever supported > 1 container in the guest it'd be
necessary to actually setup the cg limits. This change just sets a
memory limit and cpu toggles to match whatever was specific for the
container.
2025-10-09 16:03:40 -04:00
J Logan 2443a245cb Fixes proxy logic in RegistryClient. (#314)
- Closes #255.
- Fixes ProxyUtils so that the environment variable to be used for proxy
selection is determined by the request scheme.
- RegistryClient uses ProxyUtils to get the proxy URL used by the
HTTPClient.
- Tweak hostname resolution error message to avoid misleading output if
the proxy hostname cannot be resolved.
2025-10-09 10:30:49 -04:00
J Logan 995a231348 Removes "all rights reserved" from license header. (#309) 2025-10-03 13:27:59 -07:00
Euan Harris 511dd59e19 Remove accidental dependency on NIOFileSystem (#298)
Release containerization / deployDocs (push) Has been cancelled
Release containerization / Publish release (push) Has been cancelled
Release containerization / containerization (push) Successful in 1s
swift-nio's public export of NIOFileSystem was removed in 2.86.1:
https://github.com/apple/swift-nio/pull/3370

NIOFileSystem was not yet supposed to be public, but _NIOFileSystem
depended on it as a public import. This made it possible for
`containerization` to see the `NIOFileSystem` package by accident.

Replacing the use of `NIOFileSystem` by `_NIOFileSystem`, as used
elsewhere, fixes the problem.

## Why does CI currently pass?

The change in `swift-nio` does not currently cause `containerization`'s
CI to fail because `Package.resolved` pins `swift-nio` to 2.83.0, before
the change was made. New versions of upstream dependencies will not be
tested until `Package.resolved` is explicitly updated.

When containerization is built as a dependency of a end-user project,
its `Package.resolved` file is ignored. Instead, the dependency
constraints from containerization's Package.swift file are combined with
those of the project and any other library dependencies, so SwiftPM or
Xcode can find a set of mutually compatible packages. This can lead to
new versions of containerization's upstream dependencies being used,
even though those versions have never been tested in CI.

The build failure can be demonstrated by creating a new package which
depends on `containerization` but does not constrain package versions:
```
    % swift package init --type executable
    Creating executable package: test
    Creating Package.swift
    Creating Sources
    Creating Sources/test/test.swift
    % cat > Package.swift <<EOF
    heredoc> // swift-tools-version: 6.2
    // The swift-tools-version declares the minimum version of Swift required to build this package.

    import PackageDescription

    let package = Package(
        name: "test",
        platforms: [
            .macOS(.v26),
        ],
        dependencies: [
            .package(url: "https://github.com/apple/containerization", from: "0.7.2"),

        ],
        targets: [
            // Targets are the basic building blocks of a package, defining a module or a test suite.
            // Targets can depend on other targets in this package and products from dependencies.
            .executableTarget(
                name: "test",
                dependencies: [
                    .product(name: "Containerization", package: "containerization"),
                ]
            ),
        ]
    )
    EOF
    % swift build
    ...
    /private/tmp/test/.build/checkouts/containerization/Sources/ContainerizationOCI/Client/RegistryClient+Fetch.swift:25:8: error: no such module 'NIOFileSystem'
     23 |
     24 | #if os(macOS)
     25 | import NIOFileSystem
        |        `- error: no such module 'NIOFileSystem'
     26 | #endif
     27 |
```
2025-09-23 09:52:29 -07:00
Michael Crosby 8bcd77173b only return arm64 for proper 64 architectures (#293)
Signed-off-by: crosbymichael <michael_crosby@apple.com>
2025-09-18 10:40:50 -04:00
Dmitry Kovba 9319eecf14 Prevent a leak detection crash when close() fails (#280)
When an image is broken, `container-core-images` may crash with:
```
Application Specific Information:
_NIOFileSystem/SystemFileHandle.swift:131: Fatal error: Leaking file descriptor: the handle for '/Users/Dmitry/Library/Application Support/com.apple.container/content/ingest/03144D38-D98D-4833-9E0C-60229BF653D9/.tmp-MIB1oc' MUST be closed or detached with 'close()' or 'detachUnsafeFileDescriptor()' before the final reference to the handle is dropped.

Thread 9 Crashed:
0   libswiftCore.dylib            	       0x1adbfe110 _assertionFailure(_:_:file:line:flags:) + 176
1   container-core-images         	       0x105773628 closure #1 in SystemFileHandle.deinit + 424 (SystemFileHandle.swift:131)
2   container-core-images         	       0x10577365c partial apply for closure #1 in SystemFileHandle.deinit + 20
3   container-core-images         	       0x104ebd994 closure #1 in LockStorage.withLockedValue<A>(_:) + 124 (NIOLock.swift:183)
4   container-core-images         	       0x104ebde38 partial apply for closure #1 in LockStorage.withLockedValue<A>(_:) + 52
5   container-core-images         	       0x104c387fc ManagedBuffer<>.withUnsafeMutablePointers<A, B>(_:) + 152
6   container-core-images         	       0x104ebd8ec LockStorage.withLockedValue<A>(_:) + 172 (NIOLock.swift:180)
7   container-core-images         	       0x104ebe088 NIOLockedValueBox.withLockedValue<A>(_:) + 104 (NIOLockedValueBox.swift:38)
8   container-core-images         	       0x105773438 SystemFileHandle.deinit + 112 (SystemFileHandle.swift:128)
9   container-core-images         	       0x105773684 SystemFileHandle.__deallocating_deinit + 28
10  libswiftCore.dylib            	       0x1adaeef50 _swift_release_dealloc + 56
11  libswiftCore.dylib            	       0x1adaefabc bool swift::RefCounts<swift::RefCountBitsT<(swift::RefCountInlinedness)1>>::doDecrementSlow<(swift::PerformDeinit)1>(swift::RefCountBitsT<(swift::RefCountInlinedness)1>, unsigned int) + 152
12  container-core-images         	       0x104b81aac RegistryClient.fetchBlob(name:descriptor:into:progress:) + 180 (RegistryClient+Fetch.swift:200)
13  container-core-images         	       0x104b9f481 protocol witness for ContentClient.fetchBlob(name:descriptor:into:progress:) in conformance RegistryClient + 1
14  container-core-images         	       0x1049b6b29 ImageStore.ImportOperation.fetchBlob(_:) + 1 (ImageStore+Import.swift:167)
15  container-core-images         	       0x1049b5c6d ImageStore.ImportOperation.fetch(_:) + 1 (ImageStore+Import.swift:153)
16  container-core-images         	       0x1049b4f69 closure #1 in closure #1 in ImageStore.ImportOperation.fetchAll(_:) + 1 (ImageStore+Import.swift:126)
17  container-core-images         	       0x1049bb08d partial apply for closure #1 in closure #1 in ImageStore.ImportOperation.fetchAll(_:) + 1
18  libswift_Concurrency.dylib    	       0x2945e05f9 completeTaskWithClosure(swift::AsyncContext*, swift::SwiftError*) + 1
```

Replacing the `SwiftNIO` file handle with the `Foundation` file handle
resolved the crash. However, this led to a worse performance:

```
Debug version:
SwiftNIO: 1:05s
Foundation: 1:18s (1.2 times worse)

Release version:
SwiftNIO: 0:34s
Foundation: 1:00s (1.8 times worse)
```

This PR attempts to prevent the crash without switching back to the
`Foundation` file handle.
2025-09-04 23:07:11 -07:00
Dmitry Kovba c3f5816353 Handle the same file name in the fetchBlob method (#258)
With the suggested change, when a file exists, we'll verify whether it
has the same content before skipping it.
2025-08-15 14:02:22 -04:00
Danny Canter 3cc638624f KeychainHelper: Simplify lookup (#214) 2025-07-17 09:37:59 -07:00
YR Chen 402339e248 Add a com.apple.containerization.index.indirect annotation to distinguish synthesized index (#198)
In general I believe it's clever to synthesize an index for
single-platform image manifest, but we still need a way to distinguish
it. Add a dedicated annotation is the slightest change I've come up
with, and it's also OCI compliant. With this change come in, we can work
around https://github.com/apple/container/issues/212 and imitate the
behavior of other runtime with `container`.

Note that since `cctl` is meant to be a dedicated tool for inspecting
the Containerization framework itself, I didn't apply the indirection
for it, and it will be as-is with the genuine storage.
2025-07-16 10:24:18 -07:00
Kathryn Baldauf ab559752b8 Handle keychain query errors in keychain helper lookup (#211)
Release containerization / deployDocs (push) Has been cancelled
Release containerization / Publish release (push) Has been cancelled
Release containerization / containerization (push) Successful in 1s
This allows us to handle errors from lookup with only the keychain
helper error types which simplifies the logic

related https://github.com/apple/container/pull/331

Signed-off-by: Kathryn Baldauf <k_baldauf@apple.com>
2025-07-14 11:40:30 -07:00
Aditya Ramani 016c80fac0 Better parsing for www-authenticate headers (#155)
There was a bug where the `www-authenticate` header in the HTTP response
from a registry would not be parsed accurately.

Specifically, if the header value had more than one `<space>` character,
the entire header would be ignored. This PR fixes this bug and adds unit
test to detect this in the future.


Fixes https://github.com/apple/container/issues/240
And most likely fixes https://github.com/apple/container/issues/237

Signed-off-by: Aditya Ramani <a_ramani@apple.com>
2025-06-20 15:51:47 -07:00
Danny Canter 1992cfe779 Containerization: Reduce allocations for image subsystems (#152)
Continue the allocations journey for anything that is in the codepaths
for pulling images. This time there's a couple spots in archive and ext4
we can get rid of some copies.
2025-06-18 16:11:46 -07:00
Alexey Makhov 689af841d6 Sort keys in image index to keep the digest consistent (#149)
The digest is calculated based on written json data, but JSONEncoder
doesn't preserve order bby default, so the digest is not consistent.

Signed-off-by: Alexey <makhov.alex@gmail.com>
2025-06-17 12:23:25 -07:00
Alexey Makhov 4a196b6dc2 Adds some context to fetchToken errors (#129)
Fixes https://github.com/apple/container/issues/182 and
https://github.com/apple/container/issues/183

Signed-off-by: Alexey <makhov.alex@gmail.com>
2025-06-17 08:26:46 -07:00
Danny Canter 328221e44c ContainerizationOCI: Cut down on allocations (#141)
We were doing intermediate step copies to Data objects both for push and
pull. We only need the data for the lifetime of the singular writes to
update the state of the checksums and to write to disk somewhere, so we
can use a view into the buffer from the http client to satisfy this.
2025-06-16 22:18:36 -07:00
Dmitry Kovba b14395515c Fix warnings in make docs (#139)
This PR also has additional small improvements.
2025-06-16 17:22:22 -07:00
Josh Soref c00ede68c1 fix comment misspellings (#131)
This PR corrects misspellings identified by the [check-spelling
action](https://github.com/marketplace/actions/check-spelling)

The misspellings have been reported at
https://github.com/jsoref/containerization/actions/runs/15662940240/attempts/1#summary-44123291170
The action reports that the changes in this PR would make it happy:
https://github.com/jsoref/containerization/actions/runs/15662940315/attempts/1#summary-44123291367

---------

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2025-06-16 16:06:37 -04:00
Aditya Ramani f4177e7d67 More informative errors from RegistryClient (#134)
Also creates an `ErrorResponse` type to model the errors typically
returned by a container registry.

Reference: https://distribution.github.io/distribution/spec/api/#errors

Example error message:
```
Error: HTTP request to https://ghcr.io/token?client_id=containerization-registry-client&service=ghcr.io&scope=repository:user/image:pull failed with response: 403 Forbidden. Reason: {"errors":[{"message":"requested access to the resource is denied","code":"DENIED"}]}
```

Signed-off-by: Aditya Ramani <a_ramani@apple.com>
2025-06-16 11:07:32 -07:00
Michael Crosby 5d2d7a1bc3 update license header removing new line (#99)
Fixes #63

Signed-off-by: crosbymichael <michael_crosby@apple.com>
2025-06-12 09:57:35 -04:00
Seyed Mojtaba Hosseini Zeidabadi 27db60af22 fix: structure in createOCILayoutStructure (#93)
Both the callee and caller names have been corrected.
2025-06-11 14:51:15 -04:00
Dmitry Kovba f245ae63b3 Improve @SendableProperty (#91)
This PR ensures that we enter a lock inside the `@SendableProperty`
implementation as soon as we access a computed property. Additionally,
it mirrors the access level of the original property. Both changes are
required for [improved
accuracy](https://github.com/apple/container/pull/144) of progress
updates in container. Additionally, it should resolve
https://github.com/apple/containerization/issues/60 that occurs on
certain configurations.

Please tag as 0.1.1 after merging.
2025-06-11 14:13:21 -04:00
Noritaka Kobayashi 4181e50775 refactor: fix typos (#64)
fix typos
2025-06-10 08:19:50 -07:00
Danny Canter fc4a124173 Continue documenting public surface (#25)
Signed-off-by: Danny Canter <danny_canter@apple.com>
2025-06-06 10:34:19 -04:00