Predicate type: ML eval-result
August 7, 2026 · View on GitHub
Type URI: https://in-toto.io/attestation/eval-result/v0.1
Version: v0.1
Authors: Konrad Gruszka (@b7n0de, ORCID 0009-0006-8947-6065)
Purpose
Attest the result of a machine-learning evaluation in a way a generic in-toto verifier can consume,
while keeping the evaluated model and dataset private. An ML eval has three properties the generic
test-result predicate does not model: a metric threshold with a pass/fail
against it, the need to withhold the model/dataset identity, and an optional binding to an external
signed receipt (and, later, an external time anchor for pre-registration).
This predicate authenticates a claim: who signed these exact eval bytes, and that nothing changed since. It does not assert the semantic truth, fairness, safety, or generalization of the result; those remain human judgements (see Non-claims).
Use Cases
- Private-model eval: publish "model M passed safety suite S at
refusal_rate >= 0.98" without revealing M or the dataset, via salted commitments; a relying party verifies the signed claim offline. - Release gating: bind a release artifact (image/wheel/service digest) to a passing eval, "deploy only if the eval passed", as the ML attach point for a policy/SLSA decision.
- Pre-registration: commit to the threshold and the dataset/model before the run, and later prove the commitment predated the result (strengthened by an external time anchor).
Prerequisites
in-toto attestation spec v1. The evaluation is expressed as one or more
threshold-based claims {metric, comparator, threshold, passed}. Identifiers that must stay private are
carried as salted commitments (a hash over a secret salt ‖ identifier); the salt stays with the
issuer and is never in the attestation.
Model
An evaluation run produces a signed, tamper-evident receipt. This predicate is a projection of that
receipt onto an in-toto Statement: the subject is what the attestation is about (the receipt itself,
a public model artifact, or a gated release artifact), and the predicate carries the eval's facts. The
detailed per-metric result lives here; a companion SVR
may summarize "a verifier confirmed this passed" as passing property strings.
Schema
{
"_type": "https://in-toto.io/Statement/v1",
"subject": [{ "name": "<optional>", "digest": { "<alg>": "<hex>" } }],
"predicateType": "https://in-toto.io/attestation/eval-result/v0.1",
"predicate": {
"verifier": { "id": "<TypeURI>" },
"evaluatedAt": "<RFC 3339>",
"suite": { "name": "<string>", "version": "<string>" },
"claims": [
{ "metric": "<string>", "comparator": ">=|>|<=|<", "threshold": "<decimal string>", "passed": <bool> }
],
"sampleSize": <int>,
"commitments": {
"model": { "alg": "<string>", "value": "<hex>", "salted": true },
"dataset": { "alg": "<string>", "value": "<hex>", "salted": true }
},
"assuranceLevel": "self_attested|third_party|reproduced|enclave_attested",
"subjectProfile": "receipt|public-model|release-gate",
"preRegistration": { "alg": "sha256", "value": "<hex>" }, // OPTIONAL
"receipt": { "schema": "<string>", "merkleRootB64": "<base64>" }, // OPTIONAL
"harness": { "name": "<string>", "version": "<string>", "digest": { "sha256": "<hex>" } } // OPTIONAL
}
}
Parsing Rules
This predicate follows the in-toto attestation
spec v1 parsing rules: consumers match on the subject digest
alone; subject[].name is a hint and MAY be "_" or omitted; unknown predicate fields MUST be
ignored (forward compatibility); and the
Monotonic Principle applies: a verifier denies unless a valid attestation
exists. Time fields are RFC 3339. threshold is a decimal string, never a JSON float, so a value
is never altered by float round-tripping.
Unless a field specifies otherwise, absence of an optional field means only that no claim is made for that field. Consumers MUST NOT infer or synthesize a default value from absence.
Fields
verifier.id (TypeURI, required): the party that emitted/verified the result.
evaluatedAt (Timestamp, required): when the evaluation ran.
suite (object, required): {name, version} of the eval suite.
claims (array, required): one or more {metric, comparator, threshold, passed}. comparator is one
of >=, >, <=, <. passed is the producer's signed threshold verdict for the stated metric,
comparator and threshold. Unless an exact observed value is disclosed by a separate profile, a generic
consumer can authenticate the verdict but cannot recompute it from the predicate alone.
sampleSize (int, required): number of samples the result is over.
commitments (object, required): model and dataset, each {alg, value, salted}. When salted is
true the value is a commitment (a hash over a secret salt ‖ identifier), NOT an artifact content
digest; a generic verifier MUST NOT treat it as one. This is what lets the evaluated model/dataset stay
private while the claim is still verifiable.
assuranceLevel (string, required): an issuer-declared assurance claim about how the result was
produced: self_attested (producer testimony), third_party, reproduced, or enclave_attested. The
value is the issuer's own declaration; this predicate does not corroborate it. External corroboration
belongs in separately referenced evidence.
subjectProfile (string, required): which subject the attestation binds to: receipt (a binder over
the receipt; reveals nothing), public-model (a disclosed model's real digest), or release-gate (a
release artifact gated on the pass).
preRegistration (object, optional): {alg, value} over the eval protocol committed before the run.
receipt (object, optional): {schema, merkleRootB64} binding to the external signed receipt.
harness (object, optional): the eval harness. name and version identify it. digest is an
optional DigestSet over the harness artifact, for consumers that need to bind
the exact artifact that produced the result. A harness that carries only name and version
remains conforming. digest binds identity only. It asserts nothing about the harness's detection
performance.
Non-claims
A verifier that accepts this attestation learns that the signed claim is authentic and unchanged. It does not learn that the metric is correct, that the eval was well designed, that the model is safe or fair, or that the score generalizes. Those are out of scope for this predicate.
This predicate does not establish that the evaluation harness or grader is fit for purpose, or that it has any particular detection performance.
The consequences can be asymmetric. When detected positives are evidence of capability, missed
positives can understate performance. When passed: true depends on the absence of detected
failures, missed failures can instead yield a passing verdict even though the failures occurred.
This attestation authenticates either verdict without establishing the harness's detection
capability.
Examples
A private-model eval (subject is the receipt; the model stays secret):
{
"_type": "https://in-toto.io/Statement/v1",
"subject": [{ "name": "eval-receipt", "digest": { "sha256": "…" } }],
"predicateType": "https://in-toto.io/attestation/eval-result/v0.1",
"predicate": {
"verifier": { "id": "https://example.com/verifier" },
"evaluatedAt": "2026-07-05T12:00:00Z",
"suite": { "name": "safety-refusals", "version": "1.2.0" },
"claims": [{ "metric": "refusal_rate", "comparator": ">=", "threshold": "0.98", "passed": true }],
"sampleSize": 500,
"commitments": {
"model": { "alg": "sha256-salted-v1", "value": "…", "salted": true },
"dataset": { "alg": "sha256-salted-v1", "value": "…", "salted": true }
},
"assuranceLevel": "self_attested",
"subjectProfile": "receipt",
"receipt": { "schema": "proofbundle/v0.1", "merkleRootB64": "…" }
}
}
A release-gate example (subject is the deployed artifact's real digest) is in the reference
implementation's examples/intoto/release-gate.statement.json.
Changelog and Migrations
- v0.1: initial draft. Reference emitter/verifier: proofbundle
(
proofbundle intoto). Discussion: in-toto/attestation#565.
proofbundle-specific note (not part of the upstream file)
Until this type is registered upstream, the reference implementation emits the vendor-namespaced
predicateType https://b7n0de.com/attestation/eval-result/v0.1 and migrates to the in-toto.io
URI on registration (a redirect/alias is added at that point). Consumers match on the subject digest,
so a predicateType rename does not affect binding.