Interoperability with Helm

jhelm and the Go helm CLI are interchangeable at the release-storage layer: they read and write the same on-cluster release records, so you can install a release with one tool and list, inspect, upgrade, roll back, or uninstall it with the other. This page documents what is compatible, how to switch between the tools, and the current caveats.

This page is about storage- and config-level interoperability (release records, repo/registry config), verified by a two-way integration suite. It is a separate axis from template-rendering parity — how closely jhelm’s rendered manifests match helm template — which is measured by the chart-parity suite and documented in Function coverage. A release installed by either tool is manageable by the other; the rendered content itself is byte-for-byte equal modulo the documented parity comparison-ignores.

This interoperability is covered by a two-way integration suite (HelmTwoWayInteropIntegrationTest) that runs against a real cluster and the real helm binary in CI, so it does not silently regress. See Development Guide for running the integration tests.

1. Release storage format

Helm stores each release revision in a Kubernetes Secret in the release’s namespace. jhelm writes byte-compatible records:

Aspect Value

Secret name

sh.helm.release.v1.<release>.v<revision> (e.g. sh.helm.release.v1.wordpress.v3)

Secret type

helm.sh/release.v1

Labels

owner=helm, name=<release>, status=<status>, version=<revision> — these are what helm list and jhelm’s list query on.

Payload (data.release)

The release record as JSON, gzip-compressed, then base64-encoded (the Kubernetes client adds its own base64 layer on top).

The JSON payload follows Helm’s release schema:

  • release info timestamps are the snake_case first_deployed / last_deployed;

  • config (the user-supplied values) is a bare map, exactly what helm get values returns;

  • status uses Helm’s wire strings (deployed, superseded, pending-install, …);

  • empty fields are omitted (omitempty), and fields Helm writes but jhelm does not model (e.g. hooks, labels) are tolerated on read;

  • the embedded chart matches Helm’s chart JSON — template and file content is base64-encoded, files is a [{name, data}] array, and the values schema is the base64 schema field.

2. What works across both tools

Given the same cluster and namespace (jhelm uses your active kubeconfig, just like helm):

Installed by Managed by Works

jhelm

helm

helm list, helm status, helm get values/manifest/notes, helm history, helm upgrade, helm rollback, helm uninstall

helm

jhelm

list, get (values/manifest/notes), status, history, upgrade, rollback, uninstall — jhelm decodes Helm’s release record, including the embedded chart

Because revisions are shared, you can interleave the tools freely — for example install with helm, upgrade with jhelm, then roll back with helm again. Each tool writes the next revision in the shared format and supersedes the previous one.

3. Switching between the tools

There is nothing to migrate or import. Point both tools at the same cluster/namespace and they operate on the same releases:

# install with helm
helm install wordpress ./wordpress -n web

# inspect and upgrade the same release with jhelm
jhelm list -n web
jhelm status wordpress -n web
jhelm upgrade wordpress ./wordpress -n web --set replicaCount=3

# roll back with helm
helm rollback wordpress -n web

Repository and registry configuration is shared too — see the next section.

4. Shared configuration with Helm

jhelm reads Helm’s own configuration files, at Helm’s own locations, honoring the same HELM_* environment variables. In most setups there is nothing to configure: what you set up for helm already applies to jhelm.

Helm setting jhelm behaviour Shared?

Repositoriesrepositories.yaml ($HELM_REPOSITORY_CONFIG, default ~/.config/helm/repositories.yaml)

Reads/writes the same file. Honors $HELM_REPOSITORY_CONFIG; override with the jhelm.config-path property.

✅ drop-in

Registry authregistry/config.json ($HELM_REGISTRY_CONFIG, default ~/.config/helm/registry/config.json)

Reads/writes the same docker-style credentials file. Honors $HELM_REGISTRY_CONFIG; override with jhelm.registry-config-path.

✅ drop-in

Release storagesh.helm.release.v1.* Secrets

Same format and location (see the release-storage section above).

✅ drop-in

Kubeconfig$KUBECONFIG, ~/.kube/config, in-cluster

Uses the standard Kubernetes client resolution ($KUBECONFIG~/.kube/config → in-cluster); override with jhelm.kubernetes.kubeconfig-path.

✅ drop-in

Repository cache — index files ($HELM_REPOSITORY_CACHE, default ~/.cache/helm/repository)

Honors $HELM_REPOSITORY_CACHE; otherwise uses a jhelm-native default (~/.cache/jhelm/repository) so the two tools' caches don’t collide by default.

⚠️ jhelm-native default, shareable via env

Namespace$HELM_NAMESPACE, kube-context namespace

jhelm does not read $HELM_NAMESPACE; the namespace defaults to default and is set per-command with -n/--namespace.

⚠️ jhelm-native

Because the repository, registry, and release stores are shared, the tools interleave directly:

# add a repo and log in to a registry with helm
helm repo add bitnami https://charts.bitnami.com/bitnami
helm registry login registry.example.com -u ci -p "$TOKEN"

# jhelm sees both immediately — same repositories.yaml, same registry/config.json
jhelm search repo bitnami
jhelm pull oci://registry.example.com/charts/app --version 1.2.3

To point every tool at an isolated config tree (common in CI), set the HELM_* variables once and both honor them:

export HELM_REPOSITORY_CONFIG=/work/helm/repositories.yaml
export HELM_REGISTRY_CONFIG=/work/helm/registry/config.json
export HELM_REPOSITORY_CACHE=/work/helm/cache
# helm … and jhelm … now share this tree

Settings that are jhelm’s own — cache tuning, security, or the paths above when you prefer a property over an env var — live in Spring Boot configuration (application.yaml, environment variables, or -D), documented in Configuration. The HELM_* variables and jhelm. properties resolve in this order: explicit jhelm. property > HELM_* environment variable > per-OS Helm default.

5. Caveats

  • jhelm-only chart fields. jhelm’s in-memory chart carries a few fields that are not part of Helm’s chart schema (for example a separate CRD list); Helm ignores unknown fields, so they do not break helm, but they are not surfaced by it either.

  • Hooks. Hook records that Helm stores in the release are preserved on read but are not yet modelled by jhelm; they round-trip as-is when jhelm rewrites a release.

  • Helm 3 format. The helm.sh/release.v1 Secret format targets Helm 3. Helm 2’s ConfigMap / Tiller storage is not supported.

For the exact model, see the Release and HelmReleaseCodec types in jhelm-core.