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
218 changes: 218 additions & 0 deletions hips/hip-9999.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
---
hip: "9999"
title: "Separate OCI pull identity from chart identity for dependencies"
authors: [ "BeganovR <446110@niuitmo.ru>", "Enin Kaduk" ]
created: "2026-08-10"
type: "feature"
status: "draft"
---

## Abstract

For OCI dependencies, `Chart.yaml`'s `dependencies[].name` currently has to
serve two roles at once: it is the chart's identity (compared by `helm lint`
against the vendored subchart's own `Chart.yaml` name, and used to wire
`alias`/values), and it is the basename Helm appends to the OCI repository to
build the pull reference (`oci://<repo>/<name>:<version>`). When a chart is
published or mirrored under a registry path whose basename differs from its
own `name` — common with registry mirroring or organization naming prefixes —
there is no way to satisfy both roles at once. This HIP proposes an optional
`artifactName` field on `dependencies[]` entries that carries the OCI pull
basename separately from `name`, so `name` can always represent chart
identity.

## Motivation

`helm dependency update` resolves and downloads each dependency, then vendors
it under `charts/`. For OCI repositories, the pull reference is built as
`oci://<repository>/<name>:<version>`, so `name` doubles as the registry
basename. `helm lint` separately checks that `dependencies[].name` in the
parent `Chart.yaml` matches the vendored subchart's own declared `name`.

These two uses of `name` conflict whenever a chart is republished or mirrored
under a different OCI basename than its own identity, e.g.
`oci://example.com/org/dhi-metrics-server-chart:3.13.1` for a chart whose own
`Chart.yaml` declares `name: metrics-server-chart`. Setting `name` to the OCI
basename lets `helm dependency update` pull the artifact, but `helm lint` then
fails with a paired error and warning, since the vendored chart's real
identity no longer matches the parent's `dependencies[].name`:

```text
[ERROR] chart metadata is missing these dependencies: metrics-server-chart
[WARNING] chart directory is missing these dependencies: dhi-metrics-server-chart
```

There is currently no supported way to express both identities in
`Chart.yaml`. Users hitting this today have to choose between two broken
options: set `dependencies[].name` to the OCI basename, so
`helm dependency update` works but `helm lint` fails; or set it to the chart
identity, so lint can pass only by abandoning `repository` and vendoring the
chart into `charts/` by hand. Neither is workable for charts pulled through a
registry mirror or organization naming convention the user doesn't control
(e.g. third-party or vendor-mirrored charts prefixed with `dhi-`).

This is compounded by Helm's own OCI strict naming policy, which today
requires "the basename of the registry reference [to] always match the
chart's name" for anything pushed with `helm push` — so the split described
here can currently only arise from registries populated by other tools (e.g.
mirroring), not from `helm push` itself. That said, this HIP does not depend
on relaxing strict-mode `helm push`; it only addresses *consuming* a
dependency that already lives under a different basename.

This is **not** a request to weaken `helm lint`. The current lint output is
correctly describing a genuinely underspecified model: today there is only
one field to hold two different identities, so lint has no way to tell a real
mismatch apart from a mirrored basename. Once pull identity and chart
identity are separate fields, lint keeps comparing chart identity to the
vendored chart's own name exactly as it does today, and continues to catch
real mismatches — it simply stops being fed the OCI basename as if it were
the chart's name.

