Build provenance attestations
What the build attested about the artifacts it produced, read from the in-toto statements it already wrote, whether bare or inside a DSSE envelope, and recorded as the document states it. Nothing here verifies a signature.
| Facts key | provenance |
| Version | v1 |
| Script | provenance.py |
| Timeout | 60 seconds |
Inputs
| Input | Description | Default | Environment |
|---|---|---|---|
reports | Comma separated globs matched against every file in the working directory and against bare file names, naming the attestations to read. | *.intoto.jsonl,*.att.json,attestation*.json,provenance*.json,*.dsse.json | GUARDRAIL_INPUT_REPORTS |
A guardrail that declares an input of the same name passes it through, so the guardrail's configuration in buildnote.json is what decides these values.
A guardrail asks for these facts by name, and reads them back the same way:
{
"collect": ["provenance"]
}provenance = guardrail.facts("provenance")Facts
The document provenance collects. A path carrying [] is an entry of the list before it, and one carrying [path] is a key of the object before it.
| Fact | Meaning |
|---|---|
source | report when an attestation was read, none when there was none. |
reason | Why nothing was read, present only when source is none. |
reports | Every file that was read, in path order. One file can carry several attestations. |
reports[].path | Path of the file, relative to the directory the CLI runs in. |
reports[].modified | When it was last written, ISO 8601, so an attestation older than the build is recognisable. |
reports[].format | Format it was read as, always intoto. |
counts.total | Attestations across every file that was read. |
counts.signed | Attestations that arrived inside a DSSE envelope. The envelope is recorded, never verified, so this counts attestations carrying a signature rather than attestations whose signature was checked. |
attestations | The attestations themselves. |
attestations[].predicateType | What the statement claims to be, such as https://slsa.dev/provenance/v1, or null when it names none. |
attestations[].builder | Identity the statement gives the builder that produced the subjects, or null when it names none. |
attestations[].sourceUri | Source the statement says was built, or null when it names none. |
attestations[].sourceDigest | Digest of that source, or null when the statement carries none. |
attestations[].subjects | The artifacts the statement is about. |
attestations[].subjects[].name | Name the statement gives the artifact, empty when it gives none. |
attestations[].subjects[].digest | Digest of the artifact, by algorithm, as the statement carries it. |
attestations[].envelope | true when the statement arrived inside a DSSE envelope, false when it arrived bare. |
A collector that cannot finish prints what it had along with an incomplete key saying why, so the facts below the point it stopped are absent. A check that cares reads incomplete before the rest.
Example facts
The example facts collected by provenance collector:
{
"source": "report",
"reports": [
{
"path": "provenance.json",
"modified": "2026-03-04T10:15:00Z",
"format": "intoto"
}
],
"counts": {
"total": 1,
"signed": 0
},
"attestations": [
{
"predicateType": "https://slsa.dev/provenance/v1",
"subjects": [
{
"name": "widget-1.4.0.jar",
"digest": {
"sha256": "3f786850e387550fdab836ed7e6dc881de23001b5b6e9f0b1ad1a4f0e0d0a1c2"
}
}
],
"builder": "https://github.com/actions/runner/github-hosted",
"sourceUri": "git+https://github.com/acme/widget@refs/tags/v1.4.0",
"sourceDigest": "9b2c1d4e6f8a0b3c5d7e9f1a2b4c6d8e0f1a2b3c",
"envelope": false
}
]
}