Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
278 changes: 278 additions & 0 deletions hips/hip-9999.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,278 @@
---
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 `artifactType` equals `config.mediaType`

The OCI Image Spec (descriptor.md) explicitly defines `artifactType` in Index descriptors as "the value of the config descriptor `mediaType` when the descriptor references an image manifest." This equality is intentional — it exposes the artifact identifier at the Index level for efficient selection without fetching manifests. Using `application/vnd.cncf.helm.config.v1+json` for both fields is correct per OCI spec, not a naming collision.

### Why not use layer mediaType for selection

Layer mediaTypes (e.g., `application/vnd.cncf.helm.chart.content.v1.tar+gzip`) are not exposed in Image Index descriptors — only `mediaType`, `digest`, `size`, `platform`, `artifactType`, and `annotations` are available. To check layers, Helm would need to fetch every manifest in the Index first, defeating the purpose of efficient artifact selection. The `artifactType`/`config.mediaType` approach provides artifact identification at the Index level without additional round-trips.

### 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`

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+json represented 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 like application/vnd.cncf.helm.manifest.v1+json

Copy link
Author

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+json represents the config blob format. However, using the same value for artifactType is intentional per OCI spec.

The OCI Image Spec (descriptor.md) explicitly defines artifactType in Index descriptors as:

"This is the value of the config descriptor mediaType when the descriptor references an image manifest."

This design allows clients to select manifests from an Image Index without fetching them first - the artifactType in the descriptor tells you what kind of artifact it is by exposing the config.mediaType value at the Index level.

So the equality artifactType == config.mediaType is by design, not a naming collision:

  • In Image Index descriptor: artifactType = identifier for selection
  • In Image Manifest: config.mediaType = format of the config blob

Both happen to have the same value because OCI spec defines it that way. Creating a separate type like application/vnd.cncf.helm.manifest.v1+json would 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

- 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

Choose a reason for hiding this comment

The 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 application/vnd.cncf.helm.chart.content.v1.tar+gzip

Copy link
Author

@lexfrei lexfrei Dec 2, 2025

Choose a reason for hiding this comment

The 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:

  • mediaType (of the manifest itself, always application/vnd.oci.image.manifest.v1+json)
  • digest, size
  • platform (for container images)
  • artifactType (for artifacts, OCI 1.1+)
  • annotations

To check layers[].mediaType, we would need to fetch every manifest in the Index first, then inspect each one. This defeats the purpose of efficient artifact selection.

The OCI artifact model specifically uses artifactType (derived from config.mediaType) because it's available at the Index level without additional round-trips.

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.

Reference: https://github.com/opencontainers/image-spec/blob/main/image-index.md#image-index-property-descriptions

- 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)