Skip to main content

WAI Extension: Deriving Edge

Mirrored from the canonical text at commit b1681667 ().

Status: Draft. Registers the record wai.edge.derivation: an edge that derives new objects from objects a publisher delivered, publishes each one under receipts of its own, and signs a derivation record that chains it to its source. An edge may evaluate a wai.derive.* operation of derived-tracks over decoded samples, rebuild an object at a lower rung for a sink that lacks the capability it was coded in (decode a wai.video.int_motion clip and write a frame of it as a wai.image.png still), or re-package an object without re-encoding it (explode a WAI2 component into its WAI1 envelope). The record names each input by its content hash and the delivery receipt that carries it, and by the record that derived it where another edge did; the derivation and its parameters; the output by its content hash and receipt; and the energy the derivation took, with its acquisition class. Its check says how a verifier holds the edge to it: replicate, when every step is pinned, so any verifier holding the inputs re-executes the derivation and compares the output byte for byte; or attest, when a step is not (a lossy classical encode, a resampling), so the output rests on the edge’s signature. A sink verifies its object back to its source, hop by hop, and its policy may refuse attested hops. Each record carries forward, by digest, every provenance item its inputs had — the claims about them, the C2PA Manifest Stores bound to or embedded in them, and what their own records carry — in a system or a personal compartment, and an AI-origin label that no hop may lower; a hop that omits an item or lowers a label is refused. Relays still forward every envelope and receipt unaltered (jwp-receipts §4): an edge alters nothing it received; what it derives is a new object of its own. No new transport. The reference implementation is the derive_edge module of wai-rs (feature derive_edge) and its reference edge, wai_edge; the corpus is edge-conformance/, and its second implementation is edge-conformance/edge_verify.py. Keywords MUST, MUST NOT, SHOULD, MAY are RFC 2119/8174.

Texts read for this revision: the WAI texts this one cites; RFC 8032 and RFC 8785; California Assembly Bill 853 (Statutes of 2025, Chapter 674), as chaptered, for Business and Professions Code §22757.1 (n), (o) and (p) and §22757.3.1; and the C2PA Technical Specification, version 2.4, §A.3.2 (a manifest store in PNG) and §18.15 (actions). The operations’ names are ISO/IEC 23001-16:2021’s, as derived-tracks registers them; this extension defines its own parameters and arithmetic for them (§4.2), and does not read the parts of that standard derived-tracks did not read.

1. Scope

In scope: what an edge may derive and what it must not do (§3); the derivations this revision registers, how each reads its inputs, what it computes and how it writes its output, and which check each takes (§4); the derivation record, its signature and its identity (§5); how an edge publishes what it derives, carries its inputs’ provenance forward and keeps their AI-origin label (§6); verification of a derived object back to its source (§7); who may sign a record (§8).

Out of scope: a transport, and how a record reaches a sink, which is the carrier’s, as it is for receipts (jwp-receipts §1); how a sink asks an edge for a derivation, and which derivations an edge offers; how an edge measures its energy, which energy-measurement governs; presentation, which stays with the sink (derived-tracks §1); and any derivation this revision does not register.

Relationship to the core spec: wai.edge.derivation is a record (SPEC §5): a signed record a WAI tool emits and verifies. It names no payload format and is not dispatchable. A derived object is an ordinary envelope of its own capability, dispatched by SPEC §4 like any other.

Relationship to jwp-receipts: this extension changes no receipt. A relay forwards what it receives as §4 there requires, byte for byte, and §4.3’s rules stand: a relay re-signs no receipt and rewrites no envelope. An edge that derives is a relay for what it forwards and a publisher for what it derives: each output is a new object with its own content hash, in a group the edge signs with its own key (§6). The derivation record is the separate, parallel statement §4.3 there asks of a relay that attests, and §7.5 there registers its authority (§8).

Relationship to derived-tracks: an operation of §4.2 is that extension’s operation of the same capability string, executed under conditions this profile fixes, so that derived-tracks §3 gives the output’s tier from its inputs’ (§4.5). The record carries the content derived-tracks §4 lists for a receipt over a derived sample, in a layout of its own: the executed operation, its parameters, the frame each input was read at, the inputs by content hash, and, where the check is replicate, the output.

