Skip to main content

WAI Extension: Contribution Records

Mirrored from the canonical text at commit b1681667 ().

Status: Draft. Registers the record wai.media.contribution: who contributed what to a piece of media made by people and models together, and how much, computed from the process that made it. A tool signs a session naming its parties: people, each by a key and each countersigning its own membership, and models, each by its digest. Every act in the session is a signed step in a hash chain: a person authors or edits samples, a model generates, proposes candidates or inpaints a marked region, a party selects one candidate, transforms a state with a pinned derivation, composes layers, or rejects an output. Each act has a dependency rule, and re-executing the steps gives, for every sample of the output, the parties whose act produced its value (made_by) and the parties whose choices conditioned it (directed_by): the authorship map, computed rather than declared. A verifier checks what the steps say: that an edit or an inpainting changed no sample outside its region, that a selected output is a member of the set it was chosen from and that each candidate regenerates from its seed, that a transform recomputes, that the map recomputes, and that the output’s origin claim (synthesis) is at least what the map implies. The steps are the leaves of a Merkle tree, so a creator can disclose some of them with inclusion proofs; a prompt is a salted commitment until its creator opens it; and the outcome says which tier the disclosure reaches: recomputed, checked or stated. A sample whose label no disclosed, pinned rule fixes is unknown or undisclosed, never attributed to anyone, and the shares are sample counts per party, never one blended figure. No new transport. The reference implementation is the contribution module of wai-rs (feature contribution); the corpus is live-conformance/contribution/, and its second implementation is live-conformance/contribution_verify.py. Keywords MUST, MUST NOT, SHOULD, MAY are RFC 2119/8174.

Texts read for this revision, each pinned to the version named: the C2PA Technical Specification, version 2.4 (April 2026), §10.2.3.2, §18.2, §18.15.1, §18.15.2 and §18.15.4; the Creator Assertions Working Group’s Identity Assertion, version 1.2 (ratified 15 December 2025), §5.1, §5.1.2, §6.1 and §8; the IPTC Digital Source Type vocabulary as released on 2024-10-23; arXiv 2609.07711, §3.4; and Copyright and Artificial Intelligence, Part 2: Copyrightability, a report of the United States Copyright Office (January 2025), its table of contents and its Part V. A section number of one of those texts in this extension is that version’s.

1. Scope

Process-level provenance that captures who contributed what, and to what extent, within a co-creation session and across sessions, is named as an open research direction in arXiv 2609.07711 §3.4. WAI’s derivations are exact, so a record of the process can be re-executed to the artifact, and the map of who made each sample can be computed from it.

In scope: the records (§2) and the session with its parties (§3); the steps, the acts and their pinned operations (§4); the chain of steps and its Merkle tree (§5); the contribution record (§6); the authorship map, its dependency rules, its encoding and its shares (§7); disclosure and the privacy of process (§8); agreement with an origin claim and the mappings onto C2PA actions, identity assertions and live tracks (§9); verification (§10).

This revision maps still images of RGB8 samples in full. Audio maps by sample interval and video maps by frame are not part of it: a session’s media is image (§3.1), and a later revision registers the other media with their canonical buffers.

Out of scope, each a slot a later revision fills:

Relationship to the core spec: wai.media.contribution is a record (SPEC §5): signed records a WAI tool emits and verifies. It names no payload format and is not dispatchable.

Relationship to synthesis: an origin claim says what an object is; a contribution record says how it was made and by whom, sample by sample. The record names the output’s origin claim, and a verifier holds the two to each other (§9.1). This extension takes synthesis §2’s encodings and its two integer derivations by name.

Relationship to deriving-edge: a transform or a composition of this extension evaluates one of that extension’s operations (§4.2 there) by its name, its parameters and its arithmetic, over states of a session rather than over delivered objects, with no envelope written. A deriving edge’s record chains published objects; a session’s steps chain the states of one piece of work.

2. Records

