Get Harthelm
Where the chart, the images and the release files come from: the registry login of your licence, signatures, SBOMs, air-gapped bundles and the source code.
Every Harthelm release is published on two hosts Since 1.6.0:
| Host | Holds | Access |
|---|---|---|
registry.harthelm.dev | the images and the chart, as an OCI registry | a login with your licence id and registry token |
get.harthelm.dev | the release files: hart, checksums, SBOMs, the air-gapped bundles, the source code | public HTTPS, no login |
Releases before 1.6.0 are not on these hosts; take them from the registry you got them from.
What comes with your licence
With your licence you receive two things:
- the licence file, which you install into Harthelm (Licence);
- the registry token, the password for
registry.harthelm.dev. The user name is your licence id, the id thathart admin licenseprints afterLicence:, for exampleHL-2026-0123.
The token belongs to one installation: each installation gets its own. It is shown once, when it is delivered, so put it into your secret store right away. It carries your licence's end date and stops working when the licence ends. If you lose it or it leaks, ask for a new one; the old one is then revoked.
When the token stops working
After a licence ends or is revoked, the registry refuses its token at the next login, at the latest five minutes
later, with 401 Unauthorized. Pods that run keep running, but a node that has to pull an image it does not have
yet (a new node, a workspace that wakes on another node) fails. An installation that pulls from
its own registry needs the token only to fill it.
Log in
Keep the token in a file that only you can read, here registry-token, so it stays out of your shell history. Every
client logs in with the licence id as user name:
helm registry login registry.harthelm.dev --username <licence id> --password-stdin < registry-token
docker login registry.harthelm.dev --username <licence id> --password-stdin < registry-tokenHelm keeps its own login. cosign and skopeo read Docker's; without Docker, log them in themselves:
cosign login registry.harthelm.dev --username <licence id> --password-stdin < registry-token
skopeo login registry.harthelm.dev --username <licence id> --password-stdin < registry-tokenA wrong id or token, or a licence that has ended, gets 401 Unauthorized. The registry has no anonymous access.
What the registry holds
Every image is one level below registry.harthelm.dev/harthelm/, and every image is multi-arch (linux/amd64 and
linux/arm64):
| Image | Tag |
|---|---|
harthelm, harthelm-agent, harthelm-base, harthelm-ide, harthelm-tools | the release, for example 1.6.0 |
postgresql (17.x and 18.x), mariadb (the 11.4, 11.8 and 12.3 series), valkey, rabbitmq, seaweedfs | the vendor's own tag |
cloudnative-pg, cert-manager-controller, cert-manager-cainjector, cert-manager-webhook, cert-manager-startupapicheck | the vendor's own tag |
The last row holds the operators the chart needs, so you can install them from the same registry too (their charts
take an image registry value). The chart is oci://registry.harthelm.dev/harthelm/charts/harthelm. It pins every image by
digest as registry.harthelm.dev/harthelm/<image>@sha256:…, so an install from this registry sets no
global.imageRegistry.
Release candidates (1.6.0-rc.1 and so on) may appear in the registry too. Install releases only: Helm skips
pre-releases when it resolves a version range unless you pass --devel, and you always pass the exact version.
Install from registry.harthelm.dev
The cluster's nodes pull the images with your licence's login too. Give them a pull Secret in the release namespace,
harthelm-system (created in Install):
kubectl -n harthelm-system create secret docker-registry harthelm-pull \
--docker-server=registry.harthelm.dev --docker-username=<licence id> \
--docker-password="$(cat registry-token)"and name it in your values:
global:
imagePullSecrets:
- name: harthelm-pullThe chart's pods list it, and core copies it into every team namespace and lists it on the workspace, service and
managed-database pods there. The preflight checks that it exists with the type kubernetes.io/dockerconfigjson. Names
that start with ws- are refused. See
global.imagePullSecrets.
After helm registry login:
helm install harthelm oci://registry.harthelm.dev/harthelm/charts/harthelm --version 1.6.0 \
-n harthelm-system --create-namespace -f my-values.yamlInstall has the steps before (operators, DNS, sign-in, master key, licence) and after it;
its <your registry> is registry.harthelm.dev/harthelm.
Install from your own registry
To keep the images in a registry of your own, mirror them from the release's images-public.txt with its
mirror.sh (the release files), on a host that reaches both registries and is logged in to
registry.harthelm.dev. skopeo reads that login; SKOPEO_SRC_FLAGS="--src-creds <licence id>:<registry token>"
passes it instead.
./mirror.sh registry.example.internal images-public.txt
./mirror.sh --verify registry.example.internal images-public.txtThe repository paths stay, so the images land at registry.example.internal/harthelm/<image> with the same digests.
--verify only reads: it asks your registry for every image and compares the digest. Then install the public chart,
from the registry or as harthelm-<version>-public.tgz, with your registry in the values:
global:
imageRegistry: registry.example.internalVerify the signatures before you mirror: the copies do not carry them. Air-gapped installs has the rest: the chart in your registry, the operators and the mirrors the workspaces need.
Verify the signatures
Every image of a release and its chart are signed with Harthelm's release key, with cosign. A multi-arch image has a
signature on its index and on each platform's manifest. The signatures are stored next to what they sign in
registry.harthelm.dev. They use no transparency log, so you verify with the public key alone and pass
--insecure-ignore-tlog (cosign warns about it). Use cosign 3 or newer, logged in to the registry as
above.
The release key
curl -fsSLO https://docs.harthelm.dev/cosign.pub
sha256sum cosign.pub # macOS: shasum -a 256 cosign.pubThe checksum is 50fdf235ee7d88278f787bc434f929a01ce56ac5c65c392638ecf4862c74f77a, and a copy of the key you got another way must have the same. The file, cosign.pub, holds:
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeyIf7om4hH4S4MA3LYxv7XYs+H69
DlTjcooCJsZds6ERhcEvE1Pbuj59n103EgIcTV/0VJtIM1O4T7NyFHsWRQ==
-----END PUBLIC KEY-----Images
Take an image's digest from images-public.txt and verify it:
cosign verify --key cosign.pub --insecure-ignore-tlog \
registry.harthelm.dev/harthelm/harthelm@sha256:<digest>cosign lists the checks it made, prints what it verified as JSON and exits 0. Any other result means the image is not one that Harthelm released. To verify every image of the release:
grep -v '^#' images-public.txt | while read -r ref; do
[ -n "$ref" ] || continue
repo=${ref%@*}
if cosign verify --key cosign.pub --insecure-ignore-tlog "${repo%:*}@${ref#*@}" > /dev/null 2>&1; then
echo "ok $ref"
else
echo "FAILED $ref"
fi
doneThe chart
helm pull oci://registry.harthelm.dev/harthelm/charts/harthelm --version 1.6.0Helm prints the chart's Digest: sha256:…. Verify that digest:
cosign verify --key cosign.pub --insecure-ignore-tlog \
registry.harthelm.dev/harthelm/charts/harthelm@sha256:<digest>The chart pins every image by digest, so a verified chart also fixes the images it starts.
Mirrors and bundles
A mirror made with mirror.sh holds the images without their signatures, and the air-gapped bundles hold none
either. Verify on the connected side, before you mirror or carry a bundle over. A bundle's images.txt names the
digests of the platform manifests, which are signed too, so the loop above works on it as well (each line is
registry.harthelm.dev/harthelm/<image>:<tag>@<platform digest>). The bundle's own chart is pinned to those digests
when the bundle is made and is not signed; the bundle's checksum covers it.
Read the SBOMs
harthelm-<version>-sboms.tar.gz holds one SPDX 2.3 JSON document per Harthelm image and platform: ten files, five
images on two platforms, made with syft from the released images.
v=1.6.0
sha256sum -c "harthelm-$v-sboms.tar.gz.sha256" # macOS: shasum -a 256 -c
tar -xzf "harthelm-$v-sboms.tar.gz"
ls sbom/The files are sbom/harthelm-<image>-<version>-linux-<arch>.spdx.json, for example
sbom/harthelm-harthelm-agent-1.6.0-linux-arm64.spdx.json. List the packages of one, with their version and licence:
jq -r '.packages[] | [.name, .versionInfo, .licenseConcluded] | @tsv' \
sbom/harthelm-harthelm-1.6.0-linux-amd64.spdx.jsonA vulnerability scanner that reads SPDX takes the files as they are, for example
grype sbom:sbom/harthelm-harthelm-1.6.0-linux-amd64.spdx.json. The managed services' and the operators' images come
from their vendors, and so do their SBOMs.
The release files
Every release has its files under https://get.harthelm.dev/releases/<version>/:
| File | What |
|---|---|
harthelm-<version>-public.tgz | the chart, as in the registry |
images-public.txt | every image of the release, one per line: registry.harthelm.dev/harthelm/<image>:<tag>@sha256:<digest> |
mirror.sh | copies the images into your registry (above) |
hart_<version>_<os>_<arch>.tar.gz, hart_<version>_SHA256SUMS | hart for darwin and linux on amd64 and arm64, and the checksums of the four archives |
harthelm-<version>-sboms.tar.gz and .sha256 | the SBOMs |
harthelm-restore-<version>.tar.gz and .sha256 | the restore runbooks |
harthelm-<version>-amd64.tar, harthelm-<version>-arm64.tar and their .sha256 | the air-gapped bundles |
harthelm-<version>-sources.txt | the index of the source bundle |
Check every download against its checksum:
v=1.6.0
curl -fsSLO "https://get.harthelm.dev/releases/$v/harthelm-restore-$v.tar.gz"
curl -fsSLO "https://get.harthelm.dev/releases/$v/harthelm-restore-$v.tar.gz.sha256"
sha256sum -c "harthelm-restore-$v.tar.gz.sha256" # macOS: shasum -a 256 -cThe hart archives share one file: sha256sum --ignore-missing -c hart_1.6.0_SHA256SUMS. A checksum from the same
host shows that a download is complete, not who made it; for the images and the chart, the
signatures do that.
Air-gapped bundles
Without a host that reaches both registry.harthelm.dev and your registry, carry a bundle over. There is one per
architecture, harthelm-<version>-amd64.tar and harthelm-<version>-arm64.tar, and it unpacks to one directory:
| Path | What |
|---|---|
images.txt | every image of the release for that platform, by the digest of its platform manifest |
oci/ | the images as an OCI image layout |
harthelm-<version>.tgz | the chart, pinned to those digests |
mirror.sh, README.txt | the script and these steps |
Use the bundle of your nodes' architecture: its chart pins that platform's manifests. A cluster with nodes of both
architectures needs the multi-arch images instead: mirror them from images-public.txt
(above), through a directory if no host sees both sides, and install the public
chart.
Check and verify on the connected side
Download the bundle and its .sha256, check it, and verify the signatures of its images
while you still reach registry.harthelm.dev:
v=1.6.0; arch=amd64
sha256sum -c "harthelm-$v-$arch.tar.sha256" # macOS: shasum -a 256 -c
tar -xf "harthelm-$v-$arch.tar"Then carry the .tar and its .sha256 over.
Load your registry
On a host that reaches your registry, check the file again, unpack it and load the images:
v=1.6.0; arch=amd64
sha256sum -c "harthelm-$v-$arch.tar.sha256"
tar -xf "harthelm-$v-$arch.tar" && cd "harthelm-$v-$arch"
./mirror.sh --from-bundle . registry.example.internal
./mirror.sh --verify registry.example.internalskopeo copies every image out of the bundle's layout with its digest. --verify only reads and compares.
Install
The chart holds values-airgap.yaml, the values an air-gapped install needs, with placeholders for your hosts. Take
it out, fill it in, and set global.imageRegistry: registry.example.internal in your values:
tar -xzf "harthelm-$v.tgz" harthelm/values-airgap.yaml && cp harthelm/values-airgap.yaml .
helm install harthelm "./harthelm-$v.tgz" -n harthelm-system -f values-airgap.yaml -f my-values.yamlThe images are then registry.example.internal/harthelm/<image>. Air-gapped installs
explains the values, the operators and the mirrors for the workspaces' tools.
The source code
Harthelm's images contain components under the GPL, LGPL and MPL. The complete source of each of them (Ubuntu, Debian and Alpine source packages, the Nix sources, npm tarballs and vendor tarballs), in the versions a release ships, is one file per release:
v=1.6.0
curl -fsSLO "https://get.harthelm.dev/sources/harthelm-$v-sources.tar"
curl -fsSLO "https://get.harthelm.dev/sources/harthelm-$v-sources.tar.sha256"
sha256sum -c "harthelm-$v-sources.tar.sha256"Its index, harthelm-<version>-sources.txt, is next to it and in the release's folder. The files under sources/
are written once and kept for every release. You do not need them to run Harthelm.
Open-source components says what the bundle holds and where the notices are.