Relationship to SPEC §7.1: an output of one input carries derived_from, naming that input as its motif (§4.3). A sink presents it as what it is, a derived object; it does not reconstruct its motif.

2. Terms

The record is JSON under the encodings of jwp-receipts §2.4.3: lowercase hexadecimal byte strings, unsigned integers of at most 2^53 − 1 in plain decimal, no other number, no boolean, terms and date-times as defined there, no member named twice. JCS is the canonical form of that section (RFC 8785).

3. What an edge may derive

3.1 Three kinds

  1. Evaluate an operation. Read each input as samples (§4.1), evaluate one operation of §4.2 over them, and write the result in an image capability (§4.3).
  2. Rebuild at a lower rung. Read an object coded in a capability a sink lacks, and write it in a capability of SPEC §5’s mandatory floor or recommended set: a frame of a wai.video.int_motion clip, as a wai.image.png still or a wai.image.jpeg picture. A rebuild is an evaluation of wai.derive.identity over one frame (§4.2): the read and the write change the capability, and the operation changes no sample. It serves a sink at which the object is inert (SPEC §4 step 6).
  3. Re-package. Change the container without decoding: wai-edge:explode writes one component of a WAI2 envelope as its exploded WAI1 envelope (SPEC §7.1, Exploding a component; §4.4). The output’s payload is the component’s bytes.

3.2 What an edge does not do

An edge MUST NOT:

3.3 What an edge derives from

An edge derives only from objects it has verified: each input’s delivery receipts verify (jwp-receipts §3 steps 1–5), its envelope hashes to its content hash, and, for an input another edge derived, its chain verifies by §7. It names in its record, for each input, the record that derived it, so that a sink can follow the chain (§7.3).

4. The derivations

derivationinputsparameters (§4.2)output capabilitycheck (§4.5)
wai.derive.identity1nonewai.image.png, wai.image.jpegreplicate for wai.image.png with no encoder; else attest
wai.derive.crop1x, y, w, hwai.image.png, wai.image.jpegas identity
wai.derive.roi_selection1x, y, w, hwai.image.png, wai.image.jpegas identity
wai.derive.rotation1quarter_turnswai.image.png, wai.image.jpegas identity
wai.derive.mirror1flipwai.image.png, wai.image.jpegas identity
wai.derive.grid_compositioncolumns × rowscolumns, rowswai.image.png, wai.image.jpegas identity
wai.derive.overlay_composition1 to 16width, height, fill, atwai.image.png, wai.image.jpegas identity
wai.derive.scaling1width, heightwai.image.png, wai.image.jpegattest
wai-edge:explode1componentthe component’s capabilityreplicate

A derivation is named by the string in its first column. The wai.derive.* strings are derived-tracks’ registered operations; wai-edge:explode is a term of this extension and not a capability string. The other operations derived-tracks registers are not derivations of this revision: wai.derive.srgb_fill reads no input, so an output of it chains to no source, and wai.derive.dissolve has a time structure of its own, which an input read at one frame does not give it.

4.1 Reading an input

An evaluation reads each input at one frame, the input’s frame (§5.1), as samples:

An input of either capability holds decode-equivalence (determinism-tiers §2): every conforming reader reads the same samples. An input that does not read as its record states it — another capability, a frame it does not have, a payload its reader refuses, a chunk this section excludes — is refused (input_unreadable).

4.2 The operations

Each operation computes its output samples from its inputs’ samples and its parameters by the rule below, and no other arithmetic. W × H is an input’s width and height; out(i, j) is the output pixel at (i, j) and in(x, y) an input’s, all three bytes of it. An output’s width and height are each at least 1, and its samples at most 2^26 bytes; a parameter the inputs cannot satisfy (a region outside an input, an output past that bound) is refused (params_invalid) when the inputs are evaluated (§7.2 step 5).