Every record of this extension is a JSON object (RFC 8259) under the encodings of jwp-receipts §2.4.3, read as synthesis §2 reads a record: strict JSON, no member named twice, at most 32 levels of nesting, byte strings in lowercase hexadecimal, integers unsigned and at most 2^53 − 1, no other number, no boolean, terms and date-times as defined there. Each record has exactly the members its section lists, no member missing and none added, at any level, and a kind naming it; one that does not is record_malformed. So is a member outside the values or ranges its section gives.

A signed record carries signer_pubkey (32 bytes, an Ed25519 key of RFC 8032) and sig (64 bytes):

sig       = Ed25519(signer_key, "wai:contribution\x01" || JCS(record without "sig"))
link_hash = BLAKE3("wai:contribution-link\x01" || JCS(record))

JCS is RFC 8785. The signature is verified strictly (SPEC §7 Signatures): a key that fails step 1 or 2 is bad_key, and a signature that fails a later step bad_signature. A record is named by its link hash wherever another names it. issued_at holds the instant its signer signed it, self-asserted.

A state is a picture a step produces: width × height pixels as the RGB8 buffer of reconstruction-binding §2 (row-major, three bytes a pixel, no row padding), each side at least 1 and the buffer at most 2^26 bytes. Its samples digest is SHA-256 (FIPS 180-4) over the buffer, and a state is named by it and by the step that produced it. A pixel is addressed (x, y), x to the right and y down from the top-left. A sample of the map is a pixel.

3. The session

3.1 The session record

The tool a session runs in signs a session record, kind "wai-session":

membertypemeaning
kindstring"wai-session"
session32 B hexfresh random bytes naming the session
tooltermthe tool, as a C2PA softwareAgent names it (§9.2)
mediastringimage
partiesarray1 to 64 parties, each named by its position from 0
issued_atdate-timewhen the tool signed it
signer_pubkey, sig§2: the tool’s key

A party is one of:

A human or an audience party is a keyed party. No key appears twice, no digest twice, and no keyed party’s key is the tool’s (record_malformed), so the tool’s signature never stands for a person’s.

3.2 Membership

A keyed party countersigns its membership: it signs a membership record, kind "wai-membership", over the session as the tool signed it:

membertypemeaning
kindstring"wai-membership"
session32 B hexthe link hash of the session record
partyintegerits own position among the parties
issued_atdate-timewhen it signed it
signer_pubkey, sig§2: the party’s key

A membership whose session is not the session’s link hash, whose party is not a keyed party, or whose signer is not that party’s key is membership_invalid. A keyed party with no membership is party_not_countersigned: no person is enrolled as a party of a session, and so as a maker or a director of any sample (§7), without its own signature over the session’s parties. A model party signs nothing; the tool signs its acts (§4.1).

4. Steps

4.1 The step record

Each act is a step, kind "wai-step":

membertypemeaning
kindstring"wai-step"
session32 B hexthe link hash of the session record
seqintegerthe step’s position in the session, from 0
prev32 B hex or nullthe link hash of step seq − 1; null for step 0
partyintegerthe party that performed the act (the performer)
actstring§4.2
inputsarray0 to 16 states, each {"step", "samples"}: the seq of the step that produced it, below this step’s seq, and its samples digest; none twice
outputobject or nullwhat the act produced (§4.2)
regionarray or nullfor edit and inpaint, 1 to 256 rectangles {"x", "y", "w", "h"}, w and h at least 1, each within the output (x + w ≤ width, y + h ≤ height); the region is their union. null for every other act
paramsobject§4.3
checkstringreplicate or attest (§4.4)
issued_atdate-timewhen it was signed
signer_pubkey, sig§2: the performer’s key for a keyed performer, the tool’s key for a model

An output is {"samples", "width", "height"}, one state; for candidates it is {"set", "width", "height"}, a set of 2 to 16 states of one size by their samples digests, none twice; for reject it is null.

A step whose session is not the session’s link hash is session_mismatch; whose party, or a party its params name, is not a party of the session, party_unknown; whose performer’s role is not one its act admits (§4.2), party_role_mismatch; and whose signer is not the one this section names, signer_mismatch.

4.2 Acts

