Skip to content
New kind of attack — caught and fixed before it was publicly known·Read the timeline
Probity Open Predicate Standard
/predicate/v1/adversarial-execution-evidence-consumer-policy
View raw .md →

Adversarial Execution Evidence — Consumer Policy Profile, v0.1

<!-- restatement-banner:begin Every figure inside this block is recomputed by scripts/lint_spec_restatement.py and is a build failure when it is wrong. Do not edit a number here by hand; re-derive with --repin and read what it reports. These sentinels are the gate's parse boundary and the reason the banner's own MUSTs are not counted as obligations. Removing or moving one is a build failure. -->

THIS DOCUMENT IS A PROFILE, NOT THE SPECIFICATION

The authoritative predicate text is spec/predicates/adversarial-execution-evidence.md in the in-toto/attestation fork, mirrored byte-identically in the aee-conformance repository. That document is 2,322 lines, whose sha256 is 759d2383e5da36fa509dc335e6159a20b87641b25ebbadcf1676c55d75ffd8b0; this one is 798. This document does not restate the predicate. It states the consumer-policy half the predicate deliberately assigns outside itself, and most of what is written here is text the authority does not carry. The ledger below says which sentences those are, one row each.

Do not read a sentence here as one the specification imposes. The pin ledger adversarial-execution-evidence-consumer-policy.pins.json records, per sentence, which of the two it is. A sentence recorded as anchored is carried verbatim by the authority at a named span. A sentence recorded as unanchored under class profile-addition is new normative text proposed for the next revision of the predicate and has no standing beyond this profile until that revision lands. A sentence under prose-divergence restates an authority obligation in different words, and only a person reading both texts establishes that the two say the same thing.

Why this exists. The predicate splits verification in two. A byte-pure validity core every consumer evaluates identically, and everything irreducibly trust-relative, which it assigns to consumer policy. As of 2026-07-30 the trust-relative half existed only as one vendor's undocumented policy across three enforcement rails, and an audit of that policy found ten consumer controls of which two are derivable from the published predicate, the absent-pin behaviour of those two is not, the highest-value control is not reachable from the published text at all, and the one obligation the predicate makes mandatory is missing from the implementation. Four independent implementers reading the predicate today would build four different consumers, and none of them would build ours.

This file carries 38 sentences imposing MUST and 0 imposing REQUIRED. Every normative sentence below is enumerated there: 8 are anchored to a span of the authority that carries that sentence verbatim, and 30 are recorded as unanchored. The counts are over sentences, taken with this banner and the fenced examples excluded and wrapped paragraphs rejoined first.

What the pin proves, and its honest limit. That the authority has not moved since a person last read it against this text, and that every predicate member this profile constrains exists in the authority at all. It does not prove the authority imposes any obligation here, and for most of them it does not.

<!-- restatement-banner:end -->

Profiles: https://in-toto.io/attestation/adversarial-execution-evidence/v0.7

Status: draft for the standards thread. Nothing here is adopted upstream.


0. Scope, and what this document is not

0.1 The split this profile occupies

The predicate states the boundary it draws, and the boundary is the reason this document exists rather than a longer specification:

The comparison is deliberately not a validity gate: validity is a function of carried bytes alone and holds identically for every consumer, while the expected corpus and substrate differ per consumer.

A verifier proceeds in two stages. Stage one is byte-pure and belongs to the predicate: statement well-formedness, coverage validity, the result recompute, and manifest and vocabulary digest integrity. Stage two is trust-relative: the envelope signature, the per-row evidence tier against consumer key policy, then the strength orderings and the rest of consumer policy. This profile is stage two, and only stage two.

The membership test for this document is mechanical, and it is not "does the predicate currently say so". It is: does the rule read anything the consumer supplies? A rule with no consumer operand — no pin, no key, no clock, no threshold — is byte-pure and belongs in the predicate's validity gate however recently it was written, however unimplemented it is upstream, and however convenient it would be to ship it here first.

0.2 Three rules deliberately moved OUT of this profile

Three obligations were carried as one vendor's consumer policy at the time this profile was written. All three are byte-pure by the test above, and publishing them here would announce as trust-relative three rules that hold identically for every consumer — precisely the confusion this document exists to end. Each belongs in the predicate's coverage-validity list, and each is filed there rather than here:

  • A clean row may not resolve an observationRefs index to an interception record. Shipped today as an unconditional admission rule with no consumer knob gating it. It is a pure predicate over containmentObserved, observationRefs, and the decoded kind of a carried record.
  • Every carried interception record is resolved by at least one caught row. Unwritten anywhere. Same shape, same argument. Its value against an attacker who deletes the records rather than orphaning them is zero, and it must not be priced as a closure.
  • subject cardinality. The predicate already makes this a validity rule — "For this predicate subject MUST contain exactly one entry on a statement of any basis; a statement carrying zero or more than one subject is malformed, regardless of whether any row is basis: substrate" — and the reference rego rail enforces it nowhere while two corpus vectors sit on a denylist under two mutually contradictory reasons for why it is out of scope. It is trivially expressible and it is not consumer policy.