Under these rules every operation but scaling is exact for the instance it executes, in the sense of derived-tracks §2: its inputs and output are 8-bit RGB with no stated colour information, and the canvas fill is a constant the record names (same-representation); it copies samples and computes none (copies-only); each input is one frame, and the parameters have one value (time-invariant); a region or a cell lies on the sample grid, a rotation is a multiple of 90° over one resolution, a reflection maps the grid onto itself, and a composed input is placed at its own size at integer offsets, opaquely. Scaling computes new sample values, and derived-tracks registers it untiered.

4.3 Writing the output

The output of an evaluation is a WAI1 envelope whose manifest is, with no other bytes and no whitespace, members in this order:

{"wai":"1.0","media":"image","intent":"replicate","model_requirement":{"capability":C},
 "conditioning":{"kind":K},"derived_from":{"motif":M,"motif_capability":MC,"renderer":R,
 "settings":S},"target":{"frames":1,"h":HEIGHT,"w":WIDTH}}

(shown here on three lines). C is the output capability and K its last dot-separated segment (png, jpeg); HEIGHT and WIDTH are the output’s. derived_from (SPEC §7.1) is written exactly when the derivation has one input: M and MC are that input’s content hash and capability, R is the derivation followed by @1 when the record names no encoder and the encoder’s id otherwise, and S is BLAKE3("wai:render-settings\x01" ‖ JCS(settings)) with settings the object {"derivation", "encoder", "frame", "params"} of the record’s members of those names and the input’s frame. An output of several inputs carries no derived_from: it presents several motifs, and its record names them all. Strings are written as JSON strings, integers in decimal; nothing in them needs an escape.

The payload:

4.4 Re-packaging

wai-edge:explode, { "component" }, reads one input, a WAI2 envelope, and writes the exploded envelope of its entry number component (from 0), exactly as SPEC §7.1 defines it: the entry’s envelope_manifest where it carries one, and otherwise the manifest that section assembles from the WAI2 manifest’s members, over the entry’s rendition bytes. Nothing is decoded and no sample is read. The output’s capability is the entry’s, and the input’s capability in the record names it too. An entry that cannot be exploded, or a component past the last entry, is refused (input_unreadable). A component and its exploded envelope are the same object for every rule that names an object by content hash (SPEC §7.1), so an exploded output is the object a sink would have taken from the WAI2, now carried on its own, under the edge’s receipts.

4.5 The check

A derivation’s check is replicate exactly when every step of it is pinned: the reading of its inputs (§4.1 pins every reading this revision has), its operation, which is exact (§4.2), and its writer, which is packaging §5’s still writer for a wai.image.png output whose record names no encoder, or SPEC §7.1’s explode. Otherwise its check is attest:

A record’s check MUST be its derivation’s (check_mismatch): one derivation has one check, and a sink reads from the record what it is relying on.

5. The derivation record

5.1 Members

A record is a JSON object with exactly these members, under §2’s encodings:

membertypemeaning
kindstring"wai-derivation".
derivationtermA derivation of §4.
paramsobjectIts parameters, exactly the members §4.2 or §4.4 gives it, each an integer, a string, or for at an array of { "x", "y" }.
encoderobject or nullnull where the output is written by a writer this revision pins (§4.3, §4.4); otherwise { "id": term, "settings": object }, the encoder the edge ran and the settings it ran it with, its statement.
checkstringreplicate or attest (§4.5).
inputsarray1 to 16 inputs, in the order the derivation reads them (below).
outputobjectThe output (below).
energyobject or nullnull when the derivation was unmetered. Otherwise { "joules_micro", "energy_provenance", "energy_uncertainty", "energy_counter_filtering", "energy_calibration_ref" }, each as jwp-receipts §2.4.5 writes it: the energy the edge spent on this derivation, reading, operating and writing, with its acquisition class.
provenancearrayAt most 1024 provenance items carried forward (§6.1, below), in ascending order of input, then kind as a string, then digest, none twice.
synthesisstring or nullThe output’s AI-origin label (§6.2): assist, generate, or null for none.
issued_atdate-timeWhen the edge signed the record. Self-asserted.
signer_pubkey32 B hexThe edge’s key.
signer_idstringThe edge’s identity string, as for a group receipt (jwp-receipts §2.4.1, §7.1).
sig64 B hex§5.2.

