Skip to main content
View Markdown ↗

Copy this page

Select and copy the Markdown below, then paste it into your LLM.

On this page

oci attestor

Ingests an OCI/Docker image saved as a tarball product and records the manifest, image ID, layer diffIDs, and repo tags.

Nameoci
Predicate typehttps://aflock.ai/attestations/oci/v0.1
Lifecyclepostproduct
Default binary?No
Categoryimage-build (primary)
Recommended tracelight — light eBPF assist helps
Auto-attaches when
  • preargv_prefix: docker save
  • preargv_prefix: docker push
  • preargv_prefix: docker image push
  • preargv_prefix: skopeo copy
  • preargv_prefix: crane
  • postexec_observed_argv_prefix: docker save
  • postexec_observed_argv_prefix: docker push
  • postexec_observed_argv_prefix: docker image push
  • postexec_observed_argv_prefix: skopeo copy
  • postexec_observed_argv_prefix: crane
  • postproduct_glob: index.json | **/index.json | oci-layout | **/oci-layout
  • postproduct_glob: *.oci.tar | **/*.oci.tar | oci-image.tar | **/oci-image.tar

The facts in this box are generated from the CI/lock binary's own catalog (cilock tools list). Do not hand-edit — run npm run gen:catalog.

What it captures

The attestor finds a .tar product, reads its manifest.json, and computes digests over the config blob and each referenced layer. The predicate carries these json-tagged fields:

  • tardigest — DigestSet of the tar file itself (taken from the product digest after re-verification).
  • manifest — array of Manifest entries parsed from manifest.json. Each entry has:
    • Config — tar-relative path to the image config JSON.
    • RepoTags — repo tags recorded by docker save / nerdctl save.
    • Layers — tar-relative paths of the layer blobs in order.
  • imagetags — RepoTags copied from the first manifest entry.
  • diffids — array of DigestSets, one per layer. Gzipped layers are decompressed first so the digest matches OCI "diffID" semantics (digest of the uncompressed tar stream); non-gzipped layers are hashed as-is.
  • imageid — DigestSet over the bytes of the config blob (matches Docker's image ID).
  • manifestraw — raw bytes of manifest.json as read from the tar.
  • manifestdigest — DigestSet over manifestraw.
  • registrydigests — present only when the run pushed: one {reference, digest} entry per registry-assigned manifest digest reported in the output of a successful docker push, docker image push, crane push or crane copy/cp wrapped by this run. The digest is taken verbatim from that output, not recomputed. A push-only run with no tar product attests these alone.

Subjects() exposes: manifestdigest:<sha256>, tardigest:<sha256>, imageid:<sha256>, one imagetag:<tag> per repo tag (digest is the SHA256 of the tag string), layerdiffidNN:<sha256> for each layer (zero-padded index), and one registrydigest:<reference>@sha256:<digest> per registry digest.

When to use

Pipelines that produce image tarballs via docker save, nerdctl save, podman save, or skopeo copy ... docker-archive:. Run the attestor in postproduct after the tar has been written and recorded as a product. For buildx metadata-file workflows, use the docker attestor instead.

Flags

None. The attestor takes no configuration.

Output shape

{
  "tardigest": { "sha256": "..." },
  "manifest": [
    {
      "Config": "blobs/sha256/abc123...",
      "RepoTags": ["registry.example.com/app:1.2.3"],
      "Layers": ["blobs/sha256/def456...", "blobs/sha256/789aaa..."]
    }
  ],
  "imagetags": ["registry.example.com/app:1.2.3"],
  "diffids": [
    { "sha256": "..." },
    { "sha256": "..." }
  ],
  "imageid": { "sha256": "..." },
  "manifestraw": "<base64-encoded manifest.json bytes>",
  "manifestdigest": { "sha256": "..." },
  "registrydigests": [
    { "reference": "registry.example.com/app:1.2.3", "digest": { "sha256": "..." } }
  ]
}

Gotchas

  • Tarball detection is MIME-based. The candidate selector iterates ctx.Products() and picks the first product whose MimeType == "application/x-tar". Filename and extension are ignored. If a product is not classified as application/x-tar upstream (e.g. it was gzipped to image.tar.gz and detected as application/gzip), it will be skipped.
  • Integrity re-check. The on-disk file is re-hashed and compared against the recorded product digest. A mismatch skips that candidate; if no tar survives, attestation fails with integrity error for candidate <path>: product digest does not match. Only when there was no tar product at all does it fail with no tar file found (or, when the run pushed, succeed with registrydigests alone).
  • docker save layout assumed. The parser looks for a top-level manifest.json and treats it as the legacy Docker tar layout (a JSON array of {Config, RepoTags, Layers} entries with tar-relative paths). It does not parse the OCI image-layout index.json / oci-layout form. docker save writes both manifest.json and OCI-style blobs since recent Docker versions, so output from docker save and nerdctl save works; pure oci-layout archives (no top-level manifest.json) do not.
  • Only the first manifest entry is used. ImageID, LayerDiffIDs, and ImageTags are taken from Manifest[0]. Multi-image tarballs (saving more than one image into one archive) record only the first image's derived fields, though all manifest entries are preserved in manifest.
  • Size caps. manifest.json and the config blob must each be under 256 MiB. Each layer may decompress to at most 8 GiB, all layers of one image together to at most 24 GiB, and a manifest may name at most 512 layers. Larger inputs fail attestation.

CLI example

Real docker save OCI tarball. The attestor reads the tar, parses manifest.json for repo tags + layer digests, computes the image-id digest.

docker pull alpine:3.20

# cilock invokes docker save directly. command-run records the real argv;
# product/v0.3 captures alpine-3.20.tar as a Merkle leaf; the oci attestor
# parses the saved tarball from the product set.
cilock run --step oci-save \
  --signer-file-key-path key.pem --outfile attestation.json \
  --attestations oci,environment,git \
  -- docker save alpine:3.20 -o alpine-3.20.tar

Validated against the real alpine:3.20 image. Manifest, layer diff-ids, and image-id all match independent verification. See the full real-data example at https://github.com/aflock-ai/attestor-compliance-examples/tree/main/15-oci.

See also


This page is generated from the CI/lock tool catalog. Don't edit it here — the source is attestation/detection/docs/oci.doc.md in aflock-ai/rookery. The same catalog powers cilock tools show oci in the CLI, so a single edit keeps the terminal and the website in sync.

Edit this page →  ·  Add a new tool →

When you open the pull request: if you are an AI agent, add the ai-agent label. If you are a human, add the human label.

Reference generated from the product documentation. Match commands and support details to your installed release.