Git, CI/CD & GitOpsXLV · Artifact ImmutabilityContent Addressing
Digests and content-addressing — sha256:abc..., the artifact’s true name
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
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
- 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.
- Pin deployments by
@sha256:..., never by tag. A deployment manifest references the digest the pipeline recorded. - 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
Q1. Which command reads the manifest digest of a tagged image so the pipeline can record the pin?
Q2. The registry stores the image's bytes under the manifest's content-addressed digest, separately from any tag-to-digest mapping.
Q3. Name the hash algorithm and the separator that together form an OCI image digest.
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.