actperformerinputsoutputregionwhat it introduces
authorhumannoneone statenoneevery sample: the person’s own
generatemodelnoneone statenoneevery sample
candidatesmodelnonea setnoneevery sample of every member
selectany partyone: a member of a candidates step’s setone state: the inputnonenothing
edithumanoneone state of the input’s sizerequiredthe region’s samples: the person’s own
inpaintmodeloneone state of the input’s sizerequiredthe region’s samples
transformany partyoneone statenonenothing
composeany party1 to 16one statenonethe canvas fill (§4.5)
rejectany partyonenonenonenothing
directreserved

A step that breaks the table’s inputs, output or region is record_malformed; an input of a select that is not a member of a candidates step’s set, or a select whose output is not its input, is selection_not_in_set (§10.2 step 6).

4.3 Parameters

A step’s params has exactly these members:

actparams
author{"source"}: what the person made, as synthesis §3.2 has a capture state it: digitalCapture, computationalCapture, screenCapture or digitalCreation
generate{"generator", "prompt", "prompt_by", "seed", "steps"}
candidates{"generator", "prompt", "prompt_by", "seeds", "steps"}
inpaint{"generator", "prompt", "prompt_by", "mask_by", "seed", "steps"}
select, edit, reject{}
transform, compose{"derivation", "params"}: a term naming the derivation, and an object, its parameters

4.4 The check

A step’s check says whether every sample it carries forward is fixed by a pinned rule:

actcheck
author, edit, select, rejectreplicate
generate, candidates, inpaintreplicate when generator is wai.generate.spectral; attest otherwise
transformreplicate when derivation is one §4.5 pins for it; attest otherwise
composereplicate when derivation is wai.derive.overlay_composition or wai.derive.grid_composition; attest otherwise

A generator another extension pins is not wai.generate.spectral: a step of influence’s conditioned generator, wai-influence:spectral-blend, is attest here, and that extension regenerates it (influence §4.4). A step whose check is not its act’s is check_mismatch. A replicate step’s parameters are those §4.5 gives its operation; a generate, candidates or inpaint step of wai.generate.spectral has a seed (each of its seeds) from 0 to 2^32 − 1 and steps from 1 to 64, and its output’s sides are each at most 4096 with width · height at most 2^23, the ranges SPEC §5 Payload of wai.generate.spectral gives an instruction object. Otherwise params_invalid. An attest step’s params under derivation is any object.

4.5 The pinned operations

Each operation computes its output from its inputs’ samples and its parameters, and defines, for each output pixel, the input pixels it reads: its sample mapping, which the map follows (§7.2).

Transforms:

Compositions, over the step’s inputs in its order:

Generation, for generate, candidates and inpaint:

A parameter the inputs cannot satisfy (a crop outside its input, grid inputs of different sizes, an at entry missing) is params_invalid when the inputs’ sizes are known (§10.2 step 6).

5. The chain and its tree

5.1 The chain

A session’s steps are numbered from 0 by seq, and each names the link hash of the one before it in prev: a hash chain, so no step can be removed, inserted or reordered without breaking every link after it. A step whose seq is not below the record’s steps (§6), a seq given twice, a step 0 whose prev is not null, a later step whose prev is null, or a prev that is not the link hash of step seq − 1 where the verifier holds that step, is chain_broken.

5.2 The tree

The steps are the leaves of a binary Merkle tree, built as jwp-receipts §2.2 builds a group’s, over the steps’ link hashes in seq order, with domains of this extension’s own so that the root of a session’s steps can never be read as a group’s:

leaf(step)        = BLAKE3("wai:contribution-leaf\x01" || link_hash(step))
node(left, right) = BLAKE3("wai:contribution-node\x01" || left || right)

At each level with an odd number of nodes the last is duplicated; the root is the single node left. The duplicated last leaf also walks to the root from the position past it, so the record states the number of leaves, steps, and §5.1 refuses a seq that is not below it: no step can stand at a position the tree does not have.

A disclosed step carries an inclusion proof: its siblings from the leaf level upward, exactly ⌈log₂ steps⌉ of them (none for one step). It is walked as jwp-receipts §3 step 5 walks an object’s proof, with the leaf index the step’s seq and the direction bits the bits of seq: at level i, bit i of seq set means the current node is the right child. A proof of another length, or one that does not reach the record’s root, is inclusion_failed.