A reader looking for those three in this document will not find them, and that absence is deliberate rather than an omission.

0.3 Conformance language

MUST, MUST NOT, MAY and SHOULD carry their RFC 2119 senses. They bind a consumer: a party that reads an adversarial-execution-evidence statement and makes a decision from it. They bind no producer and constrain no wire format.

A conforming consumer at a stated level satisfies every obligation that level names, and declares — in the machine-readable rail map of section 6 — the disposition of every obligation it does not enforce. A consumer that silently omits an obligation is non-conformant even where the omission is forced by its engine, because the omission and the declaration are what a relying party can tell apart.


1. The obligation registry

Fourteen obligations. Each carries a permanent slug of the form aee-p-<name>. The slug is never renumbered, never reused, and retired only by a tombstone row.

Why a slug and not an ordinal. Every ordinal in the reference implementation that names an obligation has drifted, and the drift reached the diagnostics: a filter step renumbers Kyverno condition indices so that conditions 13 through 16 are reported as 11 through 14 in every corpus-run message; a prose reference to "conditions (6) and (7)" as separately requiring basis and method names a single combined entry and a length check; a policy header says fifteen conditions where the file carries sixteen. Spec line anchors are no better for this purpose — an obligation outlives any line of a document under active review, and the anchor gate that exists in the sibling repository exists because those lines move. A slug cannot drift, because there is nothing to recompute.

SlugObligation, in shortSourceConsumer inputAbsent-input default
aee-p-tier-derivationderive a per-row evidence tier before crediting any basis: substrate rowauthority L537-542substrate observation keysmandatory; no opt-out
aee-p-no-tofuwith no pinned substrate root, every basis: substrate row is unattested, and the root is never inferred from the predicateauthority L542-545samemandatory; no opt-out
aee-p-ignore-carried-tierignore a carried evidenceTier, and any predicate-level member with the reserved prefixauthority L550-552nonemandatory; no opt-out
aee-p-corpus-anchorpin the expected corpus digest out of band and compare it at consumptionauthority L1213-1218expected corpus digestdeny, on the two-leg argument
aee-p-substrate-anchorpin the expected substrate digest out of band and compare it at consumptionauthority L1213-1218expected substrate digestdeny, on the two-leg argument
aee-p-thresholdadmit only the result tokens this consumer has declaredauthority L448-449accepted result tokensthe authority's own default, the strict end
aee-p-clean-row-livekey on each clean row's vantage, directness and derived tierauthority L450-453, L1270-1272a declining booleankeep the obligation
aee-p-key-window-operandevaluate a key validity window against a substrate-signed instant, never the producer's clockauthority L925-943per-key validity windowvacuous; the obligation is conditional
aee-p-admission-resultexpose one admission result conjoining validity, tier policy and the anchorsauthority L1223-1226nonerecommended, not mandatory
aee-p-demanded-scopepin the assessment classes this deployment demands and require the evidence to cover themnewdemanded class codesdeny, on the third-leg argument
aee-p-issuance-lagbound the interval between the substrate-signed instant and the producer's clocknewa bound in hoursvacuous, deliberately
aee-p-evidence-agebound the age of the evidence against the substrate-signed instantnewa bound in hoursvacuous, deliberately
aee-p-posture-allowlistrequire the carried network posture to be one this deployment acceptsnew, promoted from a MAYallowed posture tokensvacuous
aee-p-catchpolicy-pinrequire the carried catch-policy digest to equal a pinned onenew, promoted from a MAYexpected catch-policy digestvacuous

The Source column is the justification for publishing at all, and it should be read as the answer to one question: could an independent implementer, reading only the published predicate, arrive at this obligation? Five of the fourteen rows say new, and for two more — the anchors — the obligation is reachable while its absent-input behaviour is not. Of section 2's additions only the two replay pins have any counterpart upstream, and that counterpart is a bare permission with no mechanism attached.


1.1 aee-p-tier-derivation — the one obligation the predicate makes mandatory

The authority states it, and this profile adds nothing to it:

Given a valid attestation, a consumer MUST, before crediting any basis: substrate row or applying either strength ordering, derive a per-row evidence tier: a basis: artifact row is declared;

A basis: substrate row is attested when every covering record's signature verifies against a key this consumer's policy names as a substrate observation key, and unattested otherwise. The tier is total and deterministic given the key policy, and it never alters result.

A consumer MUST NOT treat this obligation as optional on the ground that its engine cannot perform it. An engine with no signature primitive cannot derive a tier; that fact makes the consumer's admission result mean less, and it does not make the obligation go away. Such a consumer declares unreachable under section 6 and states the reduced meaning of its result under section 5.

Consumer input. A set of substrate observation keys, each an Ed25519 public key. A record's keyid is an unauthenticated lookup hint that selects which pinned key to try and is never the check itself.

What declining costs. Everything the predicate's second gate is for. Every field a keyless consumer reads out of a record payload — aeeRunBinding, aeeMethod, the record kind — comes out of a base64 payload whose signature nothing in that consumer's path checks, so anything a real substrate can write into a record, the party assembling the statement can write too.

