# Probity Predicate Standard, v1

An open, vendor-neutral format for **signed, offline-verifiable security verdicts** about
software artifacts and agent servers. A verdict says, in cryptographically checkable form, that a
named artifact was run in isolation, attacked with a pinned corpus of exploits, and given a
PASS / DEGRADED / FAIL outcome with per-class evidence. The format is the public surface; the
runtime that produces it is out of scope for this standard.

The base URI for a predicate type in this standard is:

```
https://getprobity.dev/predicate/v1/<name>
```

One exception: [adversarial-execution-evidence.md](adversarial-execution-evidence.md) is proposed for
standardization in the **in-toto attestation namespace** and carries its own type URI
(`https://in-toto.io/attestation/adversarial-execution-evidence/v0.7`) rather than a
`getprobity.dev` one. A consumer always dispatches on the full `predicateType` string, so the
namespace split is transparent to verification.

## Why this exists

A verdict is only useful to a third party if that party can read and trust it **without** trusting
or re-running the tool that produced it. CI systems, admission controllers, auditors, and registries
need a stable, documented schema they can parse offline and verify against a configurable trust
anchor. This standard publishes that schema so any tool can emit a conforming verdict and any tool
can verify one. The intent is interoperability: a single agreed-upon shape for "this artifact was
adversarially tested and here is the signed result," before the surface fragments into mutually
incompatible vendor schemas.

This document is the **scope and governance index**. Each component has its own self-contained
specification (linked below). Read the envelope first; it is the substrate every predicate rides on.

## Wave 1 components (this release)

Read in this order. The order is also the dependency order: later documents reference earlier ones.

| # | Document | predicateType | What it carries |
|---|---|---|---|
| 1 | [signing-envelope.md](signing-envelope.md) | (envelope, predicate-agnostic) | DSSE PAEv1 + ed25519 envelope, key id, trust root, offline-verify procedure |
| 2 | [vocabulary-registry.md](vocabulary-registry.md) | (registry, versioned) | The closed, versioned enums (`layer`, `violation_type`, `sink`, `auth_downgrade_cause`) and the governance process |
| 3 | [catch-record.md](catch-record.md) | (component of the verdicts below) | The forensic catch-record: per-record host signature + batch Merkle root |
| 4 | [security-verdict.md](security-verdict.md) | `.../security-verdict` | Per-server scan verdict for a single artifact against an attack corpus |
| 5 | [adversarial-execution-evidence.md](adversarial-execution-evidence.md) | `https://in-toto.io/attestation/adversarial-execution-evidence/v0.7` | Bounded adversarial-stimulus evidence for a scanned artifact (Tier-2 runtime, in the box): fused recomputable result, digest-committed coverage bound, per-attack containment chain, signed intercept records |
| 10 | [static-scan-verdict.md](static-scan-verdict.md) | `.../static-scan-verdict` | Tier-1 detection verdict: static scan of the declared surface — no microVM, no runtime execution; verdict-bearing but carries no catch-policy binding |
| 9 | [sut-provenance.md](sut-provenance.md) | `.../sut-provenance` | Build provenance of a scanned SUT image: a known base image plus a fully integrity-pinned dependency closure with a deterministic binding hash |

## Substrate / mesh components (this release)

These predicates attest properties **below** the application and network perimeter - the isolation
substrate itself and the composition of a mesh of microVMs running over it. Their distinguishing
property is that a perimeter-only sandbox cannot emit them: the evidence is captured at the hypervisor
memory boundary and from the host's view of the guest, not at a syscall or network shim. Each carries
its own self-contained specification under the same envelope, vocabulary, and versioning model.

| # | Document | predicateType | What it carries |
|---|---|---|---|
| 6 | [ml-bom.md](ml-bom.md) | `.../ml-bom` | Execution-grounded model bill-of-materials: INTENT from a guest VMA walk, IDENTITY from a host-side 3-source resolver |
| 7 | [mesh-quiescence.md](../v2/mesh-quiescence.md) | `https://getprobity.dev/predicate/v2/mesh-quiescence` | Freeze-atomicity attestation: bounded hypervisor-pause distributions, captured in-flight escape, closed-world egress |
| 8 | [a2a-mesh-scorecard.md](../v2/a2a-mesh-scorecard.md) | `https://getprobity.dev/predicate/v2/a2a-mesh-scorecard` | Mesh composition-safety verdict over an aggregate MeshManifest subject (N member substrate roots + ModelBOM digests + hop graph) |

