# WAI Extension: AI Usage Preferences and Consumption Claims > Status: Draft. Registers the record `wai.ai.usage`: AI usage preferences for media, bound to > the bytes they govern, and the signed statement of an agent that consumed media. A **usage > statement** carries, verbatim, the preferences of three published vocabularies: the AI usage > preference vocabulary of `draft-ietf-aipref-vocab-08`, in its exemplary Structured Field form; > the TDM Reservation Protocol's `tdm-reservation` and `tdm-policy` (Final Community Group > Report `CG-FINAL-tdmrep-20240510`); and the entries of the `cawg.training-mining` assertion > (Training and Data Mining Assertion, version 1.1). WAI adds no category to any of them. The > statement rides in an envelope's manifest, where the object's receipt signs it with the > content, so it cannot be removed without the object failing its receipt; in a room usage > record, `WAU1`, that the room authority signs; and in a room grant, as the terms one principal > is granted. An HTTP origin writes the `Content-Usage` field of `draft-ietf-aipref-attach-05` > and the TDMRep fields from the signed statement, and a MoQ catalog states it for a track > before a sink subscribes. One precedence rule decides a use: a grant's terms, then every > signed statement, the most restrictive governing, then unsigned statements where nothing is > signed. An agent that consumed objects signs a **consumption claim**, the > [jwp-receipts](jwp-receipts.md) claim class `consumption`, naming the objects by content hash, > the term it acted under in each vocabulary, its key as a `did:key`, the statements it saw and > the grant it held; a publisher verifies it against the objects, and a relay or gateway MAY > require one before it serves an agent. No new transport. The reference implementation is the > `ai_usage` module of `wai-rs` (feature `ai_usage`), with the class's body in `claim_receipt`; > the corpus is [`live-conformance/usage/`](../live-conformance/usage/), and its second > implementation is `live-conformance/ai_usage_verify.py`. > Keywords **MUST**, **MUST NOT**, **SHOULD**, **MAY** are RFC 2119/8174. > > Texts read for this revision, each pinned to the version named: > `draft-ietf-aipref-vocab-08` (14 September 2026) and `draft-ietf-aipref-attach-05` > (19 August 2026), Internet-Drafts of the IETF AI Preferences Working Group, both Working > Group documents intended for Proposed Standard and neither yet submitted to the IESG; the > vocabulary marks its §3 and §4 as not yet having the group's consensus. RFC 9651 > (Structured Field Values for HTTP). The TDM Reservation Protocol, Final Community Group > Report of 10 May 2024, the report's latest. The Creator Assertions Working Group's Training > and Data Mining Assertion, version 1.1, ratified 16 May 2025, the current version. A section > number of one of these texts is that version's. ## 1. Scope In scope: the usage statement, its three vocabularies, how each is read and what each says about a use (§3); where a statement is carried: an envelope, a room, a grant, an HTTP response and a MoQ catalog (§4); the precedence rule and what a conflict means (§5); the consumption claim (§6); the gate a relay or gateway applies before serving an agent (§7); and verification (§8). Out of scope: whether a preference is followed, which the vocabulary leaves to its recipient (`draft-ietf-aipref-vocab-08` §3.2), and which this extension makes evidence of, not a duty; the legal effect of a preference or of a claim; how a crawler or an agent is recognised as one (§7.1); a profile of the agent protocols that fetch media, which this revision does not define; and the robots.txt rule of `draft-ietf-aipref-attach-05` §3, which is the origin's and binds no object (§4.4). Relationship to the core spec: `wai.ai.usage` is a **record** ([SPEC](../SPEC.md) §5): a signed record a WAI tool emits and verifies. It names no payload format and is not dispatchable. The manifest member `ai_usage` (§4.1) is an OPTIONAL member under SPEC §8's rule for MINOR revisions. Relationship to [jwp-receipts](jwp-receipts.md): the envelope's statement is signed by the object's receipt (§2.1 there); the consumption claim is a `claim` receipt of the class `consumption` (§2.4.4 there), on that extension's envelope, canonical form, signature and chain, with the body and class rules of §6 here. Relationship to [live-rooms](live-rooms.md): the room descriptor's layout is unchanged. A room's statement is a record of its own, chained like the review policy of [live-moderation](live-moderation.md) §3.4, and a grant states its principal's terms in the attribute `ai_usage` (live-rooms §4.4). ## 2. Terms - **Vocabulary**: one of `aipref`, `tdmrep` and `cawg`, the three sources §3 carries. - **Term**: what a use is named by in a vocabulary: an `aipref` usage category label (`train-ai`, `ai-use` or `search`, `draft-ietf-aipref-vocab-08` §6.1); the `tdmrep` property `tdm-reservation`, which governs text and data mining as a whole; or a key of a `cawg.training-mining` assertion's entries. - **Preference**: what a vocabulary says about one term: **allow**, **disallow** or **unknown** (`draft-ietf-aipref-vocab-08` §3). - **Statement**: a JSON object of §3 holding one or more vocabularies. - **Signed statement**: one of an envelope whose receipts verify (§4.1), or of a room usage record that verifies (§4.2). - **Agent**: a party that consumes media by automated means, for any use a vocabulary names. An **agent principal** in a room is one whose grant carries the attribute `ai_usage` (§4.3). - **Content hash**: an object's [jwp-receipts](jwp-receipts.md) §2.1 content hash, 32 bytes, written as 64 lowercase hexadecimal digits in JSON. Integers are little-endian. A digest is SHA-256 (FIPS 180-4), 32 bytes. Ed25519 is RFC 8032's, verified strictly ([SPEC](../SPEC.md) §7 *Signatures*). The **canonical form** of a JSON value is its JCS serialisation (RFC 8785) as jwp-receipts §2.4.3 restricts it: strings, unsigned integers, `null`, arrays and objects. ## 3. The usage statement ### 3.1 Members A statement is a JSON object with one or more of these members and no other: | member | value | source | |---|---|---| | `aipref` | an object: `revision`, `value` | `draft-ietf-aipref-vocab-08` | | `cawg` | an object: `revision`, `entries` | Training and Data Mining Assertion | | `tdmrep` | an object: `revision`, `tdm-reservation`, `tdm-policy` | the TDM Reservation Protocol | Each vocabulary keeps its own terms and its own preferences. A statement maps no term of one onto a term of another, and a WAI tool **MUST NOT** write a term none of them defines. ### 3.2 `aipref` - `revision`: the string `draft-ietf-aipref-vocab-08`. - `value`: a statement of preference in the vocabulary's exemplary serialisation (`draft-ietf-aipref-vocab-08` §6), as the text an HTTP `Content-Usage` field would carry (`draft-ietf-aipref-attach-05` §2): for example `train-ai=n, search=y`. It is 1 to 1024 characters, each U+0000 to U+007F (§6.3 there: the syntax is ASCII), and it **MUST** parse as a Dictionary (§3.3). The preference of a term is read as `draft-ietf-aipref-vocab-08` §6.5 has it: the Dictionary's member whose key is the term's label, its parameters disregarded, gives **allow** when its value is the Token `y`, **disallow** when it is the Token `n`, and **unknown** otherwise; a label absent is unknown, and a key that is not one of the three labels is ignored (§6.4 there). When a key repeats, the Dictionary keeps its last value (RFC 9651 §4.2.2), so `train-ai=n, train-ai=y` allows. ### 3.3 The Dictionary `value` parses as RFC 9651 §4.2 parses a field of type Dictionary: its characters are bytes (§4.2 step 1, which fails on a byte above 0x7F); leading spaces are discarded; the Dictionary is parsed by §4.2.2, with its keys (§4.2.3.3), parameters (§4.2.3.2), inner lists (§4.2.1.2) and every bare item: Integer and Decimal (§4.2.4), String (§4.2.5), Token (§4.2.6), Byte Sequence (§4.2.7), Boolean (§4.2.8), Date (§4.2.9) and Display String (§4.2.10); leading spaces are discarded again; and any character left fails. RFC 9651 leaves two checks of a Byte Sequence to the parser (§4.2.7); here the sequence parses when its content, the characters between its colons, is characters of the base64 alphabet (RFC 4648 §4) followed by at most two `=`, with the alphabet characters numbering other than one more than a multiple of four. Neither padding nor pad bits are checked. `draft-ietf-aipref-vocab-08` §6.5.1 reads a value that does not parse as unknown for every term. A statement does not carry one: a signer whose preference would be read as unknown throughout has made a mistake that binding the statement would preserve, so it is refused. ### 3.4 `cawg` - `revision`: the string `1.1`. - `entries`: an object of 1 to 32 members, the assertion's `entries` map (Training and Data Mining Assertion 1.1 §3.2), member for member. Each key is 1 to 255 bytes of components separated by single `.`, a component being one or more of `a`–`z`, `A`–`Z`, `0`–`9`, `_` and `-`; a key beginning `cawg.` is one of the four the assertion defines, `cawg.data_mining`, `cawg.ai_inference`, `cawg.ai_generative_training` and `cawg.ai_training`, since it reserves the prefix (§3.1 there). Each value is an object holding `use`, one of `allowed`, `notAllowed` and `constrained`, and optionally `constraint_info`, 1 to 1024 bytes of UTF-8 (the member the assertion's CDDL names; its prose spells it `constraints_info`). A term is a key: `allowed` is **allow**; `notAllowed` is **disallow**; `constrained` is **disallow**, since the assertion treats it as `notAllowed` in the absence of additional information (§3.1 there), which a grant can give (§5.2); a key absent is **unknown**. The assertion's `metadata` map is not carried. ### 3.5 `tdmrep` - `revision`: the string `CG-FINAL-tdmrep-20240510`. - `tdm-reservation`: the integer 1, TDM rights are reserved, or 0, they are not (the protocol's §5.1). The protocol treats any other value as unset; a statement carries none. - `tdm-policy`: `null`, or the URL of the rightsholder's TDM policy (the protocol's §5.2): 8 to 2048 bytes of `!` to `~` (0x21–0x7E), beginning `https://` or `http://`. The one term is `tdm-reservation`: 1 is **disallow** and 0 is **allow**. `tdm-policy` is carried for whoever seeks a licence and is not evaluated. ### 3.6 Reading a statement A statement is read from JSON in which no object names a member twice. It is refused at the first of these that holds: 1. It is not an object, it has no member, it has a member §3.1 does not list, or a vocabulary's object has a member absent, a member its section does not list, or a member of another type or outside its range: `usage_malformed`. A `revision` that is a string is in range here; in `aipref`, a `value` that does not parse (§3.3) is not. 2. A vocabulary's `revision` is other than the string its section names: `usage_revision_unknown`. 3. Its canonical form is longer than 4096 bytes: `usage_malformed`. So a statement with a malformed `tdmrep` and an unknown `aipref` revision is refused `usage_malformed`. The **digest** of a statement is SHA-256 over its canonical form. ## 4. Carriage ### 4.1 The envelope: the manifest member `ai_usage` A `WAI1` or `WAI2` manifest ([SPEC](../SPEC.md) §3, §7.1) MAY carry, at its top level, the member `ai_usage`, a statement. It applies to the object: every rendition and companion of a `WAI2`. A reader of an earlier revision ignores it (SPEC §8); a malformed one leaves the manifest readable, and this extension refuses the statement, not the envelope. The member is inside the envelope's bytes, and the object's content hash covers them ([jwp-receipts](jwp-receipts.md) §2.1): the group receipt that includes the object signs the statement with the content. An object whose statement is altered, added or removed no longer hashes to the content hash its receipt names, and fails its receipt. A verifier given an envelope and its receipts: 1. accepts the receipts as jwp-receipts §3 steps 1 to 5 do, the envelope hashing to the object receipt's `content_hash` excepted; a refusal there is `receipt_invalid`; 2. requires the envelope to hash to that `content_hash`: otherwise `usage_unbound`; 3. reads the manifest's `ai_usage`, where present, by §3.6. A manifest that does not read (SPEC §2) holds no statement that can be read: `usage_malformed`. An object that passes all three is **held**, with its statement **signed by** the group receipt's `signer_pubkey`, or with none. A publisher that states a preference for an object writes the member before it receipts the object. An envelope that carries no receipt carries an unsigned statement, which §5.3 reads as it reads an HTTP field. ### 4.2 A room: the room usage record `WAU1` The room authority states the room's preference in a **room usage record**: | field | size | meaning | |---|---|---| | `magic` | 4 | `WAU1` (ASCII) | | `version` | 1 | 1 in this revision | | `room` | 32 | the room id (live-rooms §3.2) | | `prev` | 32 | the digest of the record this one replaces; 32 zero bytes for the first | | `effective` | 8 | u64: the room-clock instant from which it applies; 0 for the first | | `statement` | 2 + *n* | a u16 length, then the statement's canonical form, 2 to 4096 bytes | | `sig` | 64 | Ed25519 by the room descriptor's `authority` over the 18 bytes `wai:ai-usage-room` (ASCII) and 0x01, followed by every byte from `magic` through `statement` | The record's **digest** is SHA-256 over the bytes from `magic` through `statement`. A reader reads exactly the bytes it is given, in the order above, and refuses at the first of these that holds: fewer than 4 bytes, `truncated`; a magic other than `WAU1`, `malformed`; no byte after the magic, `truncated`; a version other than 1, `unknown_version`; the bytes end before a field, `truncated`; bytes after `sig`, `malformed`. It then reads `statement` as JSON and by §3.6 (`usage_malformed`, `usage_revision_unknown`), and refuses bytes that are not the canonical form of what they hold (`usage_malformed`). Then `room` is the room id (`wrong_room`), and the signature verifies under the authority (`bad_key`, `bad_signature`). A verifier is given a room's records in a list. The first has `prev` zero and `effective` 0; each later one has as `prev` the digest of the one before it, and an `effective` above that one's. A record that breaks this is refused `room_usage_unlinked`. The record **in effect** at room-clock instant *t* is the last whose `effective` is at or below *t*. A record is replaced, never amended: as `draft-ietf-aipref-attach-05` §3.3 has it for robots.txt, a change applies from its instant on and never to what was published before. An object is the room's when it is given with the room (§8). It is governed at its manifest's `live.t` ([live-ingest](live-ingest.md) §6), an integer; an object of the room whose manifest has none is governed by every record in the list. ### 4.3 A grant's terms: the attribute `ai_usage` A room grant MAY carry the attribute `ai_usage` (live-rooms §4.4), whose value is the canonical form of a statement: the **terms** the authority grants that principal. They are an arrangement in the sense of `draft-ietf-aipref-vocab-08` §5.2, which overrides a statement of preference for the uses it names. A grant whose attribute does not read by §3.6, or is not the canonical form of what it holds, is refused `grant_usage_malformed`, and its principal holds no terms. The attribute is signed with the grant (live-rooms §4.2), and the 1024 bytes the grant's attributes take bound the terms. ### 4.4 HTTP: fields mapped from the signed statement An origin that serves an object ([http-delivery](http-delivery.md) §5) whose envelope carries a statement **SHOULD** send, for a `GET` or `HEAD` of it: - for `aipref`: `Content-Usage:` and the statement's `value`, byte for byte (`draft-ietf-aipref-attach-05` §2); - for `tdmrep`: `tdm-reservation:` and `0` or `1`, and, where `tdm-policy` is not `null`, `tdm-policy:` and the URL (the TDM Reservation Protocol §6.2). `cawg` has no HTTP field. These fields are unsigned. A client reads them as the response's statement and compares it with the object's signed statement: - `Content-Usage` field lines are combined as RFC 9110 §5.3 combines them, with a comma and a space, and parsed by §3.3. It **conflicts** when it does not parse, or when it gives any of the three labels a preference other than the signed `value` gives it. Parameters, unknown keys and spelling (`train-ai=n,search=y` against `train-ai=n, search=y`) do not conflict. - `tdm-reservation` conflicts when more than one field line carries it, or when its value, without leading and trailing spaces and tabs, is other than the signed one written in decimal. `tdm-policy` conflicts when more than one field line carries it, or when its value, trimmed the same way, is other than the signed URL, or the signed one is `null`. An absent field conflicts with nothing. - A field for a vocabulary the signed statement does not carry, or any field for an object with no signed statement, conflicts with nothing: it is an unsigned statement (§5.3). A response whose fields conflict is refused `header_conflicts`. Field names compare without regard to case. The robots.txt `Content-Usage` rule (`draft-ietf-aipref-attach-05` §3) states a preference for a path, not for an object, and is unsigned; a WAI origin MAY write one, and this extension reads none. ### 4.5 MoQ: the catalog field `wai_ai_usage` A WAI track's catalog entry MAY carry `wai_ai_usage`, a statement ([moq-streaming-format](moq-streaming-format.md) §4), so that an agent knows the preference before it subscribes. A catalog is unsigned. Its statement **MUST** be the statement of every object on the track that carries one; a catalog whose statement does not read by §3.6, or whose digest differs from the digest of a held object's statement on the track, is refused `catalog_conflicts`. A room track's catalog **SHOULD** state the room record in effect when the catalog is published. ## 5. Precedence ### 5.1 What governs an object An object's **governing statements** are its signed statements: the statement of its envelope, when it is held (§4.1); and, when it is a room's, the room record in effect at its `live.t`, or every record of the room where it has none (§4.2). A principal's **terms** are those of the grant it acts under (§4.3). ### 5.2 The rule The preference of a use, named by a term *T* of a vocabulary *V*, for an object and a principal: 1. **Terms.** When the principal's terms carry *V* and give *T* allow or disallow, that is the preference: an arrangement overrides a statement of preference (`draft-ietf-aipref-vocab-08` §5.2). 2. **Signed statements.** Otherwise, of the governing statements that carry *V*: disallow when any gives *T* disallow; else allow when any gives it allow; else unknown. This is the vocabulary's own rule for combining statements (§5.1 there), the most restrictive applying. 3. **Unsigned statements.** When no governing statement carries *V*, an agent MAY read an unsigned statement for *V* (an HTTP field, a catalog, an envelope without receipts) by the same rule. It names no declaring party (§3.1 there), and no verifier can check what it said. A use named in several vocabularies is disallowed when any of them disallows it; allowed when none disallows it and one allows it; unknown otherwise. A use that falls under no term of a vocabulary is **outside** that vocabulary, which then says nothing about it. What an agent does with an unknown preference is not this extension's: the vocabulary takes no position on a default (§5 there), and a gate MAY (§7.1). ### 5.3 Conflicts A signed statement governs over every unsigned one. An unsigned statement that differs from a signed one is not the signer's: a client that finds one ignores it in step 3, reports the response or catalog refused (`header_conflicts`, `catalog_conflicts`), and decides by the signed statement. A stricter unsigned statement is the preference of a party nothing identifies; an agent MAY honour it as well, and a consumption claim is checked against the signed statements alone. Two signed statements that differ are not a conflict: rule 2 applies, and the most restrictive governs. ## 6. The consumption claim ### 6.1 The body A consumption claim is a [jwp-receipts](jwp-receipts.md) §2.4.3 `claim` of `class` `consumption`, `subject_type` `object`, signed by the agent's key, whose `subject` is the first of `objects`. Its `signer_id` is the `did:key` of `signer_pubkey` (jwp-receipts §7.1), the key that traces the agent. Its body has exactly these members, read in this order at jwp-receipts §3.1 step 2: | member | type | meaning | |---|---|---| | `objects` | array of 1 to 1024 32 B hex | the content hashes of the objects consumed | | `usage` | array of 1 to 3 entries | the use, in each vocabulary the agent answers | | `seen` | array of 0 to 64 32 B hex | the digests of the statements that governed the objects (§5.1) | | `grant` | 32 B hex or `null` | the digest of the room grant the agent consumed under (live-rooms §4.2) | A `usage` entry has exactly `vocabulary`, one of `aipref`, `cawg` and `tdmrep`, and `term`, a string of 1 to 255 bytes or `null`, read in that order. A term names the use as that vocabulary names it; `null` states that the use falls under no term of the vocabulary. An array of another length, a `vocabulary` outside the three and a `term` of another length are `field_type`. ### 6.2 Class rules At jwp-receipts §3.1 step 4, in this order: 1. `objects` is in strictly ascending order: `consumption_objects_unordered`. 2. `subject` is the first of `objects`: `consumption_subject_mismatch`. 3. `usage` is in strictly ascending order of `vocabulary`: `consumption_usage_unordered`. 4. Each `term` that is not `null` is a term of its vocabulary: for `aipref` one of `train-ai`, `ai-use` and `search`; for `tdmrep`, `tdm-reservation`; for `cawg`, one of the assertion's four keys or a key of §3.4's form that does not begin `cawg.`: `consumption_term_unknown`. 5. `seen` is in strictly ascending order: `consumption_seen_unordered`. 6. `signer_id` is the `did:key` of `signer_pubkey`: `consumption_signer_not_principal`. ### 6.3 Context At jwp-receipts §3.1 step 6 a verifier is given the objects it holds, with their receipts (§4.1), and, where the claim names a grant or its objects are a room's, the room: its descriptor, its grants and its usage records. It refuses at the first of these that fails: 1. Each of `objects` is the content hash of an object it holds: `consumption_object_unknown`. 2. When `grant` is not `null`: it is the digest of one of the room's grants that live-rooms §4.3 accepts, whose `principal` is `signer_pubkey`, and which is good at the `live.t` of every object of the room the claim names that has one; and every object the claim names is the room's. Otherwise `grant_mismatch`. A grant refused `grant_usage_malformed` holds no terms and is still the principal's. 3. `seen` is exactly the digests of the objects' governing statements: `preference_mismatch`. 4. `usage` answers every vocabulary any governing statement carries: `usage_unanswered`. 5. For each object in the order of `objects`, and each entry in the order of `usage`, the preference of §5.2, with the grant's terms where `grant` names one, is not disallow: `usage_disallowed`. A room whose usage records are refused (§4.2) states no preference: its objects are governed by their own statements alone. A grant the room refuses (live-rooms §4.3) is not one of its grants, and a claim that names it is refused at step 2. ### 6.4 What an accepted claim states An accepted claim states, under the agent's key, that the agent consumed those objects for that use, having seen those statements, under that grant. A verifier reports, for each object, the preference §5.2 gives the claimed use: **allowed**, **unknown**, or **outside** when every term is `null`. A claim refused `usage_disallowed` is still its signer's statement: its signature and chain hold, and what it records is a use the governing statements disallow. A refusal at step 6 is of the use, never of the signature. The publisher, the agent and any party that holds the objects and the claim reach the same verdict. ## 7. The gate ### 7.1 Policy A relay or a gateway MAY require consumption claims before it serves an agent. Whether it does, and how it recognises an agent outside a room, is its own policy; in a room, an agent principal is one whose grant carries terms (§4.3). The policy has two parameters: - `window`, 0 to 16: how many of the most recent groups the gate served the agent may lack a claim. With 0, every object the agent requests is claimed before it is served, as over HTTP, where the agent names each object by its content hash before it fetches it ([http-delivery](http-delivery.md) §5.2). With *n* ≥ 1, the agent claims what it received after it received it, as over MOQT, where a live object's content hash is known only once it is published. - `require_allowed`: whether a use whose preference is unknown or outside is refused. ### 7.2 Decision The gate holds the claims the agent gave it, each verified (§8) and signed by the agent's key (in a room, its grant's principal; elsewhere, the key the gateway authenticates it by): an accepted claim, or one refused `usage_disallowed`, is held; any other is not. It keeps the groups it served the agent, in order, a group over HTTP being one object. For a request, a group of objects, the objects it checks are the requested ones when `window` is 0, and otherwise those of every served group but the last `window`. For each of those objects, in that order, it refuses at the first of these: 1. No held claim names it: `claim_required`. 2. A held claim that names it was refused `usage_disallowed`: `usage_disallowed`. 3. With `require_allowed`, a held claim that names it gives it a preference other than allowed: `usage_not_granted`. Otherwise it serves the request. A gate that refuses tells the agent the reason; over MOQT a relay ends the subscription, and over HTTP a gateway answers `403`. ### 7.3 Over HTTP An agent gives a gateway a claim with its request: the field `WAI-Usage-Claim:` and the claim's canonical form in base64url without padding (RFC 4648 §5). The gateway verifies it, holds it as §7.2 says, and decides. `WAI-Usage-Claim` is proposed for the HTTP Field Name Registry (RFC 9110 §16.3), and is not registered. This revision defines no MOQT carriage for a claim; a relay that gates over MOQT holds the claims its agents gave it over HTTP. ## 8. Verification A verifier is given what each check reads, and nothing more: | check | inputs | outcome | |---|---|---| | a statement (§3.6) | its JSON | accepted with its digest and every term's preference, or refused | | an object (§4.1) | its envelope, object receipt and group receipt | held, with its statement or none, or refused | | a room (§4.2, §4.3) | its descriptor, grants and usage records | each record and grant accepted or refused | | a response (§4.4) | an object and its response fields | consistent, or `header_conflicts` | | a catalog (§4.5) | a catalog entry and the objects of its track | consistent, or refused | | a claim (§6) | the claim, the claim before it of its signer, its objects, and its room | accepted with each object's preference, or refused at a step, with its reason | | a gate (§7) | the policy, the served groups, the claims given, and a request | served, or refused with its reason | A room descriptor or a grant that does not verify is refused as live-rooms §3.4 and §4.3 refuse it; a room with no verified descriptor governs nothing. ## 9. Reasons The codes this extension adds, each with where it is raised: | reason | raised at | |---|---| | `usage_malformed` | §3.6, §4.2 | | `usage_revision_unknown` | §3.6 | | `receipt_invalid` | §4.1 step 1 | | `usage_unbound` | §4.1 step 2 | | `room_usage_unlinked` | §4.2 | | `grant_usage_malformed` | §4.3 | | `header_conflicts` | §4.4 | | `catalog_conflicts` | §4.5 | | `consumption_objects_unordered` | §6.2 rule 1 | | `consumption_subject_mismatch` | §6.2 rule 2 | | `consumption_usage_unordered` | §6.2 rule 3 | | `consumption_term_unknown` | §6.2 rule 4 | | `consumption_seen_unordered` | §6.2 rule 5 | | `consumption_signer_not_principal` | §6.2 rule 6 | | `consumption_object_unknown` | §6.3 step 1 | | `grant_mismatch` | §6.3 step 2 | | `preference_mismatch` | §6.3 step 3 | | `usage_unanswered` | §6.3 step 4 | | `usage_disallowed` | §6.3 step 5, §7.2 | | `claim_required` | §7.2 | | `usage_not_granted` | §7.2 | A room usage record also raises `truncated`, `malformed`, `unknown_version`, `wrong_room`, `bad_key` and `bad_signature`, as live-rooms §3.4 names them, and a claim every code of jwp-receipts §3.1 steps 1 to 5. ## 10. Conformance [`live-conformance/usage/`](../live-conformance/usage/) holds the corpus, written from fixed seeds by `wai_ai_usage_vectors gen` as `vectors.json`. It has: - **Statements**: each vocabulary alone and the three together, with every term's preference; `aipref` values that exercise the Dictionary: a repeated key, parameters, a value that is not a Token, an inner list, a key that is not a label, and every kind of bare item; and one statement refused for each defect of §3.6, among them a value that does not parse and an upper-case key. - **Objects**: a publication whose envelopes carry statements under one group receipt, among them a `WAI2`; the same envelope with its statement removed (`usage_unbound`), and altered (`usage_unbound`); one under a receipt whose signature fails (`receipt_invalid`); and one whose statement does not read. - **Rooms**: a room with two chained usage records and the grants of two agent principals, one of whose terms lift a disallow; a record of another room, one whose signature fails, one out of its chain, and one whose statement does not read; a grant whose terms do not read. - **Responses** and **catalogs**: fields that agree with the signed statement in another spelling, and fields and a catalog that contradict it. - **Claims**: claims accepted with each object's preference — allowed, unknown, outside, and allowed under a grant over a room's disallow — and one refused for each reason of §6.2 and §6.3, among them a use the statement forbids, objects the verifier does not hold, and a signature that fails. - **Gates**: requests served and refused under each policy, for each reason of §7.2. A conforming verifier gives every case the outcome, the reason, the step and everything else the corpus states. `wai_ai_usage_vectors verify` and the test `tests/ai_usage.rs` check that the file regenerates byte for byte, that `wai-rs` gives every case its outcome, that every reason of §9 appears, and that §9 lists exactly the reference's codes. `live-conformance/ai_usage_verify.py` is the second implementation, written from this text: it shares no code with `wai-rs`; it reads rooms with the live-rooms corpus's second implementation (`live_rooms_verify.py`), checks every signature with WAI's strict rule (`claim-conformance/_ed25519.py`) and computes BLAKE3 with `claim-conformance/_blake3.py`, and parses Structured Fields, canonical forms, receipts and claims itself; and it mutates copies of the corpus and requires every copy to fail. ## 11. Security and privacy considerations - A preference is not a security mechanism (`draft-ietf-aipref-vocab-08` §8). What this extension adds is evidence: the publisher's statement bound to the bytes, and the agent's statement of what it did, both signed. Neither makes a use happen or stops one. - Removing an envelope's statement changes its content hash. An object served without its receipts carries an unsigned statement at most, and an agent that requires receipts sees the difference. A party that strips the statement and receipts the object under its own key becomes that object's publisher, under its own key. - An HTTP field and a catalog are written by whoever serves them. A signed statement governs over both (§5.3), so an intermediary that rewrites a field changes nothing a claim is checked against, and a client that compares them sees the rewrite. - A consumption claim is signed by the agent's key and names the agent by that key alone (jwp-receipts §7.1). A key-log confirmation, where the agent's origin publishes one, says who holds the key; without one the claim is the key's statement. - A claim lists the objects an agent consumed. A publisher that holds it learns what the agent read and for what use, which is its purpose; an agent that claims under a gate discloses that to the gate's operator too. - A claim records the use its agent states. A false statement is a signed false statement: a verifier checks it against the objects and the statements, and cannot check what the agent did afterwards. - `tdm-policy` is a URL a statement carries and a verifier never fetches.