1.2 aee-p-no-tofu — no trust on first use

A consumer with no policy-pinned substrate root MUST treat every basis: substrate row as unattested and MUST NOT infer the substrate root from the predicate.

This is the obligation that makes aee-p-tier-derivation total rather than conditional: there is no state in which a consumer has no key policy and therefore no tier. It has one. Every substrate row derives unattested, and a consumer whose admission rule requires attested admits no substrate-bearing statement at all until it pins a key.

A consumer MUST NOT resolve a record's keyid, or any other carried value, into a trust anchor it did not already hold. The predicate carries the substrate's own subject reference in observationEnvironment.substrate, and reading that as an instruction about which key to trust is exactly the inference this obligation forbids.

1.3 aee-p-ignore-carried-tier — the reserved prefix

A carried predicate member named evidenceTier, or any predicate-level member beginning with the reserved prefix aee, MUST be ignored and MUST NOT alter the derivation.

A consumer satisfies this by never reading such a member. Satisfying it by accident — because no rule happens to name one — is satisfaction, but it is not a declaration, and section 6 requires the disposition to be stated either way. The distinction matters the first time a producer ships a carried tier and a consumer that never considered the question starts reading it because the field is conveniently there.

1.4 aee-p-corpus-anchor and aee-p-substrate-anchor

The authority makes both mandatory in one sentence, and the sentence is unambiguous about the members compared:

A consumer MUST pin, out of band, the corpus digest and the substrate digest it expects for the deployment it is admitting into, and at consumption MUST compare them against observationEnvironment.corpus.digest and observationEnvironment.substrate.digest; on mismatch the attestation is not admitted, exactly as an attestation whose covering signatures do not verify is not admitted.

These are two obligations rather than one because a consumer can hold one value and not the other, and a rail that folded them together would let an operator who pinned the corpus believe it had pinned the substrate.

Why in-policy re-derivation cannot substitute, and this is the part an implementer gets wrong. Every other corpus check available to a consumer is self-consistency: the digest commits the manifest, the coverage parts partition it, the attack identifiers exhaust it. Self consistency costs an attacker holding only the envelope key nothing. Substituting a weaker corpus for the real one — one declaring a single trivial attack the run duly passes — and re-hashing the substitute needs no key at all, and the substituted statement then satisfies every recompute a consumer can run. The difference between the real corpus and the substitute is not carried in the bytes, so no amount of in-policy re-derivation can reach it. Only a value pinned outside the attestation can.

A consumer MUST NOT derive a pinned anchor value from a bundle the party being checked supplied. A digest copied out of a producer's own statement pins whatever the producer chose to ship, and it will match forever.

The absent-input default is argued in section 2.1, and it is the single most important sentence in this profile that the published predicate does not contain.

1.5 aee-p-threshold — the admission threshold, and why it is not the row gate

The default admission threshold is result == "pass".

A consumer MAY accept pass_indirect beside pass. A consumer's accepted-result set MUST be either {pass} or {pass, pass_indirect} and MUST NOT be any other set. This is a threshold over the ordering fail < degraded < pass_indirect < pass, not a free choice of tokens: a set naming pass_indirect without pass names no threshold, the empty set names none, and a set naming degraded admits a disclosed coverage gap. Each is a typo rather than a policy, and a consumer MUST refuse a malformed accepted-result set by name rather than falling back to a default, because a fallback here silently narrows or widens a running deployment.

The absent-input default is the authority's own, {pass}, and this is the one place in the profile where an absent input neither denies nor is vacuous. The corpus, substrate and scope pins deny when absent because the obligation exists whether or not the consumer states it and only the consumer holds the value. The replay pins and the freshness bounds are vacuous when absent because nothing obliges a consumer to set them at all. Here the predicate itself supplies the value an absent pin would otherwise have to demand, and that value is the strict end of the range, so absence cannot admit anything a pin would have refused.

1.6 aee-p-clean-row-live — the row obligation a relaxed threshold must keep

A consumer MAY accept pass_indirect, and a consumer relaxing its threshold below pass MUST additionally key on each clean row's basis and method and on that row's derived evidence tier, because below pass the ordinal stops distinguishing them: a degraded reached through a disclosed coverage gap and a degraded whose clean rows are all artifact carry the same token.

and, at the authority's own consumer-policy example:

a policy relaxed to admit pass_indirect MUST keep the rule, because the token states that some clean row is indirect and never which one.

The obligation itself: a clean row — one whose containmentObserved is inside the carried observationVocabulary.labels and outside its caught subset — is basis: substrate and method: intercepted. A clean row that is artifact-basis or reconstructed is a "nothing bad happened" claim assembled from state diffing rather than from a live observation at the substrate.

Three normative consequences, and the first two are where the reference implementation was wrong until 2026-07-30.

