Apply attestation v1
| Predicate type | https://dragoscv.github.io/axiom/spec/attestation/apply/v1 |
| Envelope | in-toto Statement v1 (_type: https://in-toto.io/Statement/v1), optionally wrapped in DSSE |
| Producer | axiom plan verify <bundle> --tree <dir> --attest <out.json> (old verb axiom verify), the GitHub Action with attest: true |
| Builder | buildApplyAttestation in packages/canon/src/statement.ts |
| Legacy URI | https://axiom.dev/attestation/apply/v1 — what 2.x produced. 3.x never produces it; a verifier that must accept 2.x statements treats both URIs as the same predicate (AXIOM_APPLY_ATTESTATION_V1_LEGACY in @codai/axiom-canon) |
| Status | stable |
The statement is written only after every artifact digest in the manifest has been checked
against the tree (and, when the manifest declares one, the pre-image set). A mismatch exits 1
and writes no attestation. The builder reads no clock: two runs over the same inputs produce
byte-identical JSON.
Subject
Section titled “Subject”One entry for the manifest plus one per artifact that exists after the change, sorted by
name:
name |
digest.sha256 |
|---|---|
the bundle file name given on the command line (e.g. manifest.jcs) |
hex of manifestDigest |
each root-relative artifact path whose op is not delete |
hex of the file’s sha256 |
gh attestation verify can therefore match either the manifest or any single file it wrote.
Predicate
Section titled “Predicate”| Field | Type | Meaning |
|---|---|---|
manifest |
{ sha256: hex } |
the manifest digest |
plan |
{ sha256: hex } |
the plan digest the manifest was compiled from |
profile |
string | the check profile named by the manifest |
tree |
"post" | "pre" |
which state of the tree was verified (--pre checks the declared pre-images before an apply) |
paths |
{ path, op, sha256 }[] |
every artifact path verified, sorted by path; sha256 is a hex digest or "absent" for deletes |
preImage |
{ path, sha256 }[], optional |
the manifest’s declared pre-image set, sorted by path |
verifier |
{ id, version } |
id = https://dragoscv.github.io/axiom/spec/builder/mcp@v2; version = the AXIOM version |
source |
{ repository?, ref?, sha?, runId? }, optional |
copied from GITHUB_REPOSITORY, GITHUB_REF, GITHUB_SHA, GITHUB_RUN_ID when present; never content-addressed |
Example
Section titled “Example”{ "_type": "https://in-toto.io/Statement/v1", "subject": [ { "name": "manifest.jcs", "digest": { "sha256": "4f1c…e2" } }, { "name": "src/hello.ts", "digest": { "sha256": "9b7a…10" } } ], "predicateType": "https://dragoscv.github.io/axiom/spec/attestation/apply/v1", "predicate": { "manifest": { "sha256": "4f1c…e2" }, "plan": { "sha256": "c03d…7a" }, "profile": "default", "tree": "post", "paths": [ { "path": "old.txt", "op": "delete", "sha256": "absent" }, { "path": "src/hello.ts", "op": "create", "sha256": "9b7a…10" } ], "verifier": { "id": "https://dragoscv.github.io/axiom/spec/builder/mcp@v2", "version": "3.0.0" }, "source": { "repository": "acme/web", "sha": "1a2b3c…" } }}Verifying
Section titled “Verifying”-
Re-check the tree yourself — the statement is a claim; the tree is the evidence:
Terminal window axiom plan verify bundle.json --tree . # exit 0 = every path matches the manifest -
Verify a CI-uploaded attestation (Sigstore, uploaded by the Action with
attest: true):Terminal window gh attestation verify manifest.jcs --repo <owner>/<repo> \--predicate-type https://dragoscv.github.io/axiom/spec/attestation/apply/v1Attestations made by 2.x carry the legacy type; pass
--predicate-type https://axiom.dev/attestation/apply/v1for those. -
Check the predicate —
predicate.manifestequals the digest you expect,treeis the state you meant (postafter an apply), andverifier.idis the builder URI above.
Full walkthrough: Verify tree. Signing the bundle itself: Signing.