An input:

membertypemeaning
content_hash32 B hexThe input’s content hash.
capabilitytermThe capability the derivation read it as (§4.1), or, for wai-edge:explode, the capability of the entry exploded.
receipt32 B hexThe link hash of the group receipt that carries it.
object_idintegerIts object_id in that group.
frameinteger or nullThe frame it was read at (§4.1); null for wai-edge:explode.
samples32 B hex or nullThe samples digest of what was read; null for wai-edge:explode.
derivation32 B hex or nullThe hash of the record that derived it, or null for an object the edge received from its publisher.
synthesisstring or nullThe input’s AI-origin label (§6.2), as synthesis above.

A provenance item:

membertypemeaning
inputintegerThe position, among the inputs, of the input it came with.
kindstringclaim: a claim about that input (jwp-receipts §2.4.3); c2pa: a C2PA Manifest Store bound to it.
digest32 B hexA claim’s link hash (jwp-receipts §2.4.3); a manifest store’s SHA-256 over its bytes.
compartmentstringsystem or personal (§6.1).

The output:

membertypemeaning
content_hash32 B hexThe output’s content hash.
capabilitytermIts capability.
receipt32 B hexThe link hash of the edge’s group receipt that carries it (§6).
object_idintegerIts object_id in that group.
width, heightinteger or nullIts size; null for wai-edge:explode.
samples32 B hex or nullThe samples digest of the samples written: those the operation produced, which an attested encoder was given; null for wai-edge:explode.

5.2 Signature and identity

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

The signature is verified under jwp-receipts §3.1 step 3’s strict rule. A record SHOULD be sent in canonical form; relays forward it byte for byte.

5.3 Reading and rules

A reader reads a record and refuses at the first of these that holds:

  1. It is not one JSON text of the shape §5.1 gives, with every member of the right type and encoding, no member missing and none added, at any level, kind "wai-derivation", and its provenance items in their order, none twice, each naming an input the record has (record_malformed).
  2. Its derivation is not one of §4 (derivation_unknown).
  3. Its params do not hold exactly the derivation’s members, each in its range (§4.2, §4.4): w, h, width, height, columns and rows at least 1; quarter_turns 1, 2 or 3; flip one of its two values; fill six lowercase hexadecimal digits; at one entry per input, each { "x", "y" }; a size the parameters fix (a region’s, a canvas’s, a scaling’s) within 2^26 sample bytes. Or its encoder is null where §4.3 pins no writer (a wai.image.jpeg output), or not null for wai-edge:explode. Or its output capability is not one the derivation writes (§4), or, for wai-edge:explode, not the input’s (params_invalid).
  4. Its inputs are not the derivation’s: another number of them (columns × rows for a grid), an input of a capability §4.1 does not read, a frame, input samples, output width, height or samples that is null for an evaluation or not null for an explode, or a frame other than 0 for a wai.image.png input (inputs_invalid).
  5. Its check is not its derivation’s (§4.5) (check_mismatch).
  6. Its energy breaks energy-measurement §2: a figure of 0 beside a class, the legacy HwShunt, a class that does not read, a hardware class without its evidence or below the resolution floor (§2.3 there) (energy_invalid).
  7. Its synthesis is lower than an input’s (§6.2) (synthesis_dropped).

6. Publishing the output

An edge publishes each output on a track of its own, under its own key, as a publisher does (jwp-receipts §2): a group receipt it signs and an object receipt for the output, whose content_hash is the output’s. The record’s output.receipt and output.object_id name them.

An edge serving a derived object MUST offer its subscriber the record of each hop of the object’s chain, the delivery receipts each record names and the provenance items each carries (§6.1), by the same carriage as the object’s receipts. Without them a sink can verify the object’s delivery from the edge and nothing about where it came from. It forwards the source’s receipts unaltered (jwp-receipts §4.1).