6. The contribution record

The tool signs the contribution record, kind "wai-contribution":

membertypemeaning
kindstring"wai-contribution"
session32 B hexthe link hash of the session record
stepsintegerthe number of steps, at least 1
root32 B hexthe root of the steps’ tree (§5.2)
outputobjectthe final output: {"step", "samples", "width", "height", "content_hash"}, the seq of the step that produced it (below steps), its samples digest and size, and the content hash (jwp-receipts §2.1) of the object it was published as, or null
map32 B hexthe digest of the authorship map (§7.3)
origin32 B hex or nullthe link hash of the output’s origin claim (synthesis §3.1); null only with content_hash null too, or where the tool signs none
estimatesarray0 to 16 estimated shares (below)
issued_atdate-timewhen the tool signed it
signer_pubkey, sig§2: the tool’s key

A record that is not the session’s is session_mismatch; one not signed by the tool’s key, signer_mismatch; one whose origin is not null while content_hash is, record_malformed.

An estimate is {"estimator", "class", "party", "basis", "share_ppm"}: a term naming the method, a term naming its class (the family of methods it belongs to, such as an information-theoretic one), a party (party_unknown otherwise), made_by or directed_by, and the share it estimates in parts per million, at most 1 000 000. An estimate is the tool’s statement with its class, as an energy figure carries its class: a verifier reports it as stated, beside the counts of §7.4 and never in place of them, and never adds it to them.

7. The authorship map

7.1 Labels

Every sample of the output has a label: two sets, made_by, the parties whose act produced the sample’s value, and directed_by, the parties whose choices conditioned it (a prompt, a mask, a selection, a transform’s parameters, an arrangement). A set holds parties, and may hold one of two markers:

A marker is never a party and is never counted as one: unknown is never zero. A sample a rule cannot attribute is reported as such, not credited to anyone.

7.2 Dependency rules

The map of a state is computed from its step and its inputs’ maps. The map of an input whose step the verifier does not hold has every sample ({undisclosed}, {undisclosed}). For a step performed by party a:

actlabel of each sample of the output
author({a}, {})
generate; each member of candidates({a}, P), P the set of prompt_by, where it is not null
editinside the region ({a}, {}); outside it, the input’s label at the same pixel
inpaintinside the region ({a}, P ∪ {mask_by}); outside it, the input’s label at the same pixel
selectthe input’s label at the same pixel, with a added to directed_by
transform, replicatethe union, over the input pixels its sample mapping reads (§4.5), of their made_by, and of their directed_by with a added
compose, replicateat a pixel an input covers, that input pixel’s label with a added to directed_by; at a canvas pixel no input covers, ({a}, {a}): the fill is the arranger’s
transform or compose, attest({unknown}, {unknown})
rejectno output

So a geometric transform carries each label through its exact sample mapping; a filter that mixes pixels takes the union of what it mixes; a value transform keeps made_by and adds its chooser to directed_by; a selection keeps the chosen candidate’s map and adds the selector everywhere; a composition takes each layer’s map where the layer is visible. A model’s samples, generated or inpainted, are made_by that model whether or not its generator is pinned: the act places them, and the step’s status (§10.3) says whether they were regenerated. An edit’s or an inpaint’s region takes nothing from the samples it replaced: they no longer condition it.

Where the verifier does not know an input’s size (its step is not held), the sample mapping cannot be followed; a transform or compose then gives every output sample ({undisclosed}, {undisclosed, a}), and a select gives the input’s label as above. The output’s map is the map of the state the record’s output names: the one its step produced.

7.3 Encoding

The map is a JSON object under §2’s encodings, kind "wai-authorship-map", unsigned and bound by its digest:

membertypemeaning
kindstring"wai-authorship-map"
output32 B hexthe samples digest of the output
width, heightintegerthe output’s size
palettearraythe distinct labels, each {"made_by", "directed_by"}, in the order each first occurs in row-major order
rowsarrayheight rows, each an array of runs [i, n]: n ≥ 1 pixels with palette label i, left to right, summing to width; adjacent runs never share a label

