Documentation
¶
Overview ¶
Package receipt builds and verifies content-addressed, git-committable reproducibility receipts for a job run.
A receipt is a Merkle-style aggregation over a run's persisted identity:
receiptDigest = sha256(
sorted per-task identity hashes +
resolved image digests +
manifest content hash +
git commit )
It is the REPRODUCE half of the data-plane-memory substrate (see docs/design-data-plane-memory.md): a small, deterministic, commit-into-git artifact that attests *what ran*. `caesium verify` re-derives the receipt from the run's persisted state and flags DRIFT — e.g. a `:latest` tag that moved to new content (digest mismatch) or a changed manifest. It does NOT resurrect deleted source data; it re-derives the signature and proves what the system recorded as having run.
Correctness rule (from the design) ¶
A receipt over an UNPINNED, mutable tag is unsound: nothing in the recorded identity is tamper-evident against a tag that moves underneath. So when a task's ResolvedImageDigest is empty — digest pinning was off, or a Podman/k8s path fell back to the literal tag — that task is marked DigestPinned=false / Degraded=true, and the receipt as a whole is marked Degraded. The receipt digest is still computed (it folds in the literal tag, the only identity available), but verify reports honestly that the run is NOT fully reproducible. We never silently attest a mutable tag as reproducible.
Index ¶
Constants ¶
const Version = 1
Version is the schema version of the receipt format. Bump it whenever the canonical serialization or the digest derivation changes, so a verifier can refuse to compare receipts produced by incompatible builders rather than reporting spurious drift.
Variables ¶
var ErrRunNotFound = errors.New("receipt: run not found")
ErrRunNotFound is returned by Build when the requested run does not exist.
Functions ¶
This section is empty.
Types ¶
type Drift ¶
type Drift struct {
Kind DriftKind `json:"kind"`
Task string `json:"task,omitempty"`
Expected string `json:"expected,omitempty"`
Actual string `json:"actual,omitempty"`
Detail string `json:"detail"`
}
Drift is one detected divergence between a committed receipt and the re-derived one. Task is empty for run-level drifts (manifest, git commit, version, receipt digest).
type DriftKind ¶
type DriftKind string
DriftKind classifies a single way a re-derived receipt diverges from a committed one. Each value names a concrete, operator-actionable cause.
const ( // DriftReceiptDigest is the top-level signal: the re-derived receipt digest // does not match the committed one. It is always accompanied by one or more // of the specific drifts below explaining why. DriftReceiptDigest DriftKind = "receipt_digest_mismatch" // DriftImageDigest means a task's resolved image digest changed — the tag // was mutated to point at new content (e.g. a re-pushed :latest). This is // the headline reproducibility failure the receipt exists to catch. DriftImageDigest DriftKind = "image_digest_mismatch" // DriftIdentityHash means a task's identity (cache-key) hash changed — // command, env, predecessor outputs, params, or the image digest differ. DriftIdentityHash DriftKind = "identity_hash_mismatch" // DriftManifest means the DAG manifest content hash changed — the pipeline // topology applied for the run differs from what the committed receipt // recorded. DriftManifest DriftKind = "manifest_changed" // DriftGitCommit means the git provenance commit changed. DriftGitCommit DriftKind = "git_commit_changed" // DriftTaskMissing means a task present in the committed receipt is absent // from the re-derived one. DriftTaskMissing DriftKind = "task_missing" // DriftTaskAdded means a task present in the re-derived receipt is absent // from the committed one. DriftTaskAdded DriftKind = "task_added" // DriftVersion means the two receipts were produced by different schema // versions and are not directly comparable. DriftVersion DriftKind = "receipt_version_mismatch" )
type Receipt ¶
type Receipt struct {
// ReceiptVersion is Version at build time.
ReceiptVersion int `json:"receipt_version"`
// RunID, JobID and JobAlias identify the run this receipt attests. They are
// metadata for humans and lookups; they are NOT folded into ReceiptDigest
// (two runs of byte-identical inputs produce the same digest, which is the
// point — the digest addresses *what ran*, not *which run instance*).
RunID uuid.UUID `json:"run_id"`
JobID uuid.UUID `json:"job_id"`
JobAlias string `json:"job_alias,omitempty"`
// GitCommit is the provenance commit SHA the manifest was applied from
// (empty for non-GitOps applies). Folded into ReceiptDigest.
GitCommit string `json:"git_commit,omitempty"`
// ManifestContentHash is the content hash of the DAG topology the run
// executed (from the matching dag_snapshot). Folded into ReceiptDigest so a
// changed manifest yields a changed receipt.
ManifestContentHash string `json:"manifest_content_hash,omitempty"`
// Tasks are the per-task entries, sorted by TaskName. Folded into
// ReceiptDigest in that order.
Tasks []TaskEntry `json:"tasks"`
// Degraded is true iff any task is Degraded — i.e. the run is NOT fully
// reproducible (at least one mutable, unpinned tag, or a task with no
// identity hash). When true, callers must not present this receipt as a
// reproducibility guarantee.
Degraded bool `json:"degraded"`
// DegradedTasks lists the names of every degraded task, for a concise
// honest summary.
DegradedTasks []string `json:"degraded_tasks,omitempty"`
// ReceiptDigest is the sha256 hex Merkle aggregate over the canonical
// per-task lines + manifest content hash + git commit. It is the receipt's
// content address and the value `verify` re-derives.
ReceiptDigest string `json:"receipt_digest"`
}
Receipt is the full, content-addressed reproducibility receipt for one run. It is JSON-serializable and intended to be committed to git alongside the pipeline that produced it. ReceiptDigest is the single value `caesium verify` re-derives and compares.
func Build ¶
Build re-derives the reproducibility receipt for a run from its persisted state. It reads only what the system recorded — TaskRun identity hashes and resolved image digests, the job's git provenance, and the matching DAG snapshot's manifest content hash — and never re-executes anything. This is the single derivation used both when first emitting a receipt and (via the same code path) when `caesium verify` re-derives one to compare.
The returned receipt is finalized: tasks sorted, degraded summary populated, and ReceiptDigest computed. A run with any unpinned (mutable-tag) task is marked Degraded — its digest is still derived from the literal tag, the only identity available, but it must not be presented as a reproducibility guarantee.
type TaskEntry ¶
type TaskEntry struct {
// TaskName is the stable, human-meaningful name of the task within the job.
// It is the sort key for deterministic aggregation.
TaskName string `json:"task_name"`
// Partition is the fan-out instance key this entry attests, empty for an
// unfanned task. A fanned step executes N instances with N DISTINCT identity
// hashes; collapsing them to one entry would attest a single arbitrary
// partition and silently drop the other N-1 from the receipt. So a fanned
// step contributes one entry per partition, discriminated by this field.
//
// It is omit-when-empty in both the JSON and the canonical digest line, so
// receipts for unfanned runs keep byte-identical digests and need no Version
// bump.
Partition string `json:"partition,omitempty"`
// IdentityHash is the persisted TaskRun.Hash — the content-addressed cache
// key computed by internal/cache.HashInput.Compute. Empty when the task
// never had a hash computed (caching disabled for it); such a task cannot
// be attested and forces Degraded.
IdentityHash string `json:"identity_hash"`
// Image is the literal image reference (tag) the task ran with, recorded
// for human readability and as the fallback identity when no digest was
// resolved.
Image string `json:"image"`
// ResolvedImageDigest is the content digest (sha256:...) the tag resolved
// to when digest pinning was on. Empty when pinning was off or a
// Podman/k8s path fell back to the tag.
ResolvedImageDigest string `json:"resolved_image_digest,omitempty"`
// DigestPinned is true iff ResolvedImageDigest is non-empty — i.e. the
// image identity is tamper-evident for this task.
DigestPinned bool `json:"digest_pinned"`
// Degraded is true when this task cannot be soundly attested as
// reproducible: either its image was not digest-pinned (mutable tag) or it
// has no identity hash at all. Such a task is honestly surfaced rather than
// silently attested.
Degraded bool `json:"degraded"`
// DegradedReason is a short human-readable explanation set when Degraded is
// true (e.g. "image not digest-pinned: mutable tag 'myimage:latest'").
DegradedReason string `json:"degraded_reason,omitempty"`
}
TaskEntry is one task's contribution to the receipt: its identity hash (the cache key that decided whether it ran or was served from cache) plus the image identity that hash covers. Entries are sorted by TaskName before the receipt digest folds them in, so the digest is independent of run-order.
type VerifyResult ¶
type VerifyResult struct {
// RunID echoes the run that was re-derived.
RunID uuid.UUID `json:"run_id"`
// Match is true iff the re-derived receipt digest equals the committed one
// AND the run is not degraded. A degraded run never reports a clean
// reproducibility match even when the digests are equal, because an
// unpinned tag could have moved without changing the (tag-only) digest —
// the very condition the receipt cannot attest against.
Match bool `json:"match"`
// Degraded is true iff the re-derived receipt is degraded (any task ran on
// an unpinned, mutable tag, or had no identity hash). When true, the
// receipt cannot soundly attest reproducibility regardless of digest
// equality; Drifts will include the degraded tasks via the rederived
// receipt's DegradedTasks.
Degraded bool `json:"degraded"`
// DegradedTasks lists the tasks that make the run unverifiable.
DegradedTasks []string `json:"degraded_tasks,omitempty"`
// ExpectedDigest is the committed receipt's digest; ActualDigest is the
// freshly re-derived one.
ExpectedDigest string `json:"expected_digest"`
ActualDigest string `json:"actual_digest"`
// Drifts enumerates every divergence found, empty when Match is true.
Drifts []Drift `json:"drifts,omitempty"`
// Rederived is the receipt Build produced from current state, for callers
// that want to inspect or re-commit it.
Rederived *Receipt `json:"rederived"`
}
VerifyResult is the outcome of comparing a committed receipt against the run's current persisted state.
func Verify ¶
Verify re-derives the receipt for the run named by committed.RunID from current persisted state and compares it against committed, reporting every drift. It is the engine behind `caesium verify`: it proves what ran by re-deriving the signature, and it surfaces — never hides — the case where the run cannot be soundly attested because a task ran on an unpinned tag.