A consumer MUST NOT relax aee-p-threshold to admit pass_indirect and decline aee-p-clean-row-live in the same act. These are two declarations, and a policy in which one flag performs both is non-conformant however its two halves are individually defensible. Under such a flag no configuration admits pass_indirect with the row rule still running, so the conformant posture is not merely easy to miss, it is inexpressible.

A consumer that cannot derive an evidence tier MUST NOT relax aee-p-threshold at all. The authority's sentence requires a relaxing consumer to key on the derived tier as well as on basis and method, so the relaxation is unavailable to a consumer that declares aee-p-tier-derivation unreachable. Separating the threshold from the row gate satisfies the basis-and-method half of that sentence and leaves this half open; a keyless rail offering the relaxation is non-conformant on this row even after that separation.

A consumer declining aee-p-clean-row-live under a {pass}-only threshold MUST be refused rather than accepted as a no-op. A statement carrying an indirect clean row recomputes to pass_indirect and is refused by the threshold before the row gate is reached, so that pairing changes nothing, and an operator who wrote it wanted something it does not do.

Polarity. The authority's non-normative example spells this knob as an opt-in to strictness whose default is lax. This profile inverts it: the obligation holds by default and a consumer declines it explicitly. An implementer following the example literally builds a less safe consumer than an implementer following this profile, and that divergence is stated here rather than left to be discovered.

1.7 aee-p-key-window-operand — the operand, where a consumer bounds a key

The obligation is conditional on the consumer choosing to bound a key's validity, and where it applies the authority fixes the operand hard:

Where it does, that window MUST be evaluated against a substrate-signed instant, and the one this predicate mandates is the armedAt carried inside an arming record whose signature verifies under the key being bounded; it MUST NOT be evaluated against issuedAt.

issuedAt is producer-asserted, sits outside every substrate signature, and is not among the run binding digest's inputs, so a party holding the envelope key moves it at will, changing no digest and breaking no signature. A window evaluated against it rehabilitates, by back-dating alone, every record the revoked key ever signed.

A consumer bounding a key's validity MUST refuse a statement carrying no arming record that verifies under the bounded key, and MUST NOT fall back to issuedAt. Such a statement carries no substrate-signed instant for that key and therefore supports no claim about its own age. Reading absence as satisfaction is the failure this obligation exists to correct.

The predicate amended itself for this operand once already, and the changelog states why in a sentence this profile takes as its own standard of evidence: one implementer choosing the defeated operand is a mistake, and two choosing it separately is a property of how the field reads.

1.8 aee-p-admission-result — one result, not two

Verification surfaces SHOULD expose one consumer-facing admission result that conjoins validity, tier-policy satisfaction, and the anchor comparison, so a result-only consumer cannot read a valid-but-wrong-context attestation as admissible.

This profile keeps it a SHOULD and widens what the conjunction covers: an admission result exposed under this profile conjoins validity, every obligation the consumer's declared level names, and every obligation it declares enforced above that level. A surface exposing an admission result MUST NOT report as admitted a statement failing any obligation it declares enforced.


2. The obligations this profile adds

Four obligations and one absent-input rule that the published predicate does not carry. This section has no counterpart upstream and is the profile's clearest single contribution.

2.1 The absent-input default for the two anchors: DENY

The predicate makes the anchor comparison mandatory and says nothing whatever about a consumer that has not pinned. A consumer that has pinned neither the expected corpus digest nor the expected substrate digest MUST refuse the statement, and MUST NOT treat the absent pin as vacuously satisfied. Two legs carry this, and both are needed.

The appeal-to-the-predicate leg. A policy that treats an absent mandate as vacuously satisfied has implemented a recommendation. The two replay pins in section 2.4 default permissive precisely because nothing in the predicate obliges a consumer to set either; this pair is different in kind.

The asymmetric-failure leg. The two failure modes are not symmetric. Defaulting permissive fails silently and indefinitely: an operator who believes they pinned, or who never read this far, admits evidence about any corpus and nothing in the verdict ever says so. Defaulting strict fails loudly and once, at rollout, with an error naming the exact input that is missing.

The cost, stated rather than hidden. An unedited copy of a policy written to this profile admits nothing until the operator pins. Rolling out through an audit-first variant measures the blast radius before it blocks. Declining this obligation MUST be an explicit act and MUST NOT be reachable by leaving an input unset.

2.2 aee-p-demanded-scope — the demanded assessment classes

A consumer MUST pin the set of assessment class codes its deployment demands, and MUST refuse a statement whose coverage.assessedClasses does not contain every demanded code. Absent that pin the consumer refuses, on the argument below.

Classes, not attack identifiers, and an implementer asks this first. Attack identifiers are corpus-version-scoped, so a consumer pinning them re-pins on every corpus revision, and the list it would re-pin to is one it read out of the producer's own manifest rather than one it chose. A pin whose value comes from the party being checked is not a demand.

This is not the corpus anchor restated. The corpus anchor pins bytes and says nothing about what those bytes must contain; a consumer that pinned a digest it copied out of a producer's bundle has pinned whatever the producer chose to ship. This is the only pin in the profile whose value a consumer derives from what it wants rather than from what it was handed.