A set is an array of party positions in ascending order, then "unknown" where it holds that marker; a stated map holds no "undisclosed". Every party is a party of the session. A map that is not one JSON text under §2’s encodings, or that breaks any of these rules, is map_malformed: each map has one encoding.

map digest = BLAKE3("wai:contribution-map\x01" || JCS(map))

The record names the map by its digest, and the map names the output’s samples digest and size, so the map is bound to the samples it describes. A map whose digest is not the record’s map, or whose output, width or height is not the record’s output’s, is map_unbound.

7.4 Shares

The shares of a map are exact sample counts: the number of samples; for each party, the number of samples whose made_by holds it, and separately the number whose directed_by holds it; and, for each of the two sets, the number of samples holding unknown and the number holding undisclosed. A sample counts once for each party its set holds, so a sample two parties made counts for both. The counts are never combined into one figure: what made_by and directed_by mean is a reader’s to weigh under its own rule.

8. Disclosure

8.1 Salted commitments

A prompt, and any other text a party gives a step, is carried as a salted commitment:

commitment = SHA-256(salt || UTF-8(text))

with salt 32 fresh random bytes for each commitment. A short or guessable prompt cannot be recovered by hashing candidates: the salt is not disclosed with the step. An opening {"commitment", "salt", "text"} discloses one: the salt (32 bytes) and the text (a string). An opening whose salt and text do not hash to its commitment is opening_mismatch.

8.2 What each disclosure gives

A creator discloses some or all of the steps, each with its inclusion proof, and some or all of the states, the openings of the prompts and the model files a pinned generator reads. The tier of the outcome (§10.3) follows from what is disclosed:

disclosedtier
every step behind the output, every state they read and write, the openings of their prompts and the model files of their pinned generators, no attest step behind the outputrecomputed
as above with an attest step behind the output, or with a state, an opening or a model file withheldchecked: what was held was checked, the rest is committed
some steps, with their proofs: the others committed by the rootchecked, and the samples from undisclosed steps undisclosed
the record and the map, no stepstated: the signed map is held, nothing recomputed

A withheld prompt stays private: its commitment binds the step to it, and the step’s map does not depend on its text. A withheld step stays private too: its leaf is in the root, so it cannot be replaced later, and the samples it produced are undisclosed, never credited to anyone.

9. Agreement and mappings

9.1 The origin claim

Where the record names an origin claim (synthesis §3.1) the verifier holds, read by synthesis §2 and §3.1:

9.2 The C2PA actions

Where every step is disclosed, the record gives one C2PA statement, {"actions": A}, written in JCS: A is the actions of the output’s manifest (C2PA 2.4 §18.15.1), one per step behind the output, in seq order, after a first c2pa.created action:

stepactiondigitalSourceType (the IPTC term of)changes
authorc2pa.editedits sourcenone
generate, candidatesc2pa.editedtrainedAlgorithmicMedia for a trained model, algorithmicMedia otherwisenone
inpaintc2pa.editedcompositeWithTrainedAlgorithmicMedia for a trained model, algorithmicMedia otherwisethe region
editc2pa.drawingdigitalCreationthe region
transform: cropc2pa.croppednonenone
transform: rotation, mirrorc2pa.orientationnonenone
transform: upscale2c2pa.resizedalgorithmicallyEnhancednone
transform: denoise3c2pa.enhancedalgorithmicallyEnhancednone
transform: lutc2pa.adjustedColornonenone
transform: wai.derive.scalingc2pa.resizednonenone
another transform, a composec2pa.editednonenone
selectnone: it changes no sample

An action’s members are written in the order JCS gives them. changes (C2PA 2.4 §18.15.4.6) is one region map (§18.2): [{"region": R}], R one spatial range for each rectangle, in the step’s order, {"type": "spatial", "shape": {"type": "rectangle", "unit": "pixel", "origin": {"x": X, "y": Y}, "width": W, "height": H}}, the coordinates as integers (a manifest in CBOR writes them as the floats §18.2.3 types them). The IPTC term is http://cv.iptc.org/newscodes/digitalsourcetype/ followed by the code. The trained models’ c2pa.ai-disclosure assertions are the origin claim’s (synthesis §6). A rejected state, and a branch of the session the output does not come from, give no action: they did not make the asset. Embedding the statement in a manifest, and signing it, is C2PA’s.