6.1 Provenance carried forward

A derivation makes a new object, and nothing the source carried is in it unless the edge puts it there: a still the pinned writer writes holds no chunk but its picture, and an explode writes a new envelope. So a record carries its inputs’ provenance forward, by reference.

The provenance items of an input are, of what the edge received:

A record MUST list every provenance item of every input, each with the input it came with and its compartment, and an item that a record below it carries keeps that record’s compartment. Listing an item copies nothing of it: the record names it by digest, the edge offers the item with the chain (§6), and the item’s own signature, where it has one, is what holds it to what it says. An item the record does not list, of any compartment, has been stripped (provenance_stripped).

The compartment is the edge’s reading of an item, in the split California Business and Professions Code §22757.1 makes: personal where the item holds personal information, or unique device, system or service information reasonably capable of being associated with a particular user (§22757.1(n)), and system otherwise: information about the type of device, system or service that made the content, or about its authenticity (§22757.1(p)). Information inside a digital signature is not personal there (§22757.1(n)(2)), so a signature is system provenance. A store embedded in an input the edge reads as system provenance unless it knows otherwise.

This revision carries every item, personal ones included: an edge strips none, and a sink holds the chain’s items from the edge that served it. Redacting a personal item under a stated rule, with a salted commitment that accounts for the removal so that it is never silent, is a later revision’s; until it is registered, a personal item is carried as a system one is. A C2PA manifest an edge emits for its output names each manifest store of its inputs as an ingredient, parentOf for a derivation of one input (C2PA 2.4 §18.15: c2pa.transcoded for a rebuild, c2pa.repackaged for an explode); the reference edge emits none.

6.2 The AI-origin label

A record states, for each input and for its output, an AI-origin label: null where no label is known, or, in rising order, assist (AI altered the content and generated none of it) or generate (AI generated the content, or part of it). A label may rise along a chain and never fall:

Every derivation this revision pins copies samples or containers and synthesizes nothing, so it raises no label; an attested step is labelled by its edge, and a verifier checks that no label falls, not what an attested step did. The two labels are the two highest classes of an origin claim’s order of steps, capture, convert, assist and generate, in which every derivation this revision pins is a convert step; a later revision may carry such a claim as a provenance item, name the lower classes, and map them onto the digital source types an export writes.

7. Verification

7.1 What a verifier holds

membervaluesdefaultmeaning
attestedaccept, refuseacceptrefuse: a chain with an attest hop is refused (attested_refused).
recomputewhen-held, requiredwhen-heldrequired: a replicate hop whose inputs are not all held is refused (inputs_not_held); when-held: it is accepted as not-recomputed.
authoritywhen-given, requiredwhen-givenrequired: a hop that is not confirmed under §8 is refused (edge_not_authorised).

A record that reads as a JSON text is held under its hash (§5.2), whether or not it then passes §5.3, so that a hop that is held and broken is refused for what it breaks.

7.2 One record

