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.