These three are format-and-verification stable and ship signed conformance bundles that verify offline
(see `conformance.json`). The shipped a2a-mesh-scorecard bundle binds a real sealed N=3 jailed mesh run
(`honest_scope.substrate_roots_real == true`, `hop_graph_live == false`): real per-member byte-identity
substrate roots with the canonical A->B->C delegation replayed, not observed live on the wire. Its
composition-safety verdict is the offline `evaluate_mesh` evaluator's verbatim escalation finding
(`scope == "ESCALATED"`, `causes == ["SCOPE_BROADENING"]`). Where a *capability* behind a predicate is
still forward-looking - the egress-proxy ML-BOM identity source or a hardware host-trust root - the
relevant document's status/maturity section says so plainly.

## Wave 2 (published, standardization track open)

The first two carry the substrate evidence a wave-1 verdict references but does not contain; the
third carries the bound around a published piece of evidence rather than any evidence itself. Their
formats, producers, and offline verification are stable and their specifications are
published here, so their type URIs resolve. What remains open is the *standardization* track: they
have not been through the cross-vendor review the wave-1 components had, and a wave-1 verifier is not
required to understand them. A consumer dispatches on the full `predicateType` string, so a verifier
that does not implement them rejects them cleanly.

| # | Document | predicateType | What it carries |
|---|---|---|---|
| 11 | [signed-evidence.md](signed-evidence.md) | `.../signed-evidence` | One signature over the guest-memory Merkle commitment, the value-binding to a signed session and verdict subject, and the cross-host fold pins |
| 12 | [substrate-integrity.md](substrate-integrity.md) | `.../substrate-integrity` | The byteident proof families about the isolation substrate: fold-correctness, clone-uniqueness, and the VMGenID-reseed differential (`entropy-uniqueness` is retired and is a rejection target) |
| 13 | [evidence-scope-disclosure.md](evidence-scope-disclosure.md) | `.../evidence-scope-disclosure` | The bound around a published piece of evidence: the layers it covers, its tier, whether it is a capture or a representative fixture, and the producer's prose statement of what it does not show |

A wave-1 reference to a "substrate root" is an opaque digest plus a `substrate_proof_type`
discriminator naming the proof family; the proof itself is served alongside and verified out of band
against [substrate-integrity.md](substrate-integrity.md).

## Envelope summary