The absent-input default is deny, and the appeal-to-the-predicate leg does not carry it. The demanded-scope obligation is new text in this profile, so a consumer unpinned here would not be implementing a recommendation in place of a mandate. A third leg replaces that one and is stronger: an unpinned corpus anchor still leaves the consumer every self-consistency rule available to it, a weakened control; an unpinned scope demand leaves it nothing, because the withdrawal is provably invisible in the bytes. Absence here is not a degraded control, it is the absence of one, and that is the case for making the operator say so.

The opt-out is its own input. The two decline different things: declining the anchors concedes "evidence about any corpus and any substrate", declining the scope demand concedes "a producer may withdraw any class it likes". A consumer MUST NOT fold the two declinations into one input, because an operator who declined the digest pin would then have declined the scope demand without ever saying so.

An empty pin is not a pin. An empty array, or a value that is not an array, comprehends to the empty set. A consumer MUST treat an empty demanded-class set as an absent pin and refuse under the rule above, naming the input.

Its honest limit travels with it. This obligation converts an undetectable attack into a documented consumer obligation that some consumers will not perform. That is a real reduction in assurance relative to a validity rule, and it is the best available, because the validity rule does not exist: coverage suppression is unreachable by any rule over the carried statement, measured byte-identical on 27 of 27 finding-bearing baselines against honest analogues, three rails agreeing. And the obligation does not reach the form in which a corpus manifest never declared the demanded class in the first place, which is the one form of scope withdrawal that reaches a passing verdict.

2.3 aee-p-issuance-lag and aee-p-evidence-age — the two freshness bounds

Neither bound exists in the published predicate. What the predicate does fix, normatively, is the operand both must use, and section 1.7 states it.

A consumer bounding the interval between the substrate-signed instant and the producer's clock MUST evaluate it as issuedAt minus the armedAt of every carried arming record, and MUST refuse the statement if any one of them exceeds the bound. Every arming record, not the earliest or the latest: a run whose vantages were armed hours apart is a run whose weakest record is the one that matters.

A consumer bounding the age of the evidence MUST evaluate it as its own clock minus armedAt, and MUST NOT evaluate it against issuedAt.

The absent-input default is vacuous, and deliberately not the deny that the anchors and the scope demand take. Those implement obligations that exist whether or not a consumer states them. There is no such obligation here: the predicate does not require a consumer to bound the age of its evidence at all, and the obligation it does state is conditional — that a consumer which bounds age evaluates the bound against a substrate-signed instant. Denying an unpinned consumer would be inventing a requirement rather than enforcing one.

The applicability rule is not vacuous, and it is where an implementation goes wrong. A consumer that has pinned a bound and is handed a statement carrying no arming record MUST deny, and MUST NOT pass it and MUST NOT fall back to issuedAt.

2.4 aee-p-posture-allowlist and aee-p-catchpolicy-pin — the two replay pins

The predicate grants these as a bare MAY with no mechanism:

Consumers MAY additionally coherence-check row claims against the pinned observationEnvironment: a substrate row claiming a network-boundary observation under a networkPosture that provides no interception path at that boundary is incoherent, and a consumer MAY reject on that ground.

This profile gives the permission a shape. A consumer MAY pin the set of networkPosture.posture tokens it accepts and the expected catchPolicy digest; where it pins either, a consumer MUST refuse a statement whose carried value is outside the pinned set or unequal to the pinned digest. Both are vacuous when absent, on the argument that nothing obliges a consumer to set either.

A consumer MUST NOT evaluate either pin inside its validity gate. These are private consumer values, and a rail that lets one of them decide a validity rule makes a specification-valid statement structurally unsound on that rail alone. Where such a rail is also the oracle for a conformance corpus, it hollows the corpus in the direction that only bites the consumers who followed the guidance. The reference rego rail does exactly this today, and appendix B records it.


3. Profile levels

A level is a named set of obligations, so that a consumer states what it meets instead of publishing a bespoke list nobody can compare.

LevelObligations
Minimalaee-p-tier-derivation, aee-p-no-tofu, aee-p-ignore-carried-tier, aee-p-corpus-anchor, aee-p-substrate-anchor, aee-p-threshold
RecommendedMinimal, plus aee-p-clean-row-live, aee-p-demanded-scope, aee-p-admission-result
StrictRecommended, plus aee-p-key-window-operand, aee-p-issuance-lag, aee-p-evidence-age, aee-p-posture-allowlist, aee-p-catchpolicy-pin

A consumer MUST NOT claim a level while declaring any obligation of that level unreachable or approximated. A rail that cannot derive a tier meets no level of this profile, and it says so under section 5 rather than claiming Minimal with a footnote. The levels exist so that a partial consumer has something honest to state, not so that it has something flattering to claim.


4. What a byte-pure consumer cannot know

This section is carried forward from the reference bundle's own account, unsoftened, because it is the most useful paragraph in it and a published profile that omitted it would be selling a gate rather than describing one.

