Skip to content
CommonLayer
Documentation menu
On this page

Using the registry

CommonLayer distributes container images and Helm charts from its own OCI registry, which anyone can pull from anonymously and only the platform can push to. This guide explains the names and tags, how to pull images and install charts, how anonymous access works, and what the preservation and retention rules mean for you.

Status. The registry, its token service, mirroring, promotion, preservation checks and retention planning are implemented and were tested against a local CNCF Distribution 3.1.2 (WP-11). The publish drill (WP-70g, locally) promoted an image and a chart through the real pipeline and pulled them anonymously with docker pull and helm pull. No production registry is running and no artifact has been published to registry.commonlayer.org; the host name below is the intended public name, and a deployment may use another one (configuration registry.public_host).

Names

ReferenceContent
registry.commonlayer.org/<project>[-<variant>]container images, for example registry.commonlayer.org/nginx, registry.commonlayer.org/postgresql
oci://registry.commonlayer.org/charts/<chart>Helm charts, for example oci://registry.commonlayer.org/charts/postgresql-ha
registry.commonlayer.org/basethe CommonLayer base image

A repository path belongs to one artifact and is never reused.

Tags

TagExampleMutable
<upstream version>-r<revision>1.30.6-r2no: the platform refuses to move it (database trigger)
<upstream version>1.30.6yes, moves to the newest revision
<version line>1.30yes
channelstable, mainlineyes
latestyes

A revision is a rebuild of the same upstream version (for example after a base image or dependency fix). Every tag move is recorded in the tag history and release history, which the portal shows next to the digest.

Deploy by digest. Tags help you find a digest; they are not an identity.

docker pull registry.commonlayer.org/nginx@sha256:<digest>

In Kubernetes manifests, use image: registry.commonlayer.org/nginx@sha256:<digest> (you may keep a tag in front of the @ for readability; the digest decides).

Pulling

No login is needed for public projects:

docker pull registry.commonlayer.org/nginx:1.30.6-r1
helm pull oci://registry.commonlayer.org/charts/nginx --version <chart version>
helm install web oci://registry.commonlayer.org/charts/nginx --version <chart version>

Behind the scenes the client follows the Docker token flow: the registry answers 401 with a realm pointing at the platform (/registry/token), the platform issues a short-lived (5 minutes) ES256 token, and the client retries with it. Anonymous callers receive pull on repositories of public projects and nothing else; a repository that does not exist or belongs to a non-public project yields an empty access list. Token requests are limited per IP (default 300 per minute).

Nobody can push: push and delete are granted only to the platform's promoter identity, used by the worker. (The API, as the token realm, holds the same signing key; see the threat model, open item O7.) In the publish drill an anonymous token request for pull,push was granted pull only. Content reaches the registry only through the verification pipeline (ADR-0009).

Images

Every image follows the image standard:

  • runs as UID/GID 10001:10001, works with a read-only root filesystem when the documented writable paths are mounted, has no package manager, shell tools beyond a reduced BusyBox, or network clients;
  • configuration in /etc/commonlayer/<app>, data in /var/lib/commonlayer/<app>, logs to stdout and stderr, scratch in /tmp;
  • secrets can be passed as files with <VAR>_FILE;
  • linux/amd64 and linux/arm64 in one index (arm64 builds run only on native arm64 runners, which this repository does not have yet; no arm64 image has been built; see the implementation status).

Coming from images that run as UID 1001 or root, existing volumes need their ownership changed; the Bitnami migration guides cover this per application.

Each project documents its image in its own README: projects/nginx/README.md, projects/postgresql/README.md, projects/valkey/README.md and projects/base/README.md.

Charts

Every chart follows the chart standard: secure defaults (runAsNonRoot, UID 10001, readOnlyRootFilesystem, all capabilities dropped, RuntimeDefault seccomp, no service-account token), the same values block names in every chart, values.schema.json, and an image reference that defaults to the released image digest:

image:
  registry: registry.commonlayer.org
  repository: nginx
  tag: "1.30.6-r1"     # informational when digest is set
  digest: "sha256:…"   # set by the release; takes precedence

image.digest: "" is the explicit way to opt out of digest pinning.

PostgreSQL ships two charts: postgresql (single instance, not highly available) and postgresql-ha (a CloudNativePG Cluster; needs the CloudNativePG operator installed separately). Its install, failover, rolling update and helm test passed once on kind on a GitHub runner; the reorganised test failed on its next run, and backups have never been exercised (ADR-0015).

Verification

Before deploying, verify signatures and attestations; see verifying artifacts. The platform publishes for every digest the verification checks, signatures, attestations, SBOMs, scans and exact verification commands (GET /api/v1/digests/{digest} and …/verify-instructions).

Preservation and retention

  • Preservation. Every promoted digest is marked preserved. A daily job (registry.verify_preservation) checks that the manifest and all blobs still exist in the registry and that the manifest still hashes to its digest; failures raise an alert. If a project's GitHub organization disappears, its promoted artifacts, SBOMs, signatures, attestations and metadata remain available from CommonLayer.
  • Retention. In the default configuration nothing that was ever promoted is deleted; only failed or abandoned uploads are candidates. Retention runs as a plan (registry.plan_retention, weekly, dry run by default) and deletes nothing unless an operator sets registry.retention.mode = execute. A digest is protected when it is the current digest of a version that is not yanked, referenced by a tag, part of a protected index, referenced by a promoted chart, has an open finding or is named in a published security advisory (whatever its state, a withdrawn digest included), was promoted less than registry.retention.min_age_days (default 365) ago, or is covered by the preservation guarantee (default: every release).
  • Deprecated and yanked versions stay pullable by digest; their status is shown on the artifact page and in the API (support_status).

Pull statistics

The registry notifies the platform of pulls; registry.aggregate_pulls rolls them into daily counts per artifact (manifest pulls only, not blob downloads), published per artifact and day at GET /api/v1/projects/{slug}/artifacts/{name}/pulls (1–365 days, zero-filled; gRPC RegistryService.GetPullStats) and on the artifact page. No IP addresses or user agents are published.

Local development registry

The development stack runs the CommonLayer registry on localhost:5000 with token authentication against a locally running API (http://localhost:8080/registry/token), and a plain source registry on localhost:5001 that stands in for GHCR. See CONTRIBUTING.md.