Skip to content

Merkalis Distribution Registry

Customer-facing distribution registry for promoted, signed release artifacts:

  • Container images promoted from the internal build registry and tagged with a customer-facing semver (e.g. <customer>/kastoria-testcontainer:v0.0.1)
  • OpenTofu module packages published under modules/<env>/<component> (e.g. modules/azure/compute:v0.0.1)

Registry login server:

acrmerkalisdist0c66.azurecr.io

Every artifact is signed with Notation (Notary v2) using an HSM-backed certificate in Azure Key Vault. The trust material to verify those signatures is published in this directory:

File Purpose Download
trust-policy.json Notation trust policy to import with notation policy import Download
merkalis-dist-ca.pem Public root certificate of the AKV signing cert (trust anchor) Download

This material is versioned alongside the modules you consume: update it whenever a new module version is published.

Prerequisites

  • Docker (or another OCI client such as oras)
  • Notation CLI 1.3.x
  • OpenTofu >= 1.10 (only needed for module consumption)
  • Your registry credentials (below)

1. Get your credentials

Access is granted with a scoped token minted for your customer namespace:

  • Username: token-<customer>
  • Password: issued to you by Merkalis. It is stored in the audited Key Vault secret acr-token-<customer> and is the only credential you need.

The token only allows read access to the repositories you are entitled to (your image namespace plus the whole modules/azure/* module namespace). It cannot write or delete anything.

2. Authenticate to the registry

Log in with the scoped token. Docker writes the credential to ~/.docker/config.json, which is also what OpenTofu reads for OCI module sources:

docker login acrmerkalisdist0c66.azurecr.io
# Username: token-<customer>
# Password: <password issued by Merkalis>

If you use oras, log in the same way:

oras login acrmerkalisdist0c66.azurecr.io --username token-<customer>

For notation verify to reach the registry, Notation needs the credential too:

notation login acrmerkalisdist0c66.azurecr.io --username token-<customer>

3. Trust the signing certificate

Import the published trust anchor and trust policy (run from this directory):

notation cert add --type ca --store merkalis-dist ./merkalis-dist-ca.pem
notation policy import ./trust-policy.json

Confirm both took effect:

notation cert show --store merkalis-dist --type ca merkalis-dist-ca.pem
notation policy show

The policy is named merkalis-dist, applies to every repository on the registry, requires strict signature verification, and skips revocation because the self-signed root has no CRL/OCSP endpoints.

4. Pull a promoted image by semver tag

Promoted images are published under the customer namespace with semver tags:

docker pull acrmerkalisdist0c66.azurecr.io/<customer>/kastoria-testcontainer:v0.0.1

List the tags available to you at any time with:

az acr repository show-tags --name acrmerkalisdist0c66 --repository <customer>/kastoria-testcontainer

(requires az + reader access to the dist subscription; ask Merkalis if you need the list).

5. Verify the signature

Verify the image against the trust material before use:

notation verify acrmerkalisdist0c66.azurecr.io/<customer>/kastoria-testcontainer:v0.0.1

A successful verification prints:

Successfully verified signature for acrmerkalisdist0c66.azurecr.io/<customer>/kastoria-testcontainer@sha256:9afc667179f1f83af9ea0a29f12397ae7cbfd3c69b8d2c63f5d964cd68aa6506

Tags are mutable, so for supply-chain-rigorous verification pin the digest instead:

notation verify acrmerkalisdist0c66.azurecr.io/<customer>/kastoria-testcontainer@sha256:9afc667179f1f83af9ea0a29f12397ae7cbfd3c69b8d2c63f5d964cd68aa6506

6. Use signed modules (OpenTofu >= 1.10)

Module packages are published as OCI artifacts under modules/<env>/<component> and consumed natively by OpenTofu with an oci:// source. The ?tag= selects the release (all modules share the same version train, so the tag applies to every component):

terraform {
  required_version = ">= 1.10"
}

module "compute" {
  source = "oci://acrmerkalisdist0c66.azurecr.io/modules/azure/compute?tag=v0.0.1"
  # ... required variables for the module
}

OpenTofu reads the registry credential from ~/.docker/config.json (written by docker login in step 2), so no extra setup is needed:

tofu init

You can also verify the module package directly with Notation, exactly like an image:

notation verify acrmerkalisdist0c66.azurecr.io/modules/azure/compute:v0.0.1

7. Air-gapped / offline environments (HITRUST)

To move a promoted image into a network-isolated environment, export it to a tarball on a connected machine and docker load it inside the environment:

# On the connected machine
docker pull acrmerkalisdist0c66.azurecr.io/<customer>/kastoria-testcontainer:v0.0.1
docker save acrmerkalisdist0c66.azurecr.io/<customer>/kastoria-testcontainer:v0.0.1 \
  -o kastoria-testcontainer-v0.0.1.tar
# Transfer the tarball (plus the two trust files from this directory) over your normal
# change-control / media process, then on the offline host:
docker load -i kastoria-testcontainer-v0.0.1.tar

Verify the image on the connected machine before transfer (the signature lives in the registry, so notation verify needs registry access; the tarball itself carries no signature):

notation verify acrmerkalisdist0c66.azurecr.io/<customer>/kastoria-testcontainer@sha256:9afc667179f1f83af9ea0a29f12397ae7cbfd3c69b8d2c63f5d964cd68aa6506

Record the verified digest alongside the tarball so the offline host can confirm the loaded image matches what was verified (docker inspect --format '{{index .RepoDigests 0}} <image:tag>').

For module packages in an offline environment, pull the .zip layer out of the OCI artifact with oras and unzip it into your module cache, or run tofu init on a connected host and copy the .terraform/modules directory across.

Trust anchor fingerprints

The published certificate is self-signed (subject CN=merkalis.io, valid through 2036). Recorded fingerprints, so the file you import can be cross-checked against an out-of-band channel:

SHA256 (S256): 6895c859070cc242f2799aa5bb456b625da3dd06e6594275d021b453f172c062

Token lifecycle

  • Rotation: the token password expires ~1 year after issuance. Merkalis rotates it on the annual cycle; a new password is issued through the same channel.
  • Revocation: removing a customer's entitlement immediately breaks subsequent pulls; the token is disabled at the registry.
  • Audit: every pull is recorded in the registry's activity log; credentials are retrieved from the audited Key Vault only.