Some checks failed
joule-moq-room room service / joule-moq-room · does this change touch it (pull_request) Successful in 6s
joule-moq-web browser client / joule-moq-web · does this change touch it (pull_request) Successful in 6s
wai-web reach builds / wai-web reach · does this change touch it (pull_request) Successful in 6s
wai-web reach builds / wai-web reach builds · the package of record, every output as recorded (pull_request) Successful in 2m46s
wai-web reach builds / wai-web reach builds · this tree's wai-web, every rung on its engine line (pull_request) Successful in 4m46s
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 14s
WAI conformance / does this change touch it (pull_request) Successful in 13s
WAI energy receipts / does this change touch it (pull_request) Successful in 9s
WAI HTTP delivery / WAI HTTP · does this change touch it (pull_request) Successful in 12s
WAI neural sink gates / does this change touch it (pull_request) Successful in 14s
wai-pack / wai-pack · does this change touch it (pull_request) Successful in 11s
repository checks / lockfiles, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 1m37s
wai-quantum studio and desktop / does this change touch it (pull_request) Successful in 20s
wai-web wasm32 / does this change touch it (pull_request) Successful in 22s
WAI conformance / quantum core and corpus under wasm32-wasip2 (wasmtime) (pull_request) Successful in 3m0s
WAI conformance / cross-arch byte-equality · linux (pull_request) Successful in 30s
WAI energy receipts / energy receipts · linux (pull_request) Successful in 10m5s
WAI neural sink gates / model-free refusals of the neural sink (pull_request) Successful in 1m31s
WAI conformance / cargo-class corpora · linux (pull_request) Successful in 18m15s
wai-pack / wai-pack · linux (pull_request) Successful in 1m47s
wai-quantum studio and desktop / studio · browser (pull_request) Successful in 2m38s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Successful in 4m13s
wai-quantum studio and desktop / desktop app · macos (pull_request) Has been cancelled
wai-pack / wai-pack · linux-arm64 (pull_request) Has been cancelled
wai-pack / wai-pack · macos (pull_request) Has been cancelled
wai-pack / wai-pack · independent verifier (Python) (pull_request) Has been cancelled
wai-pack / wai-pack · the floor through a media prober (pull_request) Has been cancelled
wai-pack / wai-pack · dependency licences (pull_request) Has been cancelled
WAI HTTP delivery / HTTP corpus · falsify mutations 0/3 (pull_request) Has been cancelled
WAI HTTP delivery / HTTP corpus · falsify mutations 1/3 (pull_request) Has been cancelled
WAI HTTP delivery / HTTP corpus · falsify mutations 2/3 (pull_request) Has been cancelled
WAI HTTP delivery / HTTP corpus · falsify patches 0/3 (pull_request) Has been cancelled
WAI HTTP delivery / HTTP corpus · falsify patches 1/3 (pull_request) Has been cancelled
WAI HTTP delivery / HTTP corpus · falsify patches 2/3 (pull_request) Has been cancelled
WAI HTTP delivery / HTTP corpus · every patch and mutation in exactly one shard (pull_request) Has been cancelled
WAI HTTP delivery / HTTP corpus · verified both ways, and the differential (pull_request) Has been cancelled
WAI HTTP delivery / cache rules · controls and labelled caches (pull_request) Has been cancelled
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Has been cancelled
WAI energy receipts / energy receipts · macos (pull_request) Has been cancelled
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Has been cancelled
WAI conformance / cargo-class corpora · macos (pull_request) Has been cancelled
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Has been cancelled
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Has been cancelled
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Has been cancelled
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Has been cancelled
WAI conformance / cross-arch byte-equality · macos (pull_request) Has been cancelled
WAI conformance / codec wrappers · fuzz smoke, text decode limits, C entry points, provenance, the semantic path (pull_request) Has been cancelled
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Has been cancelled
standards workspaces build + test / macOS energy backends (pull_request) Has been cancelled
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Has been cancelled
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (pull_request) Has been cancelled
joule-moq-room room service / joule-moq-room, every target (pull_request) Has been cancelled
Co-Authored-By: Claude Opus 5.5 <[email protected]>
545 lines
33 KiB
Markdown
545 lines
33 KiB
Markdown
# 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.
|