Skip to main content
RunBook Academy

Git, CI/CD & GitOpsXLV · Artifact ImmutabilityContent Addressing

Digests and content-addressing — sha256:abc..., the artifact’s true name

Advanced⏱ ~24 mingit

What you'll learn

  • Describe the anatomy of an OCI digest: algorithm, separator, and hex-encoded hash
  • Explain how the registry stores content by digest rather than by tag
  • Read a manifest digest with crane to obtain the pin a deployment references
  • Distinguish the manifest digest from the blob digest and know which one to pin

Prerequisites

Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x

Not yet marked complete on this device.

A digest is the artifact’s true name. The format sha256:abc... looks like an opaque string, but every character is determined by the contents of the artifact. The registry does not store a digest and an artifact as separate things; the registry stores an artifact whose retrieval is keyed by its digest. The distinction is the foundation of every immutability property this part teaches: a tag is a pointer you can move, a digest is an address that does not change.

The anatomy of a digest

A digest has three parts. The algorithm name (sha256, in practice always SHA-256 in OCI registries). The separator (a single colon). The hex-encoded hash (64 lowercase hex characters for SHA-256, since SHA-256 produces 256 bits = 32 bytes = 64 hex chars). The full string is unambiguous, self-describing, and globally unique for any given input.

flowchart LR
    A["Manifest JSON"] --> B["SHA-256 hash"]
    B --> C["sha256:abc..."]
    D["Algorithm"] --> C
    E["Separator"] --> C
    F["Hex hash"] --> C
    C --> G["Manifest digest"]
    C --> H["Layer digest"]
    C --> I["Config digest"]

The registry computes the digest over a canonical encoding of the manifest, not over the raw bytes the user uploaded. The canonical encoding is the one defined by the OCI image-spec; it removes whitespace ambiguity so that two clients uploading the same logical manifest produce the same digest.

How the registry stores content

The registry is a content-addressable store: a key-value database where the key is the digest and the value is the bytes. The store does not know about tags; the tag layer is a separate, mutable index that maps tag strings to digests. When the registry receives a docker push registry.example.com/app:$TAG, it does two things: it writes the blob to the content-addressable store under the manifest’s digest, and it writes a tag-to-digest mapping under the tag. The two operations are independent. The blob write is immutable; the tag write is mutable.

flowchart TB
    subgraph ST["Content-addressable store"]
        S1["sha256:abc — manifest blob"]
        S2["sha256:def — layer blob"]
        S3["sha256:ghi — config blob"]
    end
    subgraph TA["Tag index (mutable)"]
        T1[":v3.2.7 — sha256:abc"]
        T2[":latest — sha256:abc"]
    end
    TA --> ST

This two-layer architecture is what makes the digest a stable identity: the tag index can be rewritten arbitrarily without affecting the content-addressable store. A digest that was written today will resolve to the same bytes tomorrow, regardless of what the tag index points at.

Reading the manifest’s digest

A pipeline that pushes an image by tag still needs the digest for production pinning. The crane manifest digest command reads the manifest digest from the registry:

DIGEST=$(crane manifest digest registry.example.com/app:$COMMIT_SHA)
echo "$DIGEST"
sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08

The command returns the digest of the manifest, which is what a Kubernetes deployment references by @sha256:.... The pipeline records the digest alongside the tag, and the deployment manifest pins by digest. A re-tag later in the day does not affect the deployment, because the deployment references the digest, not the tag.

Pulling by digest

A deployment that pins by digest retrieves the bytes the digest identifies, regardless of what the tag index now says:

docker pull registry.example.com/app@sha256:$DIGEST

The @sha256:... syntax is the digest reference. The pull resolves the digest through the content-addressable store, bypassing the tag index entirely. This is the operational expression of the immutable identity principle from XLV-01: the consumer names the artifact by its bytes, not by a tag someone may have reassigned.

Production discipline

  1. Always record the manifest digest at push time. The pipeline that pushes by tag must also resolve and record the digest before the deploy step.
  2. Pin deployments by @sha256:..., never by tag. A deployment manifest references the digest the pipeline recorded.
  3. Verify the digest matches before deploying. Pull by digest, inspect the manifest, confirm the layers are what you expect.

Cross-course references

  • Git, CI/CD & GitOps — Part XLV-01 (Immutable Identity) establishes the principle this lesson applies.
  • Terraform for Production Sysadmins — Part IX (State) uses content addressing for Terraform plan storage.
  • Linux for Production Sysadmins — Part XXXIV (ConfigMgmt) uses content addressing for package indexes.

Quiz

Knowledge check · 4 questions

  1. Q1. Which command reads the manifest digest of a tagged image so the pipeline can record the pin?

  2. Q2. The registry stores the image's bytes under the manifest's content-addressed digest, separately from any tag-to-digest mapping.

  3. Q3. Name the hash algorithm and the separator that together form an OCI image digest.

  4. Q4. Identify the digest-pinning gap in this pipeline and the rule that closes it.

    A team pushes a build as `app:$COMMIT_SHA` and writes `image: registry.example.com/app:$COMMIT_SHA` into the deployment manifest. Later, the build pipeline is rerun for a hotfix and overwrites the same tag with a different image. The production deployment silently picks up the new image because the manifest references the tag, not the digest.

Passing score: 75%. Answers are checked in this browser.