9.3 Identity assertions (informative)

A keyed party may give the output’s manifest an identity assertion (Identity Assertion 1.2 §5.1, label cawg.identity) whose referenced assertions include the actions assertion of §9.2, as §6.1 there recommends for an actor who performed those actions, under a credential of §8 there. The roles of §5.1.2 there (cawg.creator, cawg.contributor, cawg.editor, …) are the party’s own statement of its relationship to the asset; this extension does not derive one from the shares. The party’s key in the session is an Ed25519 key, not a credential type of §8 there, and the relation between the two is the party’s to state.

9.4 Live tracks (informative)

A record covers one still. Over live-provenance, a manifest that covers such a still (a picture a room shares, a frame made in a session and then streamed) can name the record by its link hash in an entity-specific assertion of the kind that extension’s §5 defines for the stream, the group and the take; the record, its session, its memberships, its disclosed steps and its map are then offered by the carriage that offers the track’s receipts, as deriving-edge §6 offers a chain’s records. Per-frame maps of a live track are a later revision’s.

10. Verification

10.1 Inputs

A verifier is given: the session record; its memberships, in a list; the contribution record; the disclosed steps, in a list, each with its inclusion proof; the authorship map; the openings it holds, in a list; the states it holds, each a buffer, named by its samples digest; the model files it holds, each named by its SHA-256; the origin claims it holds, in a list; and a policy of one member:

membervaluesdefaultmeaning
tierstated, checked, recomputedstatedthe lowest tier it accepts (tier_not_met)

10.2 Steps of verification

The verifier runs these steps and stops at the first refusal, which it reports with its reason and its place: ["session"], ["session", "party", i], ["membership", i], ["record"], ["step", i], ["map"], ["map", x, y], ["opening", i], ["origin", i] or ["policy"], each i a position in its list (a party’s among the parties) and (x, y) a pixel.

  1. Reading (§2, §3, §4.1–§4.3, §6, §7.3, §8.1): the session, the memberships, the record, the steps, the map and the openings, each in order, read and their signatures verify; the origin claims read and verify by synthesis §2 and §3.1. record_malformed, bad_key, bad_signature, act_reserved, map_malformed. A step whose act is direct is act_reserved once its members are read, whatever they hold. A proof that is not an array of 32-byte strings is record_malformed.
  2. Openings (§8.1): opening_mismatch.
  3. Membership (§3.2): each membership in order (membership_invalid), then each keyed party in order (party_not_countersigned).
  4. The record (§6): session_mismatch, signer_mismatch, and each estimate’s party (party_unknown).
  5. Each step, in the order given: session_mismatch, party_unknown, party_role_mismatch, signer_mismatch (§4.1); check_mismatch and params_invalid (§4.4); chain_broken (§5.1); inclusion_failed (§5.2).
  6. Each step against the states, in seq order, each by these checks in order:
    1. each state its output names that the verifier holds is width · height · 3 bytes (samples_mismatch);
    2. each input whose step the verifier holds is that step’s output, or a member of its set (input_unknown); for a select, instead: its output is its input, and where the verifier holds the input’s step, that step is a candidates step and the input is a member of its set (selection_not_in_set);
    3. for an edit or an inpaint whose input’s step is held, that step’s output has this step’s output’s size (region_invalid);
    4. for a replicate transform or compose whose inputs’ sizes are known (the verifier holds each input’s step): its parameters hold over them (§4.5, params_invalid), and the size they give is the output’s (recompute_mismatch);
    5. the act’s own check, where the verifier holds what it reads:
      • edit, inpaint: a held buffer of the input that is not of the output’s size is region_invalid; with the input’s and the output’s samples held, every pixel outside the region is the input’s, byte for byte (outside_region_changed);
      • generate, candidates, replicate: with the model file and the prompt’s opening (where prompt is not null) held, each state regenerates (§4.5): the samples digest of what the generator makes is the state’s (regeneration_mismatch);
      • inpaint, replicate: with the model file, the prompt’s opening and the output’s samples held, every pixel inside the region is the generator’s picture’s (regeneration_mismatch);
      • transform, compose, replicate: with every input’s samples held, the operation evaluated over them gives the output’s size and samples whose digest is the output’s (recompute_mismatch).
  7. The output (§6): where the verifier holds the step output.step, that step’s output is one state with the record’s samples digest and size (output_mismatch); a held buffer of the output’s digest is width · height · 3 bytes (samples_mismatch).
  8. The map (§7): the map is bound (map_unbound). The verifier computes the output’s map by §7.2 from the steps it holds and compares it with the stated map, pixel by pixel in row-major order, made_by then directed_by. Where the computed set holds undisclosed, the parties and the unknown it holds besides are in the stated set; otherwise the two sets are equal. At the first pixel where that fails: unknown_attributed where the computed set holds unknown and the stated set does not (a sample no pinned rule fixes, credited to a party); otherwise map_mismatch.
  9. The origin claim (§9.1): origin_mismatch, origin_understated, at the claim’s place.
  10. Policy: an outcome below the policy’s tier is tier_not_met.

