-
Notifications
You must be signed in to change notification settings - Fork 206
feat(hip): OCI Artifact Selection and Referrers Support #424
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,270 @@ | ||
| --- | ||
| hip: "9999" | ||
| title: "OCI Artifact Selection and Referrers Support" | ||
| authors: ["Aleksei Sviridkin <[email protected]>"] | ||
| created: "2025-11-29" | ||
| type: "feature" | ||
| status: "draft" | ||
| --- | ||
|
|
||
| ## Abstract | ||
|
|
||
| This proposal enables Helm to select chart manifests from OCI Image Index manifests and adds support for the OCI Referrers API. Currently, `helm pull` fails when encountering an OCI Image Index containing multiple artifacts. This HIP introduces artifact selection logic based on the `artifactType` field (with fallback to `config.mediaType`), sets `artifactType` during `helm push`, and optionally allows charts to be associated with container images via the `subject` field and Referrers API. | ||
|
|
||
| ## Motivation | ||
|
|
||
| The OCI Image and Distribution specifications v1.1 (released February 2024) introduced native support for artifacts and the Referrers API. These features enable bundling multiple artifacts under a single OCI reference and establishing relationships between them. | ||
|
|
||
| Currently, users who want to publish both a Helm chart and a container image for the same application version must use workarounds: | ||
|
|
||
| - Tag suffixes (e.g., `:v1.0.0` for image, `:v1.0.0-helm` for chart) | ||
| - Separate repository paths (e.g., `registry/app` for image, `registry/app-chart` for chart) | ||
| - Completely separate registries | ||
|
|
||
| These workarounds introduce unnecessary complexity in CI/CD pipelines, break atomic versioning guarantees, and require additional tooling to keep artifacts synchronized. Tools like ArtifactHub, ArgoCD, and GitOps workflows would benefit from a single source of truth for versioned artifacts. | ||
|
|
||
| The OCI 1.1 specifications provide a standard solution: an Image Index can contain multiple manifests with different `artifactType` values, and the Referrers API allows querying artifacts associated with a specific image. Helm should leverage these capabilities. | ||
|
|
||
| ## Rationale | ||
|
|
||
| ### Why `artifactType` first, then `config.mediaType` fallback? | ||
|
|
||
| The OCI Image Specification explicitly states that "artifacts have historically been created without an `artifactType` field, and tooling to work with artifacts should fallback to the `config.mediaType` value." Following this guidance ensures compatibility with: | ||
|
|
||
| 1. Charts pushed by current Helm versions (which don't set `artifactType`) | ||
| 2. Charts pushed by third-party tools that may not set `artifactType` | ||
| 3. Registries that strip or don't preserve `artifactType` | ||
|
|
||
| ### Why skip descriptors with `platform` field? | ||
|
|
||
| Descriptors with a `platform` field are container images targeted at specific architectures. A Helm chart is not platform-specific and should never have a `platform` field. Skipping these descriptors avoids unnecessary manifest fetches. | ||
|
|
||
| ### Why use first match? | ||
|
|
||
| The OCI Image Index specification states: "If multiple manifests match a client or runtime's requirements, the first matching entry SHOULD be used." This behavior is consistent with multi-architecture image selection. | ||
|
|
||
| ### Why add Referrers API support? | ||
|
|
||
| The Referrers API enables discovering all artifacts (charts, SBOMs, signatures) associated with a specific container image. For Helm, this allows: | ||
|
|
||
| - Finding the chart that deploys a specific image version | ||
| - Ensuring chart and image are always used together | ||
| - Enabling security scanning workflows that link vulnerabilities to deployment configurations | ||
|
|
||
| ## Specification | ||
|
|
||
| This HIP introduces three related changes: | ||
|
|
||
| ### 1. Artifact Selection from Image Index | ||
|
|
||
| When `helm pull`, `helm install`, or `helm dependency update` encounters an OCI reference that resolves to an Image Index (`application/vnd.oci.image.index.v1+json`), Helm MUST select a chart manifest using the following algorithm: | ||
|
|
||
| 1. **First pass**: Iterate through descriptors in `manifests[]` and check for `artifactType: application/vnd.cncf.helm.config.v1+json` | ||
| - If exactly one descriptor matches, select it | ||
| - If multiple descriptors match, select the first one (per OCI spec) | ||
|
|
||
| 2. **Second pass** (fallback): If no match in first pass, iterate through descriptors WITHOUT a `platform` field: | ||
| - Fetch the referenced manifest | ||
| - Check if `config.mediaType` equals `application/vnd.cncf.helm.config.v1+json` | ||
| - If exactly one manifest matches, select it | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would think this would search for the first layer mediaType that is
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Layer mediaTypes are not exposed in Image Index descriptors - only these fields are available at the Index level:
To check The OCI artifact model specifically uses Example Index descriptor: {
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:def456...",
"size": 567,
"artifactType": "application/vnd.cncf.helm.config.v1+json"
// layers[] is NOT here - it's inside the manifest
}The fallback path (checking config.mediaType when artifactType is absent) already requires fetching the manifest. Adding layer inspection would not improve selection accuracy but would add complexity. |
||
| - If multiple manifests match, select the first one | ||
|
|
||
| 3. **Skip**: Descriptors with a `platform` field SHOULD be skipped as they represent container images | ||
|
|
||
| 4. **Error**: If no matching chart manifest is found, return an error indicating no Helm chart was found in the Image Index | ||
|
|
||
| Example Image Index with multiple artifacts: | ||
|
|
||
| ```json | ||
| { | ||
| "schemaVersion": 2, | ||
| "mediaType": "application/vnd.oci.image.index.v1+json", | ||
| "manifests": [ | ||
| { | ||
| "mediaType": "application/vnd.oci.image.manifest.v1+json", | ||
| "digest": "sha256:abc123...", | ||
| "size": 1234, | ||
| "platform": { | ||
| "architecture": "amd64", | ||
| "os": "linux" | ||
| } | ||
| }, | ||
| { | ||
| "mediaType": "application/vnd.oci.image.manifest.v1+json", | ||
| "digest": "sha256:def456...", | ||
| "size": 567, | ||
| "artifactType": "application/vnd.cncf.helm.config.v1+json" | ||
| } | ||
| ] | ||
| } | ||
| ``` | ||
|
|
||
| In this example, Helm would select the second descriptor (digest `sha256:def456...`) because it has the matching `artifactType`. | ||
|
|
||
| ### 2. Set `artifactType` on Push | ||
|
|
||
| When `helm push` creates a manifest, it MUST set the `artifactType` field to `application/vnd.cncf.helm.config.v1+json`. | ||
|
|
||
| Current manifest structure: | ||
|
|
||
| ```json | ||
| { | ||
| "schemaVersion": 2, | ||
| "mediaType": "application/vnd.oci.image.manifest.v1+json", | ||
| "config": { | ||
| "mediaType": "application/vnd.cncf.helm.config.v1+json", | ||
| "digest": "sha256:...", | ||
| "size": 137 | ||
| }, | ||
| "layers": [...] | ||
| } | ||
| ``` | ||
|
|
||
| Proposed manifest structure: | ||
|
|
||
| ```json | ||
| { | ||
| "schemaVersion": 2, | ||
| "mediaType": "application/vnd.oci.image.manifest.v1+json", | ||
| "artifactType": "application/vnd.cncf.helm.config.v1+json", | ||
| "config": { | ||
| "mediaType": "application/vnd.cncf.helm.config.v1+json", | ||
| "digest": "sha256:...", | ||
| "size": 137 | ||
| }, | ||
| "layers": [...] | ||
| } | ||
| ``` | ||
|
|
||
| This enables efficient selection from Image Index without fetching manifest content. | ||
|
|
||
| ### 3. Referrers API Support | ||
|
|
||
| A new optional flag `--subject` is added to `helm push`: | ||
|
|
||
| ```bash | ||
| helm push mychart-1.0.0.tgz oci://registry.example.com/myapp --subject sha256:abc123... | ||
| ``` | ||
|
|
||
| When `--subject` is specified, Helm MUST: | ||
|
|
||
| 1. Set the `subject` field in the chart manifest to reference the specified digest: | ||
|
|
||
| ```json | ||
| { | ||
| "schemaVersion": 2, | ||
| "mediaType": "application/vnd.oci.image.manifest.v1+json", | ||
| "artifactType": "application/vnd.cncf.helm.config.v1+json", | ||
| "subject": { | ||
| "mediaType": "application/vnd.oci.image.manifest.v1+json", | ||
| "digest": "sha256:abc123...", | ||
| "size": 1234 | ||
| }, | ||
| "config": {...}, | ||
| "layers": [...] | ||
| } | ||
| ``` | ||
|
|
||
| 2. Handle the registry response according to OCI Distribution Spec 1.1: | ||
| - If registry returns `OCI-Subject` header, the referrer is tracked automatically | ||
| - If registry does not return `OCI-Subject` header, fall back to Referrers Tag Schema | ||
|
|
||
| The `--subject` flag accepts: | ||
| - A digest (e.g., `sha256:abc123...`) | ||
| - An OCI reference that will be resolved to a digest (e.g., `oci://registry.example.com/myapp:v1.0.0`) | ||
|
|
||
| ## Backwards Compatibility | ||
|
|
||
| This proposal is fully backwards compatible: | ||
|
|
||
| 1. **Single manifests**: Charts stored as single manifests (not Image Index) continue to work unchanged | ||
| 2. **Existing charts**: Charts without `artifactType` are still selectable via `config.mediaType` fallback | ||
| 3. **Registry compatibility**: `artifactType` is an optional field per OCI spec; registries MUST NOT error on unknown fields | ||
| 4. **Optional Referrers**: The `--subject` flag is optional; existing `helm push` workflows are unchanged | ||
|
|
||
| ## Security Implications | ||
|
|
||
| 1. **No new attack vectors**: Artifact selection uses the same validation logic applied after manifest fetch | ||
| 2. **Referrers association is weak**: The `subject` field creates an association, not a cryptographic binding. Chart integrity still depends on digest verification | ||
| 3. **Registry trust model unchanged**: Users must still trust the registry to serve correct manifests | ||
|
|
||
| ## How to Teach This | ||
|
|
||
| ### Documentation Updates | ||
|
|
||
| 1. Update the OCI registry documentation on helm.sh to explain Image Index support | ||
| 2. Add examples showing how to create multi-artifact Image Index using tools like `crane` or `oras` | ||
| 3. Document the `--subject` flag and Referrers API integration | ||
|
|
||
| ### Example: Creating Multi-Artifact Image Index | ||
|
|
||
| ```bash | ||
| # Push container image | ||
| docker push registry.example.com/myapp:v1.0.0 | ||
|
|
||
| # Push Helm chart | ||
| helm push myapp-1.0.0.tgz oci://registry.example.com/myapp | ||
|
|
||
| # Create Image Index combining both | ||
| crane index append \ | ||
| --manifest registry.example.com/myapp:v1.0.0 \ | ||
| --manifest registry.example.com/myapp:v1.0.0-helm \ | ||
| --tag registry.example.com/myapp:v1.0.0 | ||
|
|
||
| # Now helm pull works on the combined reference | ||
| helm pull oci://registry.example.com/myapp --version 1.0.0 | ||
| ``` | ||
|
|
||
| ### Example: Associating Chart with Image | ||
|
|
||
| ```bash | ||
| # Get image digest | ||
| IMAGE_DIGEST=$(crane digest registry.example.com/myapp:v1.0.0) | ||
|
|
||
| # Push chart with subject reference | ||
| helm push myapp-1.0.0.tgz oci://registry.example.com/myapp --subject $IMAGE_DIGEST | ||
|
|
||
| # Query referrers (using oras) | ||
| oras discover registry.example.com/myapp@$IMAGE_DIGEST | ||
| ``` | ||
|
|
||
| ## Reference Implementation | ||
|
|
||
| A reference implementation is available at [helm/helm#31583](https://github.com/helm/helm/pull/31583). | ||
|
|
||
| ## Rejected Ideas | ||
|
|
||
| ### Requiring explicit `--artifact-type` flag | ||
|
|
||
| Rejected because it adds unnecessary verbosity to common operations. The artifact type is always `application/vnd.cncf.helm.config.v1+json` for Helm charts. | ||
|
|
||
| ### Only supporting `artifactType` without `config.mediaType` fallback | ||
|
|
||
| Rejected because it would break compatibility with existing charts and third-party tooling that doesn't set `artifactType`. | ||
|
|
||
| ### Making `--subject` mandatory | ||
|
|
||
| Rejected because most users don't need Referrers API integration. The feature should be opt-in. | ||
|
|
||
| ### Automatic subject detection | ||
|
|
||
| Considered automatically detecting the "main" image in an Image Index and setting it as subject. Rejected because: | ||
| - Ambiguous when multiple images exist | ||
| - May not match user intent | ||
| - Explicit is better than implicit | ||
|
|
||
| ## Open Issues | ||
|
|
||
| 1. **Fetching chart via Referrers API**: Should `helm pull` support fetching a chart by image digest using the Referrers API? For example: `helm pull --referrer-of oci://registry.example.com/myapp@sha256:abc123...` | ||
|
|
||
| 2. **Multiple charts per image**: What should happen when multiple charts reference the same image via Referrers API? Current proposal: return all, let user select. | ||
|
|
||
| ## References | ||
|
|
||
| - [OCI Image Specification v1.1](https://github.com/opencontainers/image-spec/releases/tag/v1.1.0) | ||
| - [OCI Distribution Specification v1.1](https://github.com/opencontainers/distribution-spec/releases/tag/v1.1.0) | ||
| - [OCI Image Index Specification](https://github.com/opencontainers/image-spec/blob/main/image-index.md) | ||
| - [OCI Descriptor Specification](https://github.com/opencontainers/image-spec/blob/main/descriptor.md) | ||
| - [HIP-0006: OCI Support](https://github.com/helm/community/blob/main/hips/hip-0006.md) | ||
| - [helm/helm#31582: Support for OCI Image Index](https://github.com/helm/helm/issues/31582) | ||
| - [helm/helm#31583: Reference Implementation](https://github.com/helm/helm/pull/31583) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wasn't around for the helm OCI implementation, so I don't know the thinking, but I assumed
application/vnd.cncf.helm.config.v1+jsonrepresented the format of the config blob. If that is the case, it is almost as if there would need to be a new type something likeapplication/vnd.cncf.helm.manifest.v1+jsonThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're correct that
application/vnd.cncf.helm.config.v1+jsonrepresents the config blob format. However, using the same value forartifactTypeis intentional per OCI spec.The OCI Image Spec (descriptor.md) explicitly defines
artifactTypein Index descriptors as:This design allows clients to select manifests from an Image Index without fetching them first - the
artifactTypein the descriptor tells you what kind of artifact it is by exposing theconfig.mediaTypevalue at the Index level.So the equality
artifactType == config.mediaTypeis by design, not a naming collision:artifactType= identifier for selectionconfig.mediaType= format of the config blobBoth happen to have the same value because OCI spec defines it that way. Creating a separate type like
application/vnd.cncf.helm.manifest.v1+jsonwould contradict the OCI artifact model and require IANA registration for no functional benefit.Reference: https://github.com/opencontainers/image-spec/blob/main/descriptor.md#properties