Skip to main content

WAI Extension: AI Usage Preferences and Consumption Claims

Mirrored from the canonical text at commit b1681667 ().

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 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/, 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 §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: 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: 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 §3.4, and a grant states its principal’s terms in the attribute ai_usage (live-rooms §4.4).

2. Terms

Integers are little-endian. A digest is SHA-256 (FIPS 180-4), 32 bytes. Ed25519 is RFC 8032’s, verified strictly (SPEC §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:

membervaluesource
aiprefan object: revision, valuedraft-ietf-aipref-vocab-08
cawgan object: revision, entriesTraining and Data Mining Assertion
tdmrepan object: revision, tdm-reservation, tdm-policythe 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

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

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

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 §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 §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:

fieldsizemeaning
magic4WAU1 (ASCII)
version11 in this revision
room32the room id (live-rooms §3.2)
prev32the digest of the record this one replaces; 32 zero bytes for the first
effective8u64: the room-clock instant from which it applies; 0 for the first
statement2 + na u16 length, then the statement’s canonical form, 2 to 4096 bytes
sig64Ed25519 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 §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 §5) whose envelope carries a statement SHOULD send, for a GET or HEAD of it:

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:

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 §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 §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:

membertypemeaning
objectsarray of 1 to 1024 32 B hexthe content hashes of the objects consumed
usagearray of 1 to 3 entriesthe use, in each vocabulary the agent answers
seenarray of 0 to 64 32 B hexthe digests of the statements that governed the objects (§5.1)
grant32 B hex or nullthe 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:

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:

checkinputsoutcome
a statement (§3.6)its JSONaccepted with its digest and every term’s preference, or refused
an object (§4.1)its envelope, object receipt and group receiptheld, with its statement or none, or refused
a room (§4.2, §4.3)its descriptor, grants and usage recordseach record and grant accepted or refused
a response (§4.4)an object and its response fieldsconsistent, or header_conflicts
a catalog (§4.5)a catalog entry and the objects of its trackconsistent, or refused
a claim (§6)the claim, the claim before it of its signer, its objects, and its roomaccepted 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 requestserved, 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:

reasonraised 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/ holds the corpus, written from fixed seeds by wai_ai_usage_vectors gen as vectors.json. It has:

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