The verifier verifies the subject’s record by these steps, and an input’s record by the same steps when step 4 reaches it. A record verified once is not verified again. At the first step that fails, the verification is refused with that step’s reason, at that record.

  1. Reading (§5.3): record_malformed, derivation_unknown, params_invalid, inputs_invalid, check_mismatch, energy_invalid, synthesis_dropped.
  2. Signature (§5.2): a key that fails RFC 8032 §5.1.3’s decoding or is of small order is bad_key; a signature that fails, bad_signature.
  3. Output. The group receipt whose link hash is output.receipt, and the object receipt of that group at output.object_id, are held (output_receipt_not_held); the group verifies (jwp-receipts §3 steps 1–2), the object receipt is included at its position (§3 step 5 there), and its content_hash is the output’s (output_receipt_invalid); §6’s rules hold (output_receipt_mismatch, energy_disagrees). Where the output envelope is held: it hashes to output.content_hash (output_hash_mismatch); its capability is output.capability, and, for an evaluation, its manifest is the one §4.3 writes from the record (output_manifest_mismatch); and, for a wai.image.png output, the samples it reads as (packaging §5’s still reader) are of the record’s width and height and have its samples digest (samples_mismatch): PNG is lossless, so this holds whoever wrote it.
  4. Each input, in order:
    1. its group receipt (by receipt) and its object receipt (at object_id) are held (input_receipt_not_held); the group verifies, the object receipt is included at its position, and its content_hash is the input’s (input_receipt_invalid);
    2. where derivation is null, the group receipt is signed by a source key; otherwise an edge’s output is passed off as a source object, and a hop is missing (chain_skips_hop);
    3. where derivation is not null: the input’s chain is not longer than 8 hops, the subject’s record being the first (chain_too_long); the record of that hash is held (hop_not_held) and reads (§5.3, refused at that record); its output’s content_hash, capability, receipt and object_id are the input’s (hop_mismatch); and it is verified by these steps, whose refusal, at that record, is this verification’s;
    4. where the input envelope is held: it hashes to content_hash (input_hash_mismatch); it reads as §4.1 or §4.4 has it (input_unreadable); and the samples it reads at frame have the digest samples (samples_mismatch);
    5. its provenance (§6.1): each claim the verifier holds about it, each manifest store the verifier holds bound to it, and, where the input envelope is held, each store embedded in it, is an item of the record for this input, of that kind and digest; and, where an edge derived it, every item of that edge’s record is one too, with its compartment (provenance_stripped). Where an edge derived it, its synthesis is no lower than that edge’s record’s (§6.2, synthesis_dropped).
  5. Recomputation, where every input is held. For a replicate record: evaluate the derivation (§4.2–§4.4) over the inputs (params_invalid for a parameter they cannot satisfy); the output’s samples digest, size and envelope MUST be the record’s, byte for byte (replicate_mismatch). For an attest record: where the operation is exact, evaluate it, and the output’s samples, width and height MUST be the record’s (samples_mismatch); where it is not, nothing of it is recomputed. Where an input is not held, a replicate record is not-recomputed, and refused under the policy recompute: required (inputs_not_held).
  6. Authority (§8): key_log_not_held, edge_not_authorised, edge_revoked.
  7. Policy: an attest record under attested: refuse is attested_refused.

A refusal names its place: ["record", r] for steps 1 and 2, ["record", r, "output"] for step 3, ["record", r, "input", i] for step 4, ["record", r, "recompute"] for step 5, ["record", r, "authority", origin] for step 6, an origin of §8 or, where the hop has none, null, and ["record", r, "policy"] for step 7, with r the record’s position among the records given and i the input’s among its inputs.

7.3 The chain

Step 4 follows each derived input to the record that derived it, and that record’s inputs to theirs, until every input is a source object. Each hop is verified on its own, against its own receipts and inputs. A hop that names a record the verifier does not hold is refused, not skipped: a sink that cannot follow a chain to its source knows only that the edge delivered the object (§6). A chain cannot loop, since a record names its inputs’ records by hash and no hash can name a record that names it; the bound of 8 hops is a bound on work.

What a verified chain establishes: the subject’s object was derived, hop by hop, from the source objects the chain reaches, by the derivations its records name with the parameters they state, each signed by the edge that made it; each replicate hop whose inputs were held was re-executed and matched byte for byte; each attest hop rests, at its attested step, on its edge’s signature; and each source object is one its publisher signed for delivery. It does not establish that the publisher meant the object to be derived from; that is §8’s.

7.4 Outcome

A verifier states refuse with a reason and its place, or accept with:

8. Authority

A derivation is made under the authority of the origin whose content it derives from. A hop’s origins are the origins its chain reaches: for each source object, the origin its group receipt’s signer_id names where it is origin-bound (jwp-receipts §7.1), taken in ascending order, each once. The authority is edge/derive (jwp-receipts §7.5), and a hop’s edge needs it from each of its hop’s origins, the hops below it included: an origin lists, in its key log, the edges it trusts with its content, as it lists its relays and stores.