10.3 Statuses and tiers

Each step the verifier holds has a status:

The steps behind the output are the step output.step and, recursively, the step of each input of a step behind the output. The outcome’s tier:

10.4 What an accepted case states

11. Reasons

The codes this extension adds, each with where it is raised:

reasonraised at
record_malformed§2, §3.1, §4.1, §4.2, §4.3, §6, §8.1, §10.2 step 1
bad_key§2
bad_signature§2
act_reserved§4.2
map_malformed§7.3
opening_mismatch§8.1
membership_invalid§3.2
party_not_countersigned§3.2
session_mismatch§4.1, §6
party_unknown§4.1, §6
party_role_mismatch§4.1
signer_mismatch§4.1, §6
check_mismatch§4.4
params_invalid§4.4, §4.5, §10.2 step 6
chain_broken§5.1
inclusion_failed§5.2
samples_mismatch§10.2 steps 6 and 7
input_unknown§10.2 step 6
selection_not_in_set§4.2, §10.2 step 6
region_invalid§10.2 step 6
outside_region_changed§10.2 step 6
regeneration_mismatch§4.5, §10.2 step 6
recompute_mismatch§4.5, §10.2 step 6
output_mismatch§10.2 step 7
map_unbound§7.3
map_mismatch§10.2 step 8
unknown_attributed§7.1, §10.2 step 8
origin_mismatch§9.1
origin_understated§9.1
tier_not_met§10.1

12. Conformance

live-conformance/contribution/ holds the corpus, in one file, vectors.json, written from fixed seeds by wai_contribution_vectors gen. It holds the model file of wai.generate.spectral its sessions generate with, and the vectors: each a case of §10.1, its outcome and, accepted, everything §10.4 states; refused, its reason and its place. The exact generator of the corpus is wai.generate.spectral, with the model file of its conformance vector (wai-rs/tests/vectors/generate/generate_spectral_model.json). It has:

A conforming verifier gives every vector the outcome, the reason, the place and the statements the corpus states. wai_contribution_vectors verify and the test tests/contribution.rs check that the file regenerates byte for byte, that wai-rs gives every vector its outcome, and that §11 lists exactly the reference’s codes. live-conformance/contribution_verify.py is the second implementation, written from this text: it shares no code with wai-rs; it computes every map itself, evaluates every pinned operation and runs the generator in its own arithmetic, checking that arithmetic first against the generator’s conformance vector (wai-rs/tests/vectors/generate/sha256.json); it reads origin claims with the synthesis corpus’s second implementation (synthesis_verify.py), verifies every signature with WAI’s strict rule (claim-conformance/_ed25519.py) and every link hash with BLAKE3 written from its specification (claim-conformance/_blake3.py); it mutates copies of the corpus and requires every copy to fail; and it drops each of its own checks in turn and requires the corpus to catch the weakened verifier.

13. Security and privacy considerations