All posts

8 min read

How do you anchor CI/CD build evidence to a public timestamp?

A CI/CD pipeline can hash its build artifacts, SBOMs, logs, and release manifests, batch them into one Merkle root, and publish a single Label 309 proof — an independent public time anchor for later audits.

Yes — a CI/CD pipeline can publish proof of what it built, when. The pattern is to hash the outputs that matter (artifacts, SBOMs, logs, release manifests), fold those hashes into one Merkle root, and publish a single Label 309 record for the build, release, or time window. Later, anyone can prove that a specific artifact or manifest was part of that committed batch, and that the batch existed on or before a public block time.

This does not replace SLSA, Sigstore, GitHub Artifact Attestations, or in-toto. It complements them with one thing none of them provides directly: an independent, public, issuer-agnostic time anchor that no vendor controls.

What should a CI/CD pipeline actually prove?

Start with the evidence you might need to produce a year from now.

A CI/CD proof can commit to:

  • release artifacts;
  • container image digests;
  • SBOM (software bill of materials) files;
  • SLSA provenance attestations;
  • in-toto link metadata;
  • build logs;
  • test reports;
  • deployment manifests;
  • changelog snapshots;
  • source commit references;
  • dependency lockfiles;
  • signed checksums;
  • release notes.

Do not hash everything just because it exists. Hash the artifacts that matter for security, audit, incident response, customer trust, or release integrity.

A good proof answers one concrete future question: "Was this exact artifact or manifest part of the release evidence we committed at that time?"

How does the Merkle batching pattern work?

For a single artifact, one hash proof is enough.

For a release you usually have many artifacts, and publishing each one as its own transaction is wasteful. A Merkle root solves that. You hash every evidence item into a leaf, fold the ordered leaves into one 32-byte root, and publish only the root — one on-chain record covering the entire release.

The pipeline:

  1. Builds the artifacts.
  2. Generates or collects SBOMs, attestations, logs, and manifests.
  3. Hashes each evidence item into a leaf.
  4. Assembles an ordered leaf list.
  5. Builds the Merkle tree and computes the root.
  6. Publishes one Label 309 record carrying the root.
  7. Stores the leaf list and inclusion proofs alongside the release evidence.

Later, a verifier takes one file or digest, checks its inclusion proof, and confirms the item belongs to the root in the Label 309 record. The proof of inclusion grows only with the logarithm of the batch size, so a root over thousands of leaves still verifies in milliseconds — fully offline, with no gateway and no network. For the full mechanics, see one record for thousands of files.

Why not just rely on existing attestations?

Use existing attestations — then anchor them.

SLSA provenance describes where, when, and how a software artifact was produced. GitHub Artifact Attestations establish build provenance for artifacts such as binaries and container images. Sigstore records signed supply-chain metadata in a public, append-only transparency log called Rekor. in-toto is a framework for verifying that each step in a supply chain was performed as planned, by the right party, on the right inputs.

Each of those solves a real problem. Label 309 solves a different one: publishing a Cardano-anchored, independently verifiable proof that a specific evidence set existed by a specific public time. A strong pipeline does not choose "attestations or proof of existence." It uses both:

  • SLSA or GitHub attestations for build provenance;
  • Sigstore for signing and transparency-log workflows;
  • in-toto for supply-chain step verification;
  • Label 309 for a public time anchor over the whole evidence set.

What does Label 309 add on top?

It adds a durable public commitment on Cardano.

That commitment can cover a single release manifest or a Merkle root over many evidence items. It can be signed by a project or company identity. And it can be verified using only the transaction metadata and a public Cardano explorer — no account, no login, and no dependence on the original CI provider's dashboard remaining online.

That independence matters most when a team needs to prove the evidence existed before some later event:

  • a vulnerability disclosure;
  • a security incident;
  • a customer security review;
  • a procurement audit;
  • a compliance deadline;
  • a release dispute;
  • a supply-chain investigation.

The proof gives the timeline an anchor nobody involved can quietly move.

What would an auditor verify?

An auditor should be able to follow the chain from a single artifact back to the on-chain record:

  1. Take the artifact or SBOM under review.
  2. Compute its hash.
  3. Check the inclusion proof against the release Merkle root.
  4. Confirm the root appears in a Label 309 record.
  5. Look up the Cardano transaction and read its block time.
  6. Verify any record signature.
  7. Check the surrounding provenance or attestation under its own rules.