For each origin of a hop, where the verifier holds a head for it: the head is a revision the verifier holds for that origin (jwp-receipts §7.2 rules 1–3) (key_log_not_held); it has an entry whose key is the record’s signer_pubkey and whose may_sign holds edge/derive (edge_not_authorised); and that entry’s status is not revoked (edge_revoked).

A hop is confirmed when it has origins and the verifier holds a head for each that passes; unconfirmed when it has origins and lacks a head for one; and none when it has no origin, its sources naming no origin. Under the policy authority: required, a hop that is not confirmed is refused (edge_not_authorised, at the first origin that lacks a head, or at null).

edge/derive is an authority of its own: not delivery, which an edge needs from its own origin to sign its group receipts, and not live/relay, since a relay forwards what it received and an edge publishes something new. Retiring an entry does not withdraw it here; revoking it does. A metered record’s figure needs, beside it, the energy authority of its class (energy/<class>, jwp-receipts §7.5), which that section’s confirmation (§7.4 there) checks under an anchored origin; this section checks edge/derive alone.

9. Reasons

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

reasonraised at
record_malformed§5.3 rule 1, §7.2 step 1
derivation_unknown§5.3 rule 2, §7.2 step 1
params_invalid§4.2, §5.3 rule 3, §7.2 steps 1 and 5
inputs_invalid§5.3 rule 4, §7.2 step 1
check_mismatch§4.5, §5.3 rule 5, §7.2 step 1
energy_invalid§5.3 rule 6, §7.2 step 1
bad_key§7.2 step 2
bad_signature§7.2 step 2
output_receipt_not_held§7.2 step 3
output_receipt_invalid§7.2 step 3
output_receipt_mismatch§6, §7.2 step 3
energy_disagrees§6, §7.2 step 3
output_hash_mismatch§7.2 step 3
output_manifest_mismatch§4.3, §7.2 step 3
input_receipt_not_held§7.2 step 4
input_receipt_invalid§7.2 step 4
chain_skips_hop§7.2 step 4
chain_too_long§7.2 step 4
hop_not_held§7.2 step 4, §7.3
hop_mismatch§7.2 step 4
input_hash_mismatch§7.2 step 4
input_unreadable§4.1, §4.4, §7.2 step 4
samples_mismatch§4.5, §7.2 steps 3, 4 and 5
provenance_stripped§6.1, §7.2 step 4
synthesis_dropped§5.3 rule 7, §6.2, §7.2 steps 1 and 4
replicate_mismatch§4.5, §7.2 step 5
inputs_not_held§7.1, §7.2 step 5
key_log_not_held§8, §7.2 step 6
edge_not_authorised§7.1, §8, §7.2 step 6
edge_revoked§8, §7.2 step 6
attested_refused§7.1, §7.2 step 7

10. Conformance

edge-conformance/ holds the corpus, in one file, vectors.json, written from fixed seeds by wai_edge_vectors gen. Each vector is what a verifier holds (§7.1) and the outcome §7.4 gives it. It has:

A conforming verifier gives every vector the outcome, the reason, the place and the facts the corpus states. wai_edge_vectors verify and the test tests/derive_edge.rs check that the file regenerates byte for byte, that wai-rs gives every vector its outcome, that every reason of §9 appears, and that §9 lists exactly the reference’s codes; the same test runs the reference edge, wai_edge, twice over a source to make a chain of two hops, and verifies it. edge-conformance/edge_verify.py is the second implementation, written from this text: it shares no code with wai-rs; it reads delivery receipts with the JWP corpus’s second implementation (jwp-conformance/jwp_verify.py), decodes WIV1 with the integer payload corpus’s (int-payload-conformance/int_payload_verify.py), explodes WAI2 envelopes with the staged-delivery corpus’s (staged-conformance/staged_verify.py), reads key-log heads with the live-rooms corpus’s (live-conformance/live_rooms_verify.py), verifies every signature with WAI’s strict rule (claim-conformance/_ed25519.py), and writes stills and evaluates every operation itself. It requires every mutated copy of the corpus to fail, and, with each of its own checks dropped in turn, requires the corpus to catch the weakened verifier.

11. Security considerations