A verifier that evaluates coverage validity without verifying the observation records' signatures cannot distinguish a substrate observation from an assembly-plane forgery. Every record field such a consumer reads comes out of a base64 payload whose signature nothing in its path checks. Anything a real substrate can write into a record, the party assembling the statement can write too.

Stated plainly: a consumer relying on a byte-pure admission controller is extending full trust to the envelope signer. The envelope key is the entirety of the trust decision there. The record-level cryptography is evidence for an offline auditor; it is not evidence such a gate can weigh.

Three byte-pure checks narrow that gap without closing it, and none of the three is a substitute for aee-p-tier-derivation:

  • Record signature presence. A band-aid, not verification, and its home is the predicate's validity gate rather than this profile. It needs no key, so it holds in every deployment, but the bytes are never inspected and an attacker who writes garbage into a signature passes exactly as a real substrate does. What it closes is the literal strip: the batch-root leaves span only the payload type and payload, so deleting every signature entry leaves the root unchanged and the statement otherwise intact.
  • aee-p-clean-row-live. The method-and-vantage half of the authority's own example. The tier half is not computable without a key, which is why section 1.6 forbids a keyless rail from relaxing its threshold.
  • Coverage validity. The predicate's own class-match family, and a validity rule rather than a consumer threshold. The authority is explicit about its reach: a verifier that evaluates coverage validity and skips signature verification has checked that the producer filled the form in correctly.

5. Declaring a capability gap

An engine without a signature primitive cannot derive an evidence tier. That is a fact about the engine, and this profile's position is that the fact is publishable rather than embarrassing — but only as a declaration.

A consumer that cannot satisfy an obligation MUST declare it unreachable with the engine obstruction stated, and MUST NOT omit it. The declaration lives in the rail map of section 6. Concretely, a rail declaring aee-p-tier-derivation unreachable is stating all of:

  • its admission result binds to validity plus whatever obligations it does enforce, and MUST NOT be published as, or conjoined into, a claim that any basis: substrate row is attested;
  • every field it reads out of a record payload is unverified, and the envelope key is the entirety of its trust decision;
  • it meets no level of this profile, per section 3;
  • it does not offer the pass_indirect relaxation, per section 1.6.

A consumer MAY satisfy an obligation out of band. Three shapes are conformant, and all three are declarations rather than omissions:

  1. An out-of-band verifier. A tier-capable verifier runs ahead of the policy engine and the engine consumes its admission result. The engine declares aee-p-tier-derivation enforced by reference and names the verifier and the key policy it ran under.
  2. A sidecar annotation. The tier column is computed elsewhere and attached to the object under decision. A consumer relying on a sidecar MUST establish the sidecar's integrity by a mechanism outside the attestation, because a tier annotation the same party can write is the carried evidenceTier that section 1.3 forbids reading, wearing a different hat.
  3. An explicit declaration that no tier is derived, with the reduced meaning of the admission result stated above.

6. Obligation identifiers and the rail map

Each rail — this project's and anyone else's — publishes a rail map declaring, for every slug in the registry, exactly one disposition:

  • enforced, with the rule, condition or code path that carries it;
  • approximated, with the approximation stated in both directions, so that a rail applying an obligation more widely than the profile scopes it declares a strengthening rather than leaving an undocumented divergence;
  • unreachable, with the engine obstruction stated.

A rail map MUST carry an entry for every registry slug, and MUST NOT carry an entry for a slug the registry does not define. A gate asserts the correspondence in both directions. The value of the both-directions form is measured rather than assumed: the reference bundle's own cross-rail driver already refuses both an undeclared disagreement and a stale declaration whose rail has since gained the power, and the second half is the half that rots.

The mapping runs one way only. Rails map into the slug, and a rail MUST NOT define a slug. An identifier a rail can mint is an identifier the registry does not control, and the ten engine-capability reasons the reference driver carries per vector are exactly the material that belongs on a per-obligation row instead — lifted out of a bilateral exception table, where they say "my engine agrees with one vendor's module modulo my own exception list", and put where they say what an engine can and cannot do.

Registry row fields: the slug; the profile section that defines it, named rather than numbered by line; the obligation sentence; the consumer input and its type; the absent-input default and which of the four absent-input arguments carries it — the authority's own default at section 1.5, the two-leg deny at 2.1, the third-leg deny at 2.2, or deliberate vacuity at 2.3; the declining input, or none; and the denial code emitted on violation, compared as a set. Four defaults with four different arguments, and a profile that stated a default without stating which argument carried it would have published a preference.


7. The reference consumer and the conformance suite

7.1 The reference consumer

probity-verify, in packages/verify, is this profile's reference consumer. It is the only implementation in the reference tree that derives an evidence tier, and it draws the valid-versus-admitted line this profile turns on: in bare replay mode exit 0 means the statement is valid; when any consumer policy is supplied the exit code binds to admitted, and never to bare validity.