This cleanly separates two questions. The Label 309 proof answers: was this evidence committed by this public time? The SLSA, Sigstore, GitHub, or in-toto layer answers: what does this evidence say about how the artifact was built, signed, or distributed? Neither tries to do the other's job.

What belongs in the release manifest?

A release manifest should be boring and complete. It might include:

  • release name and version;
  • repository URL;
  • source commit;
  • build workflow identifier;
  • build invocation ID;
  • artifact names and digests;
  • container image digests;
  • SBOM file digests;
  • attestation file digests;
  • test-report digests;
  • build-log digest;
  • the timestamp reported by the CI system;
  • the Label 309 transaction reference, added after publish.

The manifest itself can be hashed and included as a leaf. It also doubles as the human-readable index that explains every other leaf in the batch. Keep the format stable: a proof is easier to verify when the evidence structure is predictable from one release to the next.

Should the pipeline sign the record?

Usually, yes.

A Merkle root proves that a list was committed. A signature additionally shows that a project, company, release system, or approved identity vouched for that commitment. In Label 309 this is an optional record-level signature — authorship is never required for the existence claim to verify, but it is available when you want accountability.

Manage the signing key deliberately. Do not scatter long-lived identity seeds across arbitrary CI runners. Depending on your threat model, use a dedicated release identity, a controlled signing service, a hardware-backed workflow, or a self-hosted runner with tight access controls. The open-source cardanowall CLI is built for exactly this — gateway-agnostic and raw-seed-first, so it slots into automation without a website in the loop. See using the CLI in automation for the end-to-end flow.

A signature adds accountability. It does not, by itself, make a weak build pipeline secure.

Where should the leaf list live?

Store it with the release evidence.

The leaf list and inclusion proofs are what let you prove individual items later. If you publish only the root and then lose the leaf list, you can still prove that some list existed — but you may no longer be able to prove that a specific artifact was in it.

Good storage options depend on the workflow:

  • attach it to the release archive;
  • store it in an artifact repository;
  • keep it in an internal evidence bucket;
  • seal it as encrypted content for confidential evidence;
  • publish it through content-addressed storage when it is safe to make public.

The root is the anchor. The leaf list is the map.

How often should a pipeline publish?

Match the proof cadence to the release cadence. Common choices:

  • one proof per release;
  • one proof per build;
  • one proof per deployment;
  • one proof per day for continuous logs;
  • one proof per security-significant event.

Publishing too rarely weakens the timeline; publishing too often adds cost and operational noise. Merkle batching lets you pick a cadence that matches the business question. If a customer asks "what exactly shipped in version 2.3.1?", one proof per release is plenty. If a regulator or auditor asks whether monitoring was continuous, daily or hourly commitments serve better.

Publishing costs money, because the gateway pays real Cardano transaction fees plus storage — so cadence is a genuine cost-versus-evidence tradeoff, not a free dial.

What does a CI/CD proof not prove?

It does not prove the build was secure.

A proof of existence can show that an artifact, SBOM, log, or manifest existed by a public time; that the item was included in a committed batch; and that a key signed the record. That is the whole of what it certifies.

It does not prove the source code was safe. It does not prove the runner was not compromised. It does not prove the dependencies were free of vulnerabilities. It does not prove the SBOM is complete. And it does not prove an attestation is trustworthy unless that attestation's own verification rules pass. This is exactly why Label 309 sits beside supply-chain security tools rather than replacing them — see what a proof does not prove for the general limits.

What is a good first implementation?

Start with a release proof. For each release:

  1. Generate your normal artifacts.
  2. Generate or collect SBOMs and attestations.
  3. Create a release manifest.
  4. Hash every evidence file into a leaf.
  5. Build the Merkle root.
  6. Publish a signed Label 309 record.
  7. Save the transaction reference in the release notes.
  8. Store the manifest, leaf list, and inclusion proofs with the release.

That gives you a practical evidence trail without re-architecting your build system on day one. From there, expand to daily logs, deployment manifests, or higher-volume automation.

The short version

A CI/CD proof is a timestamped commitment to release evidence.

Hash the artifacts, SBOMs, logs, attestations, and manifests that matter. Batch them into one Merkle root. Publish that root in a Label 309 record. Sign the record if you want a project or company identity to vouch for it.

Keep SLSA, Sigstore, GitHub Artifact Attestations, and in-toto for provenance and supply-chain metadata. Add Label 309 as the independent public time anchor that ties the whole evidence set to a moment no vendor can move.

Further reading

ci-cdmerklesoftware-supply-chain