Every predicate in this standard is the `predicate` field of an
[in-toto Statement v1](https://github.com/in-toto/attestation/blob/main/spec/v1/statement.md)
(`_type: https://in-toto.io/Statement/v1`), wrapped in a
[DSSE](https://github.com/secure-systems-lab/dsse) envelope and signed with ed25519. The Statement
binds the predicate to a **subject** (one or more named digests - always the artifact the verdict is
about). The signature is computed over the DSSE Pre-Authentication Encoding (PAEv1) of the Statement,
never over bare bytes, so a signature for one payload type can never be replayed as another. Each
envelope MUST carry a `keyid` equal to the lowercase-hex SHA-256 of the RFC 7638 JWK thumbprint input
for the ed25519 public key. The full construction, the trust-root model, and a worked offline-verify
example are in
[signing-envelope.md](signing-envelope.md).

## Versioning and forward-compatibility

The version is part of the contract and is encoded in the predicate type URI:

- **Major version in the URI.** Every predicate type carries its major version in the path
  (`/predicate/v1/...`). A consumer dispatches on the full `predicateType` string and MUST reject a
  predicate whose type it does not recognize. A new major version is a new URI.

- **Additive-optional fields are a minor change and stay within the major version.** A producer MAY
  add new optional fields. Adding an optional field does NOT change the major version.

- **A verifier MUST ignore unknown fields.** This is the load-bearing forward-compatibility rule: a
  v1 verifier presented with a predicate that carries optional fields it does not know MUST NOT
  reject the predicate on that basis alone. It validates the fields it understands and ignores the
  rest. This lets producers ship additive fields without breaking deployed verifiers.

- **Rename, removal, or any semantic change of an existing field is a major version (v2).** Changing
  what a field means, deleting it, or renaming it is breaking and requires a new major version URI.
  It is never shipped as a silent in-place change a deployed verifier is expected to tolerate.

### Producer-strict vs verifier-tolerant (stated explicitly)

This standard deliberately holds two rules that look contradictory and are not. They apply to
different parties:

- **A PRODUCER MUST be strict.** A conforming producer emits only the fields defined for the
  predicate version it claims, and rejects its own output if it carries fields it does not recognize.
  Producer-side validation is closed: unknown field means a bug, refuse to sign.

- **A VERIFIER MUST be tolerant of additive fields.** A conforming verifier ignores fields it does
  not recognize rather than rejecting the whole predicate, exactly so that a producer shipping a
  newer minor version can be read by an older verifier.

Strict-out, tolerant-in. The asymmetry is the mechanism that makes additive evolution safe. Note the
one exception: **vocabulary values are fail-closed at a version** (see below) - a verifier MUST
reject an unknown *enum value* even though it ignores an unknown *field*. The difference is that an
unknown field is forward-compatible additive data, whereas an unknown enum value for a known field is
a value the verifier cannot interpret and so cannot trust.

## Vocabulary registry and governance

Some fields draw from closed enumerations (the `layer`, `violation_type`, and `sink` vocabularies
used by [catch-record.md](catch-record.md)). These are not free text. They are published in a
**versioned registry** ([vocabulary-registry.md](vocabulary-registry.md)) whose version is tied to
the predicate version.

- The registry is **fail-closed at a version**: a verifier MUST reject any value not present in the
  registry version it knows. There is no warn-and-continue mode.

- **Adding a value is a registry revision**, published as a new registry list, not a silent change a
  deployed verifier is expected to tolerate. Producers and verifiers move to a new registry version
  together.

- **Governance.** Probity maintains the registry initially. Additions and changes go through a
  published contribution process (a `CONTRIBUTING` document and pull-request review). The intent is
  to graduate maintenance to a neutral, multi-party process as adoption grows; until then, changes
  are proposed in the open and reviewed before a new registry version is cut.

## Related standards

This standard is built to compose with the existing supply-chain and security-finding ecosystem
rather than replace it. Where it aligns and where it diverges:

- **[in-toto attestation](https://github.com/in-toto/attestation)** - ALIGNED. The envelope is an
  in-toto Statement v1; the subject-binding and `predicateType` routing are used exactly as in-toto
  defines them.

- **[DSSE](https://github.com/secure-systems-lab/dsse)** - ALIGNED. The signing envelope is DSSE with
  PAEv1. No deviation.

- **[SLSA Verification Summary Attestation (VSA)](https://slsa.dev/spec/v1.0/verification_summary)** -
  PARTIALLY ALIGNED. A Probity verdict plays a role similar to a VSA (a signed statement that a
  verification was performed and what it concluded). The engine-identity field in
  [security-verdict.md](security-verdict.md) is modeled on the VSA `builder.id` idea (an
  auditor-pinnable identity for the thing that produced the verdict). Probity diverges in carrying
  adversarial per-attack results and a forensic evidence reference rather than a policy-evaluation
  summary.

- **[SARIF](https://sarifweb.azurewebsites.net/)** - CROSSWALK. Per-finding `check_id` values in
  layer-carrying verdicts (see [static-scan-verdict.md](static-scan-verdict.md)) map onto SARIF rule
  identifiers; a producer MAY carry findings as, or reference, a SARIF document. Probity does not
  re-specify SARIF; it points at it. (Layer findings are out of the adversarial-execution-evidence
  core; a producer that carries them does so via the `_ext` extension convention - see
  [adversarial-execution-evidence.md](adversarial-execution-evidence.md).)

- **[OpenVEX](https://github.com/openvex/spec)** - ALIGNED for revocation. A verdict's trust in its
  attested substrate can be revoked out-of-band via an OpenVEX statement; the verifier consults a VEX
  log to void a stale PASS (see [security-verdict.md](security-verdict.md)).

- **[CycloneDX](https://cyclonedx.org/)** - RELATED, not overlapping. CycloneDX describes what an
  artifact *contains* (a bill of materials). This standard describes what *happened* when the
  artifact was attacked. The two are complementary: a verdict's subject digest is the same artifact a
  CycloneDX BOM would describe.

## Verifiable today vs roadmap (honesty section)

This standard is technically honest about what a third party can independently check versus what it
must take as a producer self-assertion. Where a field is a self-assertion a verifier cannot verify on
its own, the relevant document says so plainly. In summary:

**Compliance-framework mapping.** The crosswalk from one signed verdict to the controls of ten
compliance frameworks — and which mappings are emitted-today vs roadmap (the OSCAL assessment-results
document, the per-finding OWASP/ATLAS taxonomy tags, and the per-framework evidence packs are roadmap;
we emit DSSE/in-toto + SARIF today) — is in [compliance-crosswalk.md](compliance-crosswalk.md).
Consumers MUST follow its bright line: a verdict's signature re-verifies offline; the framework mapping
is an assertion of that crosswalk, never a claim that a bundle *contains* an OSCAL package or a
taxonomy tag.

**Independently verifiable today (no trust in the producer required):**

- The DSSE signature and the `keyid` binding (anyone with the public key can verify the bytes).
- The subject digest binding (the verdict is cryptographically tied to a specific artifact digest).
- The catch-record per-record signatures and the batch Merkle root (the host's intercept evidence is
  signed and the set/order of records is committed).
- Vocabulary conformance (a verifier checks every enum value against the registry version it knows).
- The value-binding anchors in the security verdict **when the auditor supplies the external
  reference values** (corpus exhaustion against the auditor's known corpus id set; substrate freshness
  against the auditor's approved digest). These are verifiable, but only relative to anchors the
  auditor brings, not from the verdict alone.

**Producer self-assertions (a verifier cannot independently confirm these from the verdict alone):**

- That the attack corpus contained *real* exploits and not no-ops - a verifier confirms the corpus
  *digest* but cannot, from the verdict alone, confirm the corpus *content* is adversarial. Exhaustion
  is checked against an auditor-supplied anchor, not asserted as a solo completeness guarantee.
- That the engine identity recorded is the engine that actually ran - pinnable and comparable against
  an auditor-known identity, but the verdict does not let a third party re-derive it.
- Everything an [evidence-scope-disclosure](evidence-scope-disclosure.md) carries about its own
  scope - the `honest_scope` prose, the `is_synthetic_fixture` flag, the coverage map, and the
  live-attestation posture. That predicate is built entirely out of self-assertions on purpose, and
  its observation table names each one. A verifier checks the shape and the internal consistency
  rules; nothing in it grounds a claim that the work described happened.
- For the substrate/mesh predicates: that the freeze timing numbers, the captured-segment evidence, the
  network-topology booleans, the hop log, and the VMA-walk access proofs were produced by a real run
  rather than asserted. A verifier confirms integrity, subject binding, and internal-rule consistency
  (well-formed distributions, the atomicity / bounded-pause rules, full-file-only ML-BOM identity,
  `auth_downgrade_cause` registration), and can chase every bound digest to its served evidence - but not external
  re-execution. Each substrate/mesh document states this in its own honesty section.

**Roadmap (not in v1):**

- A decentralized, keyless trust model (TUF / Sigstore-Fulcio) is deferred to a v2 envelope profile;
  v1 uses configurable public-key trust anchors.
- Cross-vendor standardization review of the wave-2 predicate families above. Their formats and
  offline verification are stable and published; a wave-1 verifier is not required to implement them.
- Automated, feed-driven substrate revocation (v1 uses a manually consulted VEX document).
- The forward-looking *capabilities* behind the substrate/mesh predicates (the egress-proxy ML-BOM
  identity source, the hardware/zkVM host-trust root). The predicate *formats* and offline
  verification are stable now; the shipped a2a-mesh-scorecard bundle binds a real sealed N=3 jailed run
  (`substrate_roots_real == true`, `hop_graph_live == false`) carrying the offline mesh evaluator's
  verbatim escalation verdict. The forward-looking leg is the live on-the-wire delegation observation
  (`hop_graph_live == true`), not yet shipped.

## Conventions

- The key words MUST, MUST NOT, REQUIRED, SHOULD, SHOULD NOT, MAY, and OPTIONAL in every document of
  this standard are to be interpreted as described in [RFC 2119](https://www.rfc-editor.org/rfc/rfc2119).
- All digests are SHA-256 unless stated otherwise, encoded as 64-character lowercase hexadecimal.
- All documents and all on-wire values are ASCII or UTF-8; no document of this standard requires any
  non-ASCII content.

<!--
Design rationale (repository-internal; not part of the published standard):
The v1 design recorded here was settled in a cross-vendor adversarial review.
See docs/website-strategy/ai-feedback-2026-06-14/PREDICATE-SPEC-DEBATE-2026-06-15.md
(relative to this file: ../../docs/website-strategy/ai-feedback-2026-06-14/PREDICATE-SPEC-DEBATE-2026-06-15.md).
-->

