A green pipeline proves something about bytes that existed on a runner for a few minutes. To deploy on the strength of it later, from a different machine, you need six things written down: a content-address over the exact payload, the source revision it was built from, a fingerprint of the target environment, the gate results as measured against that payload, the identity of the environment-side validation, and an expiry. Each field exists because a specific thing can otherwise change between the proof and the deploy without anyone noticing.
The gap between proving and shipping
Consider the ordinary shape of a gated release. On Monday, CI runs the suite against a branch and goes green. On Tuesday, a human approves. On Wednesday, a different job on a different runner performs the deployment. Nothing about that sequence is exotic; it is what every team with a change-approval process does.
Now ask what Wednesday’s job actually knows. It knows a pipeline was green. It does not know whether the artifact it is holding is the artifact that was tested. It does not know whether the environment it is deploying into is the one the tests were validated against. It does not know whether the source has moved. In most pipelines those questions are answered by convention — the artifact came from the right job, the branch is protected, the environment variable says production — and convention is not evidence. It is a claim, held together by everybody following the process.
A receipt replaces convention with a checkable fact per gap. The test of whether a field belongs on one is simple: name the thing that could change between proof and deploy, and show that the field catches it. Six survive that test.
1. A content-addressed payload digest
The receipt must describe the exact bytes that were proven, and it must describe them by their content rather than by their location. “The artifact from build #4471” is a pointer; pointers can be rebuilt, re-uploaded, and mutated. A hash over the payload is the payload’s identity.
This is the field the entire artifact rests on, because every other field is a claim about a payload and is meaningless without a way to say which one. It is also the field that must be re-checked at deploy time, not merely recorded: the stored payload is re-hashed and compared before anything is transmitted. A byte that moved is not a warning to log, it is a hard stop — the proof you are holding is about different bytes than the ones you are about to ship.
2. The source revision
A digest pins what shipped; it does not tell a human why. When something goes wrong six weeks later, the first question is which commit this was, and the digest cannot answer it. Recording the revision the payload was built from points the evidence back at reviewable source — a diff, a pull request, an author.
The honest version of this field has to handle the dirty case. A payload built from an uncommitted worktree cannot be traced to anything, and pretending otherwise is worse than not recording a revision at all. Either the process refuses to build one, or it records the fact of the dirty tree on the receipt itself, so a reader can see that the trace stops here rather than assuming it continues.
3. A target-environment fingerprint
Validation is not a property of a payload alone. It is a property of a payload against an environment: the same metadata that validates cleanly in staging can fail in production because a field is missing, a permission set differs, or a component drifted. A receipt that does not name its target implicitly claims to be valid everywhere, which is the one thing it is not.
Two design constraints pull against each other here. The receipt has to identify the environment strongly enough that deploying it elsewhere fails closed, and it has to be safe to move around — attached to a pull request, uploaded as a build artifact, emailed to an approver. A one-way fingerprint satisfies both: enough to compare, not enough to leak. It carries no alias and no credential, so a receipt is a document about a deployment rather than a key to an environment.
The visible cost is that the deploy step must be told its target explicitly, since the receipt cannot tell it. That is the correct trade.
4. Gate results, measured against that payload
This is the field everyone thinks is the whole receipt, and it is the one most often recorded wrongly. “Tests passed” as a boolean is nearly worthless. What belongs on the receipt is what was measured, at what value, against what threshold: test counts, line coverage, mutation score where a threshold is set, static-analysis violation counts together with the rule policy that produced them.
The qualifier matters more than the list. A violation count without its rule set is unreadable next year, when the rule set has changed and nobody can tell whether zero meant clean or meant the rules were off. And the results must be measured against the digested payload, not against a working tree that happened to be nearby. A gate that ran before the payload was assembled proves something about a slightly different program, and the difference is exactly where interesting bugs live.
5. The environment-side validation job identity
Local gates prove the code is good. They cannot prove the target will accept it — only the target can do that, by checking the payload itself. On Salesforce that is a check-only validation: the same bytes, submitted to the org, compiled and tested there without deploying.
Recording that job’s identity does two things. It makes the environment-side proof travel with the receipt, so “the org accepted these bytes” is a referenceable fact rather than a memory. And on Salesforce specifically it is what makes the deploy step cheap: a validated job can be quick-deployed, so the deployment reuses the proof instead of re-transmitting and re-testing the source. The receipt stops being a record of work and becomes the work’s reusable output.
6. An expiry
Every field above describes a fact that was true at a moment. Facts about an environment decay: the org drifts, someone deploys around you, a dependency changes, an admin adds a validation rule. A receipt without an expiry silently asserts that the environment it fingerprinted three weeks ago is still that environment.
So the receipt records when the validation stops counting, and the deploy step enforces it. On Salesforce there is a natural anchor — the quick-deploy window is ten days, so a policy cap on receipt age defaults to it. On other platforms you have to pick a number, and picking one badly is still better than the implicit alternative, which is infinity.
Expiry is also the field that makes the rest enforceable rather than decorative. Once evidence can go stale, “revalidate” becomes an ordinary operation instead of an admission of failure, which is what lets the deploy path refuse a bad receipt without anybody reaching for an override.
The property that ties them together
Six fields are necessary; they are not sufficient on their own. What makes them evidence rather than documentation is that the deploy path checks them and fails closed. A receipt whose payload changed, whose validation expired, whose target does not match, or whose toolchain is incompatible must be rejected, and the answer must be to revalidate.
Which means the hardest design decision on the whole artifact is a flag you do not add. Every verification step invites a --force for the emergency, and an override flag that exists is an override flag that gets used at 5pm on a Friday, by someone under pressure, for a reason that sounded good at the time. If the receipt can be bypassed, it proves nothing about any deployment — only about the deployments where nobody was in a hurry.
Signing is the optional layer above this. It does not make a receipt more true; it makes it tamper-evident, and it records who validated. Worth having when the evidence outlives the people who produced it, and worth being clear that immutable means unaltered, not sound.
One implementation
Nimbus’s release workflow is one realisation of the above, for Salesforce. nimbus release validate snapshots the selected source into a content-addressed bundle, runs the local gates against that bundle, has Salesforce check the same bytes in the target org, and writes a receipt beside the bundle under .nimbus/releases/. It deploys nothing. The six fields are the six above: bundle digest, git revision, org fingerprint, gate results, Salesforce validation job ID, quick-deploy expiry.
nimbus release deploy re-checks all of them before it sends a byte — org fingerprint, bundle digest, receipt age, toolchain policy, and whether baseline components drifted in the org since validation — then quick-deploys the validated org-side job, or re-sends the exact stored bundle. It reruns no gates and restages no source, which is precisely why the bytes cannot change between the proof and the deploy. Each failure mode has its own exit code, and a payload that changed after validation is a release integrity violation rather than a warning.
# Machine A — CI, on merge: prove it, write the receipt
nimbus release validate --release-profile production
# ── approval happens here, as an ordinary artifact hand-off ──
# Machine B — days later, different runner
nimbus release deploy \
--receipt .nimbus/releases/rel_....json \
--release-profile production \
--confirm-production
# Offline, no org, no network: re-hash the bundle,
# check the signature
nimbus release verify rel_20260720T141342_0397df68b0
# Archive the whole evidence set as one
# self-verifying document
nimbus release export --org-kind production \
--since 2026-07-01 --output evidence-2026Q3.jsonThe receipt is deliberately a portable artifact rather than a local cache: no org aliases, no credentials, no source, so uploading one between CI jobs leaks nothing. nimbus release verify re-hashes the bundle and checks the signature offline on any machine. nimbus release export writes the receipt set plus the signer roster into a single evidence document that an auditor can re-check years later without Nimbus installed, and the assurance console renders the same set as a read-only web view for approvers who never open a terminal. There is no --force and no configuration key that reintroduces one.
Checklist
- Address the payload by content, never by build number or path.
- Record the source revision — and record it when the tree was dirty.
- Fingerprint the target one-way, so the receipt travels safely and still fails closed elsewhere.
- Store measurements, not booleans — with the policy that produced them.
- Carry the environment-side validation identity, so its proof is reusable.
- Give evidence an expiry, because environments drift.
- Check every field at deploy time and fail closed. No override flag.
Evidence that travels as a file
Validate on one runner, hold for approval, deploy the exact validated payload from another machine — because the proof moves as an artifact rather than as a claim about a worktree.