This was reported and discussed in
[helm/helm#32529](https://github.com/helm/helm/issues/32529), which also
links prior discussions of the same coupling: #9627, #9386, #9278, and
#11127.

## Rationale

The fix keeps `name` as the sole source of chart identity everywhere it is
used today (lint, `charts/` vendoring, alias/values wiring), and adds a
second, purely OCI-facing field for the pull basename. This is additive and
opt-in: dependencies that don't set the new field keep behaving exactly as
before, since the pull basename still falls back to `name`.

Alternatives considered and rejected:

- **Allow `repository` to be a full OCI reference including the artifact
basename.** This overloads a field that today is documented as a registry
path, and would need every OCI-consuming code path to detect and strip a
trailing name segment, which is more invasive than adding one field.
- **Add a chart-level override instead of a per-dependency field.** A chart
can have multiple OCI dependencies pulled from differently-named artifacts,
so the override has to be per-dependency.
- **Reuse `alias`.** `alias` already has an established meaning (the values
key a subchart is addressed by) and is not tied to OCI at all; conflating
it with the pull basename would change existing, unrelated behavior.

## Specification

A new optional string field, `artifactName`, is added to `dependencies[]`
entries in `Chart.yaml`:

```yaml
dependencies:
- name: metrics-server-chart
artifactName: dhi-metrics-server-chart
version: 3.13.1
repository: oci://example.com/org
```

- `name` keeps its existing meaning: the chart's identity, matched against
the vendored subchart's own `Chart.yaml` `name` by `helm lint`, and used to
wire `alias`/values.
- `artifactName`, when set, is the basename used to build the OCI pull
reference (`oci://<repository>/<artifactName>:<version>`) instead of `name`.
It only affects OCI repositories; it has no effect for `http(s)` chart
repositories or local (`file://`/subfolder) dependencies.
- If `artifactName` is empty or unset, the pull reference is built from `name`
as it is today — fully backwards compatible.
- `helm dependency update` builds the pull reference from `artifactName`
(falling back to `name`), downloads and vendors the chart under `charts/`
using the vendored chart's own declared name (unaffected by this change),
and `Chart.lock` records `artifactName` alongside `name` so that
`helm dependency build` (which reads the lock, not `Chart.yaml`) continues
to pull correctly without re-resolving.
- `helm lint` requires no changes: it already compares `dependencies[].name`
against the vendored subchart's own name, with no OCI-specific logic. Once
`name` is no longer forced to double as the OCI basename, the two values
it's compared against naturally agree.

In short:

| `artifactName` set? | OCI pull basename | `charts/` / lint identity |
|---|---|---|
| No (default) | `name` | `name` |
| Yes | `artifactName` | `name` (unchanged) |

## Backwards compatibility

This is a fully additive, opt-in change. Existing `Chart.yaml` files have no
`artifactName` field, so every dependency continues to resolve its OCI pull
basename from `name`, exactly as today. `Chart.lock` gains one new optional
field per dependency; older Helm versions reading a lock file produced by a
newer Helm simply ignore the unknown field. There is no change to the
on-disk layout of vendored charts, to `helm lint`'s behavior for charts that
don't use this field, or to non-OCI repositories.

## Security implications

None identified. `artifactName` is sanitized the same way other
`dependencies[]` string fields already are (control characters stripped),
and is only ever used to construct an OCI reference the same way `name` is
today — it does not introduce a new class of input reaching the registry
client.

## How to teach this

This is an opt-in field that only needs documentation where it applies:
users who publish or consume OCI charts under a registry path whose basename
differs from the chart's own name. The `Chart.yaml` reference documentation
and the OCI dependencies guide should note `artifactName` alongside `name`,
`version`, and `repository`, with the mirrored-registry scenario above as the
motivating example. No changes are needed to the documentation for
non-OCI/HTTP repositories, since the field has no effect there.

## Reference implementation

A reference implementation exists as a local branch against `helm/helm`
(`oci-dependency-artifact-name`), covering:

- `pkg/chart/v2/dependency.go`: adds `ArtifactName` to the `Dependency`
struct, with the same sanitization applied to other string fields in
`Validate()`.
- `pkg/downloader/manager.go`: `downloadAll()` builds the OCI pull reference
from `ArtifactName` when set (falling back to `Name`), instead of always
using `Name`.
- `internal/resolver/resolver.go`: `Resolve()` uses `ArtifactName` for OCI
tag listing when no exact version is pinned, and persists `ArtifactName`
into the resulting `Chart.lock` entries.
- Unit tests covering sanitization and lock resolution, and an end-to-end
test (`pkg/cmd/dependency_update_test.go`) that publishes a fixture chart
under a differing OCI basename, runs `helm dependency update`, and asserts
both that the artifact was pulled by its OCI basename and that
`helm lint` no longer reports a dependency mismatch.

This branch will be opened as a pull request against `helm/helm` once this
HIP has been reviewed.

## Rejected ideas

- **Overloading `repository`, a chart-level override, reusing `alias`.** See
[Rationale](#rationale) for why each was set aside in favor of a
per-dependency `artifactName` field.
- **Relaxing `helm lint` to stop reporting the name mismatch, instead of
adding a field.** This was considered and rejected: the mismatch lint
reports today is a real signal, not a false positive — it means the parent
chart's declared dependency and the actually-vendored chart disagree on
identity. The right fix is to stop forcing `name` to carry two meanings, not
to make lint quieter about a case it's correctly catching.

## Open issues

- **Field name.** This HIP uses `artifactName`; the originating issue also
considered `ociArtifact`. `artifactName` was picked because it doesn't
imply OCI-only scope in the name itself, but this is open to maintainer
preference and doesn't affect the rest of the design.

## References

- [helm/helm#32529](https://github.com/helm/helm/issues/32529) — original bug
report and discussion this HIP addresses.
- [helm/helm#9627](https://github.com/helm/helm/issues/9627),
[helm/helm#9386](https://github.com/helm/helm/issues/9386),
[helm/helm#9278](https://github.com/helm/helm/issues/9278),
[helm/helm#11127](https://github.com/helm/helm/issues/11127) — earlier
discussions of the same OCI basename / chart name coupling.
- [Use OCI-based registries — Specifying dependencies](https://helm.sh/docs/topics/registries/#specifying-dependencies)
and the [strict naming policy](https://helm.sh/docs/topics/registries/) it
documents.