vsa attestor
Typed decoder for pre-signed SLSA VSA predicates so downstream policies can consume external VSAs as first-class attestations — distinct from policyverify, which produces a VSA at verify time.
| Field | Value |
|---|---|
| Name | vsa |
| Predicate type | https://slsa.dev/verification_summary/v1 |
| Lifecycle | verify |
| Default binary? | No — builder opt-in only |
What it captures
The attestor wraps a VerificationSummary predicate that mirrors the SLSA VSA v1 shape. Its MarshalJSON emits the embedded predicate directly so Rego sees input.verifier.id, input.verificationResult, etc.
Fields on VerificationSummary:
verifier—{ id string }identifying the party that ran verification.timeVerified— wall-clock timestamp the source VSA was produced.policy—ResourceDescriptor(uri,digest) referencing the policy that was evaluated.inputAttestations[]—ResourceDescriptorentries (uri,digest) for each attestation considered.resourceUri— the artifact the source VSA verified.verificationResult—"PASSED"or"FAILED"(the only two values defined in the package).verifiedLevels[]— the highest level verified per track (SLSA_BUILD_LEVEL_UNEVALUATEDfor a passing policy verification,FAILEDfor a failing one).stepResults[]— the per-step extensioncilock verifywrites next to the v1 fields: which collections passed and which were rejected, with the rego deny messages.
resourceUri, verifiedLevels and stepResults are omitted when the source VSA does not carry them.
ResourceDescriptor shape: { uri string, digest cryptoutil.DigestSet }.
When to use
Build this attestor in when a downstream policy needs to read a VSA that was produced outside the current pipeline — for example, a deploy gate that ingests a release pipeline's signed VSA as an external attestation, then re-asserts policy on top of it. The attestor registers by name only (FactoryByName("vsa")). A lookup by the predicate URI (FactoryByType) returns policyverify, which owns the type and decodes the same fields, so a binary that builds in both gets the same decoder whatever the package init order.
Contrast with policyverify: policyverify runs inside cilock verify and emits a fresh VSA recording the outcome of that verify run. vsa does the inverse — it has a no-op Attest() and exists purely so a pre-signed VSA predicate can be unmarshalled and exposed to Rego under canonical SLSA field names.
Flags
None. The attestor has no flag surface.
Output shape
{
"verifier": { "id": "..." },
"timeVerified": "2026-05-21T12:34:56Z",
"policy": {
"uri": "...",
"digest": { "sha256": "..." }
},
"inputAttestations": [
{ "uri": "...", "digest": { "sha256": "..." } }
],
"verificationResult": "PASSED",
"resourceUri": "...",
"verifiedLevels": ["SLSA_BUILD_LEVEL_UNEVALUATED"],
"stepResults": [
{ "step": "build", "passed": ["..."], "rejected": [] }
]
}Gotchas
- Verify-only.
RunTypeisattestation.VerifyRunType. The factory is not selectable insidecilock runandAttest()is a no-op — it never produces an attestation by itself. - Decoder, not emitter. To emit a VSA from a verify run, use
policyverify. Thevsapackage exists so external VSAs can flow into downstream policies as typed input. - Registered by name, not by type. The SLSA VSA v1 URI belongs to
policyverify; registering it twice would makeFactoryByTypedepend on init order. Ask for this decoder withFactoryByName("vsa"). - Field names match the SLSA spec, not the wrapper. Custom
MarshalJSON/UnmarshalJSONflatten the embeddedPredicatefield out of the JSON, so policies should referenceverifier,verificationResult, etc. directly — notpredicate.verifier.
CLI example
See the constraint summary + reproduction recipe at https://github.com/aflock-ai/attestor-compliance-examples/tree/main/42-vsa. This attestor is currently blocked or doc-only — the linked example explains why and shows the recipe to validate once the constraint is removed.
See also
- Catalog row
- Build a custom CI/lock
policyverify— the verify-time VSA emitter- SLSA VSA spec
Reference generated from the product documentation. Match commands and support details to your installed release.