It is a reference consumer and not a conforming one, and the difference is stated here rather than in a footnote. Measured at probity HEAD c2ebf615, it enforces aee-p-tier-derivation, aee-p-no-tofu, aee-p-ignore-carried-tier and aee-p-admission-result, and it implements the anchor comparison for both anchors while defaulting permissive when an anchor is not supplied — an unsupplied anchor compares nothing and the statement can still be admitted. That is the opposite of section 2.1, in the file whose own type documentation states that a consumer must supply both anchors for admission decisions. The obligation is written down beside the code that does not enforce it. Appendix B carries it as a defect.

The reference rego rail is the mirror image: default-deny on both anchors, and no tier at all. Neither reference implementation is a conforming consumer, and they fail on different obligations. That is the sharpest available argument that this document needed to be written: the boundary is not one an implementation holds unaided, even with the specification in front of it and the other implementation in the same repository.

7.2 The conformance suite

The suite is an extension of the published external-rail contract in aee-conformance/packaging/run_vectors.py, not a new harness and not a suite built on the reference rego rail.

Why not the rego rail. Its declaration keys are corpus vector identifiers and its ten reason families are engine-capability statements about three named engines. A third party's policy language is not on that list and cannot be added to it, so a suite hosted there would require an implementer to author its own exception table by running its engine against one vendor's module and transcribing whatever disagreed. The artifact that produces is a compatibility test between two implementations, not conformance to an obligation — and every defect in appendix B would become the correct answer by accident, so an implementer who followed this profile correctly would fail.

What the existing contract already gives. A fixed argv, a set-compared answer, and a consumer context injected by environment with a per-context expectation: each vector is run twice through the same command, once with a pinned test key policy and once with none, and the second run is what puts a rail under the no-TOFU rule. Argv is fixed by the contract, so naming a flag would dictate a spelling to every rail while naming a variable dictates only where to look.

What this profile adds to it. One response member, one context variable, one expectation cell:

  • the response object gains an admission verdict and a denials array, the array compared as a set exactly as the validity codes already are;
  • the consumer context travels in one environment variable beside the key policy, holding a path to a consumer context document;
  • each vector gains a per-context admission expectation.

The consumer context document is the profile's serialization of the inputs in section 1, and it exists so that a suite can inject a policy without dictating how a policy engine spells it. A conforming context document MUST name its members substrateObservationKeys, expectedCorpusDigest, expectedSubstrateDigest, acceptedResults, admitUninterceptedCleanRows, demandedClasses, maxIssuanceLagHours, maxEvidenceAgeHours, allowedNetworkPostures, expectedCatchPolicyDigest and keyValidityWindow, and MUST NOT require a rail to use those names anywhere else. The serialization is a wire between the suite and a rail, and a profile that let it leak into the rail's own configuration would be dictating a spelling.

Verdicts are three-valued from the first line. admit, deny, and did-not-run. A suite MUST report a rail invocation that crashed, timed out, or emitted an unparseable answer as did-not-run, and MUST NOT record it as a denial. This is not a hypothetical defensiveness: the reference cross-rail driver's oracle returns a denial when its engine process fails for any reason, indistinguishable from a genuine one, and a vector the rail under test also denies produces zero disagreement and is reported as agreement. A negative is reportable only when the check demonstrably ran.

Corpus pin. The coverage figures below were measured against the conformance corpus this repository vendors at packages/verify/vectors/aee-v06/ — release v0.8.0, source commit 0c4e27ec4f28b03d9688885bb5a00c6e96334d1e, manifest digest eb24532be577ee6a936410e6850482847cecac07147aa3c76d737fc730d4bf2b — which carries 272 vectors: 61 accept, 209 reject, 2 indeterminate. scripts/lint_failure_codes.py holds this declaration to the vendored copy on all three axes and refuses to grade anything when they disagree, and it counts the figures in the next paragraph out of that same corpus rather than reading them here.

The honest state of the coverage this suite starts from. No expectation in it is an admission expectation. The manifest's expectation shape has no member naming an admission outcome at all, which is the structural reason nothing this profile adds is measurable from the corpus yet, and it is the same absence that leaves the four anchor codes of section 1.4 with zero forcing vectors — a zero recomputed from the manifest on every run rather than asserted here. 6 vectors carry a per-context evidence-tier expectation, tierWithPinnedKey beside tierWithoutKey, and in 4 of them the two contexts differ. That four, not the six, is the forcing coverage of section 1.1's mandatory obligation. A vector whose two contexts agree is satisfied by a consumer that never consults its key policy at all, so it cannot distinguish a consumer that derives the tier from one that does not; only a vector that answers attested under a pinned key and unattested without it can. Four out of 272, and none at all for section 1.4: the mechanism the suite needs is proven and its coverage is a token. The one carrying condition aee-c-33 — that the evidence tier is derived per row and never carried — is the only vector in the corpus declaring it, and it is the one that exercises the obligation across rows of mixed basis. The profile's first release ships the vectors, and until it does no claim of conformance to this profile is measurable.


8. Non-claims, and what this profile does not cover

8.1 What a conforming consumer has and has not established

A consumer at Strict has established that a valid statement about the corpus and substrate it pinned, covering the classes it demanded, carries a result at or above its threshold, with every substrate row attested under keys it pinned, inside the freshness bounds it set. It has not established that the run happened. The substrate is a passive sensor with no view of the runner that injects the corpus, so the set of attacks actually executed is a producer assertion, and no consumer obligation in this profile reaches it.

It has not established that no attack was quietly withdrawn from scope. aee-p-demanded-scope reaches a withdrawal that leaves the demanded class out of coverage.assessedClasses; it does not reach a corpus whose manifest never declared the class, and that is the form that reaches a passing verdict.

It has not established that a caught row was not relabelled clean. The citing form of that rewrite — a relabelled row still pointing at the interception record its caught predecessor cited — is closed by a validity rule this profile deliberately does not carry (section 0.2). The deleting form is open, and it is open against every rail today.

8.2 What this document deliberately does not cover

  • Any byte-pure rule. Statement well-formedness, coverage validity, the result recompute, digest integrity, and the three rules named in section 0.2. All of them belong to the predicate and none of them is trust-relative.
  • The envelope trust decision. Which key signs the outer envelope, how it is distributed, rotated and revoked, and what a consumer does on revocation. That is the attestation framework's, and for a byte-pure consumer it is the entirety of the trust decision — which makes its absence here a gap in the reader's coverage, not a claim that it does not matter.
  • Substrate observation key distribution. This profile requires a consumer to hold pinned keys and says nothing about how it gets them.
  • Tier refinements. The predicate permits a consumer policy to subdivide attested into stricter refinements — a hardware-attested observation key, agreement of multiple keys — and this profile standardizes none of them. A refinement refines and never reorders the three tiers, and a consumer using one declares it.
  • Producing evidence. Nothing here constrains a producer.
  • Any predicate other than adversarial-execution-evidence. A result-bearing sibling predicate carried in the same bundle is out of scope, and a rail evaluating both states which obligations it applies to which.
  • Additional levels. Three are defined. A deployment needing a fourth publishes a rail map, not a level.
  • The conformance vectors themselves. Section 7.2 designs the suite; it does not ship it.

8.3 An obligation not published here is not a conformance requirement

Two obligations were considered for this release and are not in it, and they are named so that a reader can tell a deliberate exclusion from an oversight. A consumer requirement that every row declare a hash-pinned rather than time-window attribution has no member to read until the predicate revision that introduces one, and stating it now would be a requirement about a field that does not exist. A requirement that a consumer bound the number of records a statement may carry has no argument behind it yet.

This profile makes no claim to completeness. It covers the consumer surface of one shipped implementation, audited against one specification revision, and an obligation nobody has written down cannot be one this profile enforces. A document that could not fail would be worth nothing, and enumerating what it does not reach is what keeps it able to.


Appendix A — the reference rails

The rego module in deploy/admission/rego/probity_admission.rego is an implementation at a stated set of dispositions, and it is not the definition of this profile. Two facts about it must travel with any conformance claim made on it.

The cross-rail conformance driver does not execute the deployment artifact the bundle recommends. It loads the three Kyverno documents and one CUE file; the deep policy-controller policy that embeds the full module — the one the install instructions recommend — is kept in sync by a generator and a byte-equality check, which is not an execution.

A correctly pinned deployment fails that driver by construction. The driver asserts that each policy carries exactly two unreplaced anchor placeholders, so an operator who follows the documented first install step and replaces both turns their own policy files red. The check is correct as a repository drift guard and fatal to any "run this against your own deployment" reading of it.

Appendix B — known defects in the reference implementations

Recorded so that a reader who goes to the source is not misled. Every one is either fixed or disclosed before a conformance claim cites these rails.

  1. The rego rail derives no evidence tier, and credits basis: substrate rows without one. It is a non-conformant consumer of its own predicate, and until 2026-07-30 neither the module nor its documentation said so — the capability gap was stated honestly and its conformance consequence was not.
  2. The rego rail offers the pass_indirect relaxation while unable to derive a tier, which section 1.6 forbids. The 2026-07-30 separation of the threshold from the row gate closed the basis-and-method half of that sentence and left this half open.
  3. probity-verify defaults permissive on both anchors, against section 2.1, in a file whose own documentation states the obligation it does not enforce.
  4. Two consumer pins are evaluated inside the rego rail's validity gate. The catch-policy and posture pins are conjuncts of the module's soundness rule rather than of its admission rule. Latent today because both default permissive, and live for exactly the consumers who follow the guidance and pin them.
  5. subject cardinality is enforced nowhere in the rego rail, and two corpus vectors are denylisted under two reasons that are each false and that contradict each other.
  6. The cross-rail driver's oracle is two-valued, recording a crashed or unparseable engine invocation as a denial.
  7. The reference bundle's prose carries stale figures throughout — vector counts, test counts, rail-comparison tables, condition ordinals, and a worked command whose sample statement predates a run-binding version change and now fails. No figure in it should be quoted, and this profile quotes none.
Get access

Run your agents through Probity.

A few details about your setup and we'll get you gating agents fast.