wai/age: Privacy Pass admission end to end (one scope per room, the room service as an RFC 9577 origin, ratings over MOQT, the relay's serving rule) #419

Closed
dcharlot wants to merge 0 commits from wai/pp-admission into main
Owner

Privacy Pass admission, implemented end to end, closing the two gaps the age-assurance slice (#409) found: live-rooms §10.4's scopes could not be encoded, and the reference relay neither admitted by token nor held tracks to the age serving rule (age-assurance §5.5).

Stacking. Branched from wai/age-assurance (#409), which is stacked on #398. The chain is linear, with no merges of other PR branches. The base is main. Once #398 and #409 have merged I merge origin/main into this branch. Until then this PR's diff against main includes theirs.

Deployment. Once this lands, the live room has to be redeployed as one unit: the page assets (joule-moq-web/web/ and its two wasm builds), joule-moq-relay and joule-moq-room, all from one commit. The page puts the ratings track property on every PUBLISH, the relay reads WAI_RATING and the grant's age, and the service's grant answers changed. The production room has no [age] table, so it admits as it did before until one is configured. Nothing was deployed.

What is in it

  • Spec.
    • live-rooms §10.4 is rewritten: one scope per room, its bytes, why, how a verifier reads it, and the mapping table.
    • age-assurance gets:
      • §5.1: the room's scope, plus a redemption context fixed for a window of time.
      • §5.2 step 2: origin_info is read according to the binding.
      • §5.3: how long nonces are kept and their bound.
      • §5.5: a session's threshold.
      • §5.6, new: admission by the room's service over HTTP (RFC 9577).
      • §5.7, new: ratings carried over MOQT as WAI_RATING.
      • §9.1, §11 (scope_malformed, scope_mismatch), §12 and §13.
    • Also: the SPEC §5 paragraph, the CHANGELOG, and WAI_RATING in the registry, with the export regenerated.
  • wai-rs age_assurance.
    • room_scope, scope_reads (the draft's §3.2.4 grammar), server_names, check_origin_info, and Binding::{Moqt, Http} threaded through redeem and admit.
    • The corpus gains seven MOQT scope cases and five HTTP binding cases. It also carries RFC 9578 A.2's issuance (nonce, blind, salt, TokenRequest, TokenResponse), which the generator reproduces in its own arithmetic.
  • Python age_assurance_verify.py.
    • Its own scope reader and server-name check.
    • The issuance check: the blinded message is (s·r)^e mod n and the response is s·r mod n.
    • Five new checks that are dropped in turn and caught, and five new mutations.
  • joule-moq rating.
    • Reads a claim exactly as wai-rs does: strict JSON, exactly its members, RFC 3339, JCS, and Ed25519 under WAI's strict rule. Writes one. Computes track digests.
    • tests/rating_vectors.rs holds it to every claim and every serving request in the corpus.
    • GrantHead reads the age attribute as §5.4 does.
  • joule-moq-relay.
    • Takes WAI_RATING from PUBLISH and SUBSCRIBE_OK track properties and from object properties, and holds what §3.6 lets it honour.
    • What it holds is bounded: 64 claims per room or track and 65 536 in all. Claims of rooms it no longer carries are forgotten first.
    • A session's threshold is the greatest age among the grants its SETUP proved.
    • A live object of a group above that threshold is not forwarded.
    • A newly held claim ends that subscriber's streams for the groups it covers, including what is still queued at the relay.
    • A SUBSCRIBE, FETCH or fill is refused below_threshold at its start, or is answered up to the first refused group.
    • New counters: age_refused, age_withheld, ratings_held.
  • joule-moq-room [age]. The RFC 9577 origin of §5.6:
    • A grant request without a token gets 401 with a PrivateToken challenge per key.
    • The context is HMAC-SHA-256 over the room and the window, keyed by a key drawn at start. origin_info is the service's server name.
    • The token is checked with wai-rs's §5.2 against the room's age, and the grant states age.
    • Nonces are kept per window, for two windows, bounded (503 when full, and none is forgotten).
    • Admissions are counted by window, issuer and threshold, and nothing else is kept.
    • Receipts are signed by the clock and suppressed under 50: GET /age/receipt, plus receipts_out as each window closes and when a room ends.
    • The room's rating rides the clock's events track and the grant answer.
  • The page.
    • web/room/privacy-pass.js: parses RFC 9577 challenges and checks them (§2.1.3); obtains tokens by RFC 9578 §6 blind issuance (RFC 9474 §4.2–§4.4) from the issuer's directory (RFC 9578 §4).
    • The page asks again with the token on a 401.
    • It carries the room's rating on every PUBLISH, and with rate its own rating of its video, signed in wasm (ratingClaim).
    • A below_threshold refusal is shown as withheld, not as an error.
    • privacy-pass.test.mjs reproduces RFC 9578 A.2's TokenRequest and token byte for byte.
  • tests/age_rising.rs (relay). A viewer at 18 reads nothing over QUIC with small windows, so the performer's groups queue at the relay. Then the performer rates its whole track 21. The groups still waiting are ended (CANCELLED), the object carrying the claim is not forwarded, and a viewer at 21 still gets every group.
  • e2e age scenario. A test issuer (e2e/issuer.mjs) over the corpus's published keys, plus a relay, a room service with [age] and four pages (details under Checks).
  • CI.
    • Path filters for wai/live-conformance/age/** in joule-code-conformance (joule-moq's rating_vectors) and joule-moq-web (the client test and the issuer).
    • run.sh runs the client's unit test, and age is in the default scenario list.
    • The new Rust tests run where their crates already run all targets.

Design decisions, and why

  1. One scope with a prefix match (live-rooms §10.4). draft-ietf-moq-privacy-pass-auth-03 is still the latest revision: the IETF datatracker listed rev 03 on 2026-10-21, and -04 returns 404.
    • Its scopes<1..2^8-1> holds 255 bytes. Each MoQAuthScope restates its namespace match, which is 69 bytes for [R], so at most three scopes fit. The four-or-more subscribing scopes of §10.2 step 5 need 320 bytes or more.
    • A shorter room id would rename every track of every binding (§6.1).
    • A digest of the room's subscription set is not a match rule the draft defines, so no relay could evaluate it.
    • One scope fits the grammar: SUBSCRIBE_NAMESPACE, SUBSCRIBE, FETCH and TRACK_STATUS on MATCH_PREFIX [R] and MATCH_PREFIX "". That is 78 bytes, with 178 to spare whatever the layers.
    • What it does not carry (max_layer, ratings) is applied from the room's own records.
    • The previous encoding is refused by name, never read again:
      • its three scopes read, and are scope_mismatch;
      • all four, with the overflowing length byte, do not read, and are scope_malformed.
    • AGE's corpus already used this one-scope form. Its bytes stay the same; what is new is that they are now checked.
  2. Admission happens at the room service over HTTP (§5.6), not at the relay over MOQT.
    • A receipt has to be signed by a room key. The service holds those keys; the relay holds none.
    • The service can run wai-rs's reference checks itself. The relay's workspace cannot take wai-rs, whose serde_json features would leak into it.
    • The draft's MOQT hooks (AUTH CHALLENGE, PRIVACY_PASS_TOKEN) have no code points yet, while RFC 9577's PrivateToken HTTP scheme is an RFC.
    • The token is redeemed in place of a plain grant request, and the grant states the threshold (§5.4, already in AGE). The relay then enforces §5.5 from the grant.
  3. Over HTTP, origin_info is RFC 9577's list of server names. A generic client checks it under §2.1.3, so it cannot carry the binary scope. The room is bound by the window context instead.
    • §5.2 step 2 therefore depends on the binding, and the corpus's admission cases gain origin (null for MOQT).
  4. Redemption contexts are fixed per window (RFC 9577 §2.1.1.2), keyed by a key the service draws at start. The window is 600 s by default; windows w and w−1 are accepted.
    • Tokens can be obtained before they are used (§2.1.4).
    • A token is good at one service, for one room, for at most two windows.
    • Nonce memory is bounded: two windows × 65 536 × 32 bytes. When full, the service refuses rather than forget a nonce.
    • A restart turns away every challenge still outstanding.
  5. Ratings ride WAI_RATING (0x3A4D), in MOQT §8.4's application range, so no registration is needed. Publishers carry the room's claims on every PUBLISH, so the relay holds them before a track's first object.
  6. Every grant request in a room that admits by age carries a token, publishers' too. The room claim covers every track, the room's events included, so a publisher at 0 would be served nothing of the room.
  7. The type-0x0001 path is not built. §4.1's policy admits type 2 only, no issuer private key is configured, and VOPRF verification needs one.
  8. The e2e uses a test issuer and real blind issuance instead of a fixed token. A token bound to a window challenge whose context is keyed by the service cannot be fixed in advance. The double-spend check presents an already-spent token a second time.
  9. Owner rule. No consent mechanics were added. Lawful basis stays with the lawful-basis claim (jwp-receipts §2.4.6), as §7.4 already says.

Checks (all on the build host through rb unless marked local)

check result
wai_age_assurance_vectors gen ../live-conformance/age; cargo test --no-default-features --features age_assurance --test age_assurance regenerates byte for byte; 3 passed
cargo test --no-default-features --features age_assurance --lib age_assurance:: 9 passed
cargo test --no-default-features --features live_room --lib live_room:: and --test live_room 7 + 4 passed
cargo test --no-default-features --lib codecs::registry; wai registry export then --check 14 passed; up to date
python3 -I live-conformance/age_assurance_verify.py live-conformance/age 113 cases, 43 mutated copies all fail, 84 of 90 dropped checks caught. The 6 not reached are AGE's notes; all 5 new checks are caught.
every live-conformance/*_verify.py (18) all PASS
cargo test -p joule-moq (default) and --features quic --all-targets; cargo check -p joule-moq --no-default-features 90 / 93 lib + every test target passed; checks
cargo test -p joule-moq --test rating_vectors 2 passed: every claim and every serving request agrees with the corpus
cargo test -p joule-moq-relay --all-targets all passed. age_serving looped 20 more times, all passed, after a race fix (below). age_rising ran 16 times, then 15 more after its read was tightened, all passed
cargo test -p joule-moq-web --lib, and with --features e2ee --lib 9 / 11 passed
joule-moq-room: cargo test --locked --all-targets lib 9, age_admission 1, room_service 2 passed
node --test web/room/privacy-pass.test.mjs (local, also in run.sh) 4 passed
e2e/run.sh --only age 14 of 14 checks ok
e2e/run.sh (every scenario), then --only age --runs 3 every scenario (compose, webtransport, websocket, goaway, dvr, service, e2ee, age): 172 of 172 checks ok; then age 3 of 3 runs, 42 of 42 checks ok
clippy --all-targets on joule-moq, joule-moq-relay and joule-moq-web (and --features e2ee), joule-moq-room, wai-rs --features age_assurance no new notes in touched code. The two this branch raised were fixed (is_multiple_of, and too_many_arguments allowed on the page's claim writer). What remains predates this branch.
cargo metadata --locked --offline for joule-code, joule-moq-room and wai-rs; scripts/check-lockfiles.sh all current. joule-moq-room's lock gains wai-quantum (a monorepo path crate), additions only.
local: check-tests-in-ci, check-workflow-concurrency, check-spec-refs, check-wai-section-refs, check-no-vanity-metrics, test-public-text-checks, check-comparative-prose, check-orphan-sources, check-public-names (with the denylist) all clean. The only names hits are in live_provenance.rs, which this branch does not touch.

The race fix: a first --all-targets run failed age_serving once. The test assumed group 6 could not reach the relay before group 5's object, which carries the claim. Each group rides its own stream, read by its own task, so it can. The relay was right ("from the instant it holds the claim"); the test was wrong. It now waits until the relay holds the claim before sending the next groups. The object that carries the claim is never forwarded below it.

Falsifications (committed first; each rule broken, the guarding test failed, restored)

Each line below is a rule broken by hand; the guarding test failed and the rule was restored.

  • wai-rs:
    • no check_origin_info in §5.2: age_assurance regeneration and outcomes fail.
  • joule-moq:
    • no signature check on a claim: rating_vectors fails.
    • a span's end not read: rating_vectors fails.
  • relay (each fails age_serving unless noted):
    • end_groups removed: age_rising fails (groups queued when the rise was held were still delivered);
    • live objects above the threshold forwarded;
    • a SUBSCRIBE's first group not checked;
    • FETCH not gated;
    • a grant's age not read;
    • a stranger's room claim honoured: the ratings unit test fails.
  • room:
    • the window two back accepted: unit test fails;
    • the nonce bound dropped: unit test fails;
    • a viewer granted without a token: age_admission fails;
    • the token's threshold not written into the grant: age_admission fails;
    • cells under 50 not suppressed: unit test fails.
  • page client:
    • a PSS salt of 32: privacy-pass.test fails;
    • no blinding: privacy-pass.test fails.
  • Python: the verifier's own mutation framework, above.

Logging

Nothing an operator logs links a threshold to a visit. The service writes no line when it admits a token (the receipt's counts are its record), and the relay's below_threshold line names the track and the age, not the session or its threshold (age-assurance §13).

Not done

  • The MOQT binding at the relay (§5.1): AUTH CHALLENGE in its SETUP, a token in AUTHORIZATION TOKEN. The reference stack admits over HTTP instead (decision 2). The draft's code points are TBD.
  • Token type 0x0001 (decision 7).
  • The relay does not attribute object claims. It passes them over, which §3.6 allows.
  • Claims are not persisted across a relay restart. After a restart they are held again when publishers re-PUBLISH.
  • Out of scope: operating an issuer or an attester. The e2e issuer signs whatever it is sent.

🤖 Generated with Claude Code


Closed: Landed in #428 (merge train, merged as aa61ab3b with all 65 checks green).

Privacy Pass admission, implemented end to end, closing the two gaps the age-assurance slice (#409) found: live-rooms §10.4's scopes could not be encoded, and the reference relay neither admitted by token nor held tracks to the age serving rule (age-assurance §5.5). **Stacking.** Branched from `wai/age-assurance` (#409), which is stacked on #398. The chain is linear, with no merges of other PR branches. The base is `main`. Once #398 and #409 have merged I merge `origin/main` into this branch. Until then this PR's diff against main includes theirs. **Deployment.** Once this lands, the live room has to be redeployed as one unit: the page assets (`joule-moq-web/web/` and its two wasm builds), `joule-moq-relay` and `joule-moq-room`, all from one commit. The page puts the `ratings` track property on every PUBLISH, the relay reads `WAI_RATING` and the grant's `age`, and the service's grant answers changed. The production room has no `[age]` table, so it admits as it did before until one is configured. Nothing was deployed. ## What is in it - **Spec.** - live-rooms §10.4 is rewritten: one scope per room, its bytes, why, how a verifier reads it, and the mapping table. - age-assurance gets: - §5.1: the room's scope, plus a redemption context fixed for a window of time. - §5.2 step 2: `origin_info` is read according to the binding. - §5.3: how long nonces are kept and their bound. - §5.5: a session's threshold. - §5.6, new: admission by the room's service over HTTP (RFC 9577). - §5.7, new: ratings carried over MOQT as `WAI_RATING`. - §9.1, §11 (`scope_malformed`, `scope_mismatch`), §12 and §13. - Also: the SPEC §5 paragraph, the CHANGELOG, and `WAI_RATING` in the registry, with the export regenerated. - **wai-rs `age_assurance`.** - `room_scope`, `scope_reads` (the draft's §3.2.4 grammar), `server_names`, `check_origin_info`, and `Binding::{Moqt, Http}` threaded through `redeem` and `admit`. - The corpus gains seven MOQT scope cases and five HTTP binding cases. It also carries RFC 9578 A.2's issuance (nonce, blind, salt, TokenRequest, TokenResponse), which the generator reproduces in its own arithmetic. - **Python `age_assurance_verify.py`.** - Its own scope reader and server-name check. - The issuance check: the blinded message is (s·r)^e mod n and the response is s·r mod n. - Five new checks that are dropped in turn and caught, and five new mutations. - **`joule-moq` `rating`.** - Reads a claim exactly as wai-rs does: strict JSON, exactly its members, RFC 3339, JCS, and Ed25519 under WAI's strict rule. Writes one. Computes track digests. - `tests/rating_vectors.rs` holds it to every claim and every serving request in the corpus. - `GrantHead` reads the `age` attribute as §5.4 does. - **`joule-moq-relay`.** - Takes `WAI_RATING` from PUBLISH and SUBSCRIBE_OK track properties and from object properties, and holds what §3.6 lets it honour. - What it holds is bounded: 64 claims per room or track and 65 536 in all. Claims of rooms it no longer carries are forgotten first. - A session's threshold is the greatest `age` among the grants its SETUP proved. - A live object of a group above that threshold is not forwarded. - A newly held claim ends that subscriber's streams for the groups it covers, including what is still queued at the relay. - A SUBSCRIBE, FETCH or fill is refused `below_threshold` at its start, or is answered up to the first refused group. - New counters: `age_refused`, `age_withheld`, `ratings_held`. - **`joule-moq-room` `[age]`.** The RFC 9577 origin of §5.6: - A grant request without a token gets `401` with a `PrivateToken` challenge per key. - The context is HMAC-SHA-256 over the room and the window, keyed by a key drawn at start. `origin_info` is the service's server name. - The token is checked with wai-rs's §5.2 against the room's age, and the grant states `age`. - Nonces are kept per window, for two windows, bounded (`503` when full, and none is forgotten). - Admissions are counted by window, issuer and threshold, and nothing else is kept. - Receipts are signed by the clock and suppressed under 50: `GET /age/receipt`, plus `receipts_out` as each window closes and when a room ends. - The room's rating rides the clock's events track and the grant answer. - **The page.** - `web/room/privacy-pass.js`: parses RFC 9577 challenges and checks them (§2.1.3); obtains tokens by RFC 9578 §6 blind issuance (RFC 9474 §4.2–§4.4) from the issuer's directory (RFC 9578 §4). - The page asks again with the token on a `401`. - It carries the room's rating on every PUBLISH, and with `rate` its own rating of its video, signed in wasm (`ratingClaim`). - A `below_threshold` refusal is shown as withheld, not as an error. - `privacy-pass.test.mjs` reproduces RFC 9578 A.2's TokenRequest and token byte for byte. - **`tests/age_rising.rs` (relay).** A viewer at 18 reads nothing over QUIC with small windows, so the performer's groups queue at the relay. Then the performer rates its whole track 21. The groups still waiting are ended (`CANCELLED`), the object carrying the claim is not forwarded, and a viewer at 21 still gets every group. - **e2e `age` scenario.** A test issuer (`e2e/issuer.mjs`) over the corpus's published keys, plus a relay, a room service with `[age]` and four pages (details under Checks). - **CI.** - Path filters for `wai/live-conformance/age/**` in joule-code-conformance (joule-moq's `rating_vectors`) and joule-moq-web (the client test and the issuer). - `run.sh` runs the client's unit test, and `age` is in the default scenario list. - The new Rust tests run where their crates already run all targets. ## Design decisions, and why 1. **One scope with a prefix match** (live-rooms §10.4). `draft-ietf-moq-privacy-pass-auth-03` is still the latest revision: the IETF datatracker listed rev 03 on 2026-10-21, and -04 returns 404. - Its `scopes<1..2^8-1>` holds 255 bytes. Each MoQAuthScope restates its namespace match, which is 69 bytes for `[R]`, so at most three scopes fit. The four-or-more subscribing scopes of §10.2 step 5 need 320 bytes or more. - A shorter room id would rename every track of every binding (§6.1). - A digest of the room's subscription set is not a match rule the draft defines, so no relay could evaluate it. - One scope fits the grammar: SUBSCRIBE_NAMESPACE, SUBSCRIBE, FETCH and TRACK_STATUS on MATCH_PREFIX `[R]` and MATCH_PREFIX `""`. That is 78 bytes, with 178 to spare whatever the layers. - What it does not carry (`max_layer`, ratings) is applied from the room's own records. - The previous encoding is refused by name, never read again: - its three scopes read, and are `scope_mismatch`; - all four, with the overflowing length byte, do not read, and are `scope_malformed`. - AGE's corpus already used this one-scope form. Its bytes stay the same; what is new is that they are now checked. 2. **Admission happens at the room service over HTTP (§5.6), not at the relay over MOQT.** - A receipt has to be signed by a room key. The service holds those keys; the relay holds none. - The service can run wai-rs's reference checks itself. The relay's workspace cannot take wai-rs, whose `serde_json` features would leak into it. - The draft's MOQT hooks (AUTH CHALLENGE, PRIVACY_PASS_TOKEN) have no code points yet, while RFC 9577's `PrivateToken` HTTP scheme is an RFC. - The token is redeemed in place of a plain grant request, and the grant states the threshold (§5.4, already in AGE). The relay then enforces §5.5 from the grant. 3. **Over HTTP, `origin_info` is RFC 9577's list of server names.** A generic client checks it under §2.1.3, so it cannot carry the binary scope. The room is bound by the window context instead. - §5.2 step 2 therefore depends on the binding, and the corpus's admission cases gain `origin` (`null` for MOQT). 4. **Redemption contexts are fixed per window** (RFC 9577 §2.1.1.2), keyed by a key the service draws at start. The window is 600 s by default; windows w and w−1 are accepted. - Tokens can be obtained before they are used (§2.1.4). - A token is good at one service, for one room, for at most two windows. - Nonce memory is bounded: two windows × 65 536 × 32 bytes. When full, the service refuses rather than forget a nonce. - A restart turns away every challenge still outstanding. 5. **Ratings ride `WAI_RATING` (`0x3A4D`),** in MOQT §8.4's application range, so no registration is needed. Publishers carry the room's claims on every PUBLISH, so the relay holds them before a track's first object. 6. **Every grant request in a room that admits by age carries a token, publishers' too.** The room claim covers every track, the room's events included, so a publisher at 0 would be served nothing of the room. 7. **The type-0x0001 path is not built.** §4.1's policy admits type 2 only, no issuer private key is configured, and VOPRF verification needs one. 8. **The e2e uses a test issuer and real blind issuance instead of a fixed token.** A token bound to a window challenge whose context is keyed by the service cannot be fixed in advance. The double-spend check presents an already-spent token a second time. 9. **Owner rule.** No consent mechanics were added. Lawful basis stays with the `lawful-basis` claim (jwp-receipts §2.4.6), as §7.4 already says. ## Checks (all on the build host through `rb` unless marked local) | check | result | |---|---| | `wai_age_assurance_vectors gen ../live-conformance/age`; `cargo test --no-default-features --features age_assurance --test age_assurance` | regenerates byte for byte; 3 passed | | `cargo test --no-default-features --features age_assurance --lib age_assurance::` | 9 passed | | `cargo test --no-default-features --features live_room --lib live_room::` and `--test live_room` | 7 + 4 passed | | `cargo test --no-default-features --lib codecs::registry`; `wai registry export` then `--check` | 14 passed; up to date | | `python3 -I live-conformance/age_assurance_verify.py live-conformance/age` | 113 cases, 43 mutated copies all fail, 84 of 90 dropped checks caught. The 6 not reached are AGE's notes; all 5 new checks are caught. | | every `live-conformance/*_verify.py` (18) | all PASS | | `cargo test -p joule-moq` (default) and `--features quic --all-targets`; `cargo check -p joule-moq --no-default-features` | 90 / 93 lib + every test target passed; checks | | `cargo test -p joule-moq --test rating_vectors` | 2 passed: every claim and every serving request agrees with the corpus | | `cargo test -p joule-moq-relay --all-targets` | all passed. `age_serving` looped 20 more times, all passed, after a race fix (below). `age_rising` ran 16 times, then 15 more after its read was tightened, all passed | | `cargo test -p joule-moq-web --lib`, and with `--features e2ee --lib` | 9 / 11 passed | | `joule-moq-room`: `cargo test --locked --all-targets` | lib 9, `age_admission` 1, `room_service` 2 passed | | `node --test web/room/privacy-pass.test.mjs` (local, also in run.sh) | 4 passed | | `e2e/run.sh --only age` | 14 of 14 checks ok | | `e2e/run.sh` (every scenario), then `--only age --runs 3` | every scenario (compose, webtransport, websocket, goaway, dvr, service, e2ee, age): 172 of 172 checks ok; then age 3 of 3 runs, 42 of 42 checks ok | | clippy `--all-targets` on joule-moq, joule-moq-relay and joule-moq-web (and `--features e2ee`), joule-moq-room, wai-rs `--features age_assurance` | no new notes in touched code. The two this branch raised were fixed (`is_multiple_of`, and `too_many_arguments` allowed on the page's claim writer). What remains predates this branch. | | `cargo metadata --locked --offline` for joule-code, joule-moq-room and wai-rs; `scripts/check-lockfiles.sh` | all current. joule-moq-room's lock gains `wai-quantum` (a monorepo path crate), additions only. | | local: check-tests-in-ci, check-workflow-concurrency, check-spec-refs, check-wai-section-refs, check-no-vanity-metrics, test-public-text-checks, check-comparative-prose, check-orphan-sources, check-public-names (with the denylist) | all clean. The only names hits are in `live_provenance.rs`, which this branch does not touch. | The race fix: a first `--all-targets` run failed `age_serving` once. The test assumed group 6 could not reach the relay before group 5's object, which carries the claim. Each group rides its own stream, read by its own task, so it can. The relay was right ("from the instant it holds the claim"); the test was wrong. It now waits until the relay holds the claim before sending the next groups. The object that carries the claim is never forwarded below it. ## Falsifications (committed first; each rule broken, the guarding test failed, restored) Each line below is a rule broken by hand; the guarding test failed and the rule was restored. - **wai-rs:** - no `check_origin_info` in §5.2: `age_assurance` regeneration and outcomes fail. - **joule-moq:** - no signature check on a claim: `rating_vectors` fails. - a span's end not read: `rating_vectors` fails. - **relay** (each fails `age_serving` unless noted): - `end_groups` removed: `age_rising` fails (groups queued when the rise was held were still delivered); - live objects above the threshold forwarded; - a SUBSCRIBE's first group not checked; - FETCH not gated; - a grant's `age` not read; - a stranger's room claim honoured: the `ratings` unit test fails. - **room:** - the window two back accepted: unit test fails; - the nonce bound dropped: unit test fails; - a viewer granted without a token: `age_admission` fails; - the token's threshold not written into the grant: `age_admission` fails; - cells under 50 not suppressed: unit test fails. - **page client:** - a PSS salt of 32: `privacy-pass.test` fails; - no blinding: `privacy-pass.test` fails. - **Python:** the verifier's own mutation framework, above. ## Logging Nothing an operator logs links a threshold to a visit. The service writes no line when it admits a token (the receipt's counts are its record), and the relay's `below_threshold` line names the track and the age, not the session or its threshold (age-assurance §13). ## Not done - **The MOQT binding at the relay** (§5.1): AUTH CHALLENGE in its SETUP, a token in AUTHORIZATION TOKEN. The reference stack admits over HTTP instead (decision 2). The draft's code points are TBD. - **Token type 0x0001** (decision 7). - **The relay does not attribute object claims.** It passes them over, which §3.6 allows. - **Claims are not persisted across a relay restart.** After a restart they are held again when publishers re-PUBLISH. - **Out of scope:** operating an issuer or an attester. The e2e issuer signs whatever it is sent. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- **Closed:** Landed in #428 (merge train, merged as aa61ab3b with all 65 checks green).
A player's signed claim (WPC1) that it presented one impression of a live-ads
break: the creative's objects it presented, spans of its media time with the
fraction of pixels in view and audibility, the geometry they were measured over,
a rendered-frame count, and an optional RFC 9577 token bound to the impression
through its challenge and verified as RFC 9578 §6.4 does for type 0x0002.
An ad policy of version 2 requires claims (and optionally an attestation and an
audible run) under the video or cross-media viewability rule; the room
verifier's spend-equivalence then counts only impressions whose claim verifies.
The corpus live-conformance/presentation/ is generated from fixed seeds.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
extensions/ad-presentation.md defines the WPC1 claim, the session a session key
names, the creative digest, the video and cross-media viewability rules as their
texts state them, the quartile, the RFC 9577 token bound to one impression and
verified as RFC 9578 §6.4 has it, the presentation fields of a WAP1 policy of
version 2, and each claim's verdict; live-ads.md takes the version and the spend
that counts. ad_presentation_verify.py is written from the text, verifies RFC
9578's own vector, gives every claim the corpus's verdict, and catches every
mutated copy and every check dropped from itself. The corpus gains a signed
claim that breaks a span rule.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
The registry lists the record and the WPC1 magic (the export regenerated), SPEC
§5 describes the claim and the billing rule, and the CHANGELOG records them.
joule-moq-web gains web/ads/presentation.js, a span recorder over a video
element and the claim's writer and signer on WebCrypto's Ed25519, whose node
tests turn observation sequences into the corpus's claims byte for byte; no page
plays ads yet. wai-conformance runs the module's tests, the corpus test,
ad_presentation_verify.py and the producer's tests; joule-moq-web's e2e script
runs the producer's tests with the page's other unit tests.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Merge remote-tracking branch 'origin/main' into wai/ad-presentation
Some checks failed
joule-moq-room room service / joule-moq-room, every target (pull_request) Successful in 1m43s
WAI conformance / quantum core and corpus under wasm32-wasip2 (wasmtime) (pull_request) Successful in 2m18s
WAI conformance / cross-arch byte-equality · linux (pull_request) Successful in 26s
WAI energy receipts / energy receipts · linux (pull_request) Successful in 10m2s
wai-ops-gpu integer operators on GPUs / builds · no adapter is not a pass (linux) (pull_request) Successful in 2m5s
wai-pack / wai-pack · linux (pull_request) Successful in 1m25s
WAI conformance / cargo-class corpora · linux (pull_request) Successful in 19m14s
wai-quantum studio and desktop / studio · browser (pull_request) Successful in 3m2s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Successful in 3m49s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 5m34s
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Successful in 16s
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Successful in 13s
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Successful in 29s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 1m52s
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Failing after 14m39s
wai-pack / wai-pack · linux-arm64 (pull_request) Successful in 4m47s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Successful in 11m17s
wai-pack / wai-pack · the floor through a media prober (pull_request) Successful in 53s
wai-pack / wai-pack · dependency licences (pull_request) Successful in 1m30s
wai-pack / wai-pack · independent verifier (Python) (pull_request) Successful in 4m27s
wai-quantum studio and desktop / desktop app · macos (pull_request) Has been cancelled
wai-pack / wai-pack · macos (pull_request) Has been cancelled
wai-ops-gpu integer operators on GPUs / Metal GPU and browser WebGPU (macos) (pull_request) Has been cancelled
WAI energy receipts / energy receipts · macos (pull_request) Has been cancelled
standards workspaces build + test / macOS energy backends (pull_request) Has been cancelled
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (pull_request) Has been cancelled
WAI conformance / cargo-class corpora · macos (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 / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Has been cancelled
fe66f6d2de
# Conflicts:
#	.github/workflows/wai-conformance.yml
#	wai/CHANGELOG.md
#	wai/REGISTRY.md
#	wai/SPEC.md
#	wai/live-conformance/README.md
#	wai/wai-rs/src/codecs/registry.rs
Merge origin/main into wai/ad-presentation (IETF draft, timing-test fix)
All checks were successful
WAI conformance / quantum core and corpus under wasm32-wasip2 (wasmtime) (pull_request) Successful in 2m0s
WAI conformance / cross-arch byte-equality · linux (pull_request) Successful in 32s
WAI energy receipts / energy receipts · linux (pull_request) Successful in 10m19s
wai-ops-gpu integer operators on GPUs / builds · no adapter is not a pass (linux) (pull_request) Successful in 1m44s
WAI conformance / cargo-class corpora · linux (pull_request) Successful in 18m58s
wai-pack / wai-pack · linux (pull_request) Successful in 1m34s
wai-quantum studio and desktop / studio · browser (pull_request) Successful in 3m8s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Successful in 3m59s
joule-moq-room room service / joule-moq-room, every target (pull_request) Successful in 1m7s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 4m41s
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Successful in 2m24s
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Successful in 3m22s
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Successful in 1m39s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 5m20s
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Successful in 31m57s
wai-pack / wai-pack · linux-arm64 (pull_request) Successful in 4m14s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Successful in 15m9s
wai-pack / wai-pack · the floor through a media prober (pull_request) Successful in 1m48s
wai-pack / wai-pack · independent verifier (Python) (pull_request) Successful in 10m17s
wai-pack / wai-pack · dependency licences (pull_request) Successful in 2m55s
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (pull_request) Successful in 8m44s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 50m59s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 9m8s
WAI conformance / cargo-class corpora · macos (pull_request) Successful in 14m52s
WAI conformance / cross-arch byte-equality · macos (pull_request) Successful in 19s
WAI conformance / codec wrappers · fuzz smoke, text decode limits, C entry points, provenance, the semantic path (pull_request) Successful in 1m20s
WAI energy receipts / energy receipts · macos (pull_request) Successful in 6m59s
wai-ops-gpu integer operators on GPUs / Metal GPU and browser WebGPU (macos) (pull_request) Successful in 3m20s
wai-pack / wai-pack · macos (pull_request) Successful in 1m17s
wai-quantum studio and desktop / desktop app · macos (pull_request) Successful in 2m54s
b2f22ea8c4
Co-Authored-By: Claude Opus 5.5 <[email protected]>
extensions/age-assurance.md registers the record: wai-rating claims for an
object, a track, a span or a room under named schemes, each stating the
minimum age its value means, carried through every derivation (a scheme an
input is rated under is never dropped or lowered); a room's wai-age-policy of
Privacy Pass issuers with one key per threshold; admission by a token of RFC
9578's publicly verifiable type, the relay learning only the issuer and the
threshold; the serving rule, a span's rise holding from its first group; the
sink's age state and predicate with no return path; wai-age-admissions
receipts, every count under 50 suppressed and no member a session could ride
in; and the minors' profile over a descriptor's role bitmaps.

wai-rs gains the age_assurance module (feature age_assurance), reusing the
ad-presentation module's RSASSA-PSS and token reader, and the corpus
live-conformance/age/ written by wai_age_assurance_vectors from fixed seeds,
its issuer keys the published test keys of RFC 9578 A.2, RFC 9500 and RFC
7517.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
The independent verifier's reading of the text found that a grant may narrow a
publishing role to publish nothing and keep its chat: §8.1 gains rule 4 over
grants, held at the first record that puts a minor's publishing grant into
effect. §2, §3.4, §3.6, §5.2, §6.2, §7.3 and §9.1 now pin the reading orders
and the attribution of claims, and the corpus covers the rules it did not
reach.

live-recording §4.4 step 5 refuses a package of a room whose recording policy
has mode 0 (recording_not_permitted); its corpus gains room_never_recorded and
live_record_verify.py the check. joule-moq's grant head reads the minor
attribute, and joule-moq-relay's DVR store keeps nothing of a room once a
session proves a minor's publishing grant of it, as it does for a sealed room,
across a restart; tests/dvr.rs shows it.

live-conformance/age_assurance_verify.py is the second implementation, written
from the text: it shares no code with wai-rs, verifies every RSASSA-PSS in its
own arithmetic, and catches every mutated copy and each of its own dropped
checks. The registry export, SPEC §5, the CHANGELOG, the corpus exclusion
reason and the wai-conformance step follow.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
age_assurance: a track claim's authorisation as one match guard (clippy collapsible_match)
All checks were successful
joule-moq-room room service / joule-moq-room, every target (pull_request) Successful in 1m16s
WAI conformance / quantum core and corpus under wasm32-wasip2 (wasmtime) (pull_request) Successful in 2m3s
WAI conformance / cross-arch byte-equality · linux (pull_request) Successful in 26s
WAI energy receipts / energy receipts · linux (pull_request) Successful in 10m22s
wai-ops-gpu integer operators on GPUs / builds · no adapter is not a pass (linux) (pull_request) Successful in 1m52s
WAI conformance / cargo-class corpora · linux (pull_request) Successful in 19m22s
wai-pack / wai-pack · linux (pull_request) Successful in 1m20s
wai-quantum studio and desktop / studio · browser (pull_request) Successful in 2m39s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Successful in 4m15s
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (pull_request) Successful in 8m30s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 3m33s
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Successful in 16s
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Successful in 10s
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Successful in 24s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 2m11s
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Successful in 16m28s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Successful in 7m50s
wai-pack / wai-pack · linux-arm64 (pull_request) Successful in 1m21s
wai-pack / wai-pack · the floor through a media prober (pull_request) Successful in 23s
wai-pack / wai-pack · dependency licences (pull_request) Successful in 1m18s
wai-pack / wai-pack · independent verifier (Python) (pull_request) Successful in 4m9s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 31m37s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 7m37s
WAI conformance / cargo-class corpora · macos (pull_request) Successful in 12m56s
WAI conformance / cross-arch byte-equality · macos (pull_request) Successful in 18s
WAI conformance / codec wrappers · fuzz smoke, text decode limits, C entry points, provenance, the semantic path (pull_request) Successful in 42s
WAI energy receipts / energy receipts · macos (pull_request) Successful in 4m15s
wai-ops-gpu integer operators on GPUs / Metal GPU and browser WebGPU (macos) (pull_request) Successful in 2m26s
wai-pack / wai-pack · macos (pull_request) Successful in 1m5s
wai-quantum studio and desktop / desktop app · macos (pull_request) Successful in 2m11s
101d31ee01
Co-Authored-By: Claude Opus 5.5 <[email protected]>
live-rooms §10.4's subscribing scopes did not fit the 255 bytes draft-ietf-moq-privacy-pass-auth-03
gives one MoQAuthorizationInfo: the room's scope is now one MoQAuthScope, the subscribing
actions on MATCH_PREFIX of [R] and on every track name, 78 bytes. A challenge's origin_info is
read by its binding: over MOQT the room's scope byte for byte (scope_malformed, scope_mismatch,
the previous revision's scopes among them); over HTTP RFC 9577's list of server names holding
the service's own. The corpus gains the scope and HTTP cases and RFC 9578 A.2's issuance, which
both verifiers check.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
joule-moq reads a rating claim as wai-rs does (strict JSON, exactly its members, JCS,
Ed25519 under WAI's strict rule), writes one, and agrees with the age corpus on every claim
and every serving request. A grant head reads its age attribute (§5.4).

The relay takes claims from PUBLISH and SUBSCRIBE_OK track properties and from object
properties, holds what §3.6 lets it honour, bounded per room and track and in all, and serves
each session by its proven grants' threshold: a live object of a group above it is not
forwarded, the streams of groups a newly held claim raises are ended, and a SUBSCRIBE, FETCH
or fill is refused below_threshold at its start or answered up to the first refused group.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
With [age], each room has a policy and a room rating signed by its authority. A viewer's grant
request without a token is answered 401 with a PrivateToken challenge per key (RFC 9577
§2.1.2), its redemption context HMAC-SHA-256 over the room and a window under a key drawn at
start, its origin_info the service's server name; a token is checked by wai-rs's §5.2 against
the room's age and the grant states its threshold in age (§5.4). Nonces are kept for the two
windows accepted and bounded per window (503 past it, nothing forgotten). Admissions are
counted by window, issuer and threshold alone; GET /age/receipt and receipts_out give the
clock-signed wai-age-admissions receipts, suppressed under 50. The room's rating rides the
clock's events track and the grant's room view, for publishers to carry.

The lock gains wai-quantum (a path crate of the monorepo), by additions only.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
web/room/privacy-pass.js reads RFC 9577's challenges, checks them (§2.1.3), and obtains a
token by blind issuance (RFC 9474 §4.2 to §4.4) from the issuer's directory (RFC 9578 §4); it
reproduces RFC 9578 A.2's TokenRequest and token from the corpus's vector. The page asks
again with the token on a 401, carries the room's rating (and, with rate=, its own rating of
its video, signed in wasm) on every PUBLISH, and records a relay's below_threshold refusal
as withheld. The service now challenges publishers as it does viewers: every track of a rated
room is held to its rating, the room's events among them.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
age_serving: groups after the claim's object are sent once the relay holds it (each group is read by a task of its own); the corpus paragraph of §12 states each scope case's reason
Some checks failed
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 (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
WAI energy receipts / does this change touch it (pull_request) Has been cancelled
WAI energy receipts / energy receipts · linux (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-ops-gpu integer operators on GPUs / does this change touch it (pull_request) Has been cancelled
wai-ops-gpu integer operators on GPUs / builds · no adapter is not a pass (linux) (pull_request) Has been cancelled
wai-ops-gpu integer operators on GPUs / Metal GPU and browser WebGPU (macos) (pull_request) Has been cancelled
wai-pack / wai-pack · does this change touch it (pull_request) Has been cancelled
wai-pack / wai-pack · linux (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-quantum studio and desktop / does this change touch it (pull_request) Has been cancelled
wai-quantum studio and desktop / studio · browser (pull_request) Has been cancelled
wai-quantum studio and desktop / desktop app · macos (pull_request) Has been cancelled
wai-web reach builds / wai-web reach · does this change touch it (pull_request) Has been cancelled
wai-web reach builds / wai-web reach builds · the package of record, every output as recorded (pull_request) Has been cancelled
wai-web reach builds / wai-web reach builds · this tree's wai-web, every rung on its engine line (pull_request) Has been cancelled
wai-web wasm32 / does this change touch it (pull_request) Has been cancelled
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Has been cancelled
94444cfbf6
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
age_rising: the slow viewer reads until its receipts are whole, not to the bound
Some checks failed
joule-moq-room room service / joule-moq-room, every target (pull_request) Successful in 1m50s
WAI conformance / quantum core and corpus under wasm32-wasip2 (wasmtime) (pull_request) Successful in 2m16s
WAI conformance / cross-arch byte-equality · linux (pull_request) Successful in 32s
WAI energy receipts / energy receipts · linux (pull_request) Successful in 10m4s
wai-pack / wai-pack · linux (pull_request) Successful in 1m26s
wai-ops-gpu integer operators on GPUs / builds · no adapter is not a pass (linux) (pull_request) Successful in 1m51s
WAI conformance / cargo-class corpora · linux (pull_request) Successful in 19m11s
wai-quantum studio and desktop / studio · browser (pull_request) Successful in 2m26s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Successful in 3m52s
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Successful in 18s
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Successful in 11s
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Successful in 24s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 2m3s
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Successful in 17m40s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 4m55s
wai-pack / wai-pack · linux-arm64 (pull_request) Successful in 3m3s
wai-pack / wai-pack · independent verifier (Python) (pull_request) Successful in 8m11s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Successful in 11m40s
wai-pack / wai-pack · the floor through a media prober (pull_request) Successful in 46s
wai-pack / wai-pack · dependency licences (pull_request) Successful in 1m36s
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (pull_request) Successful in 8m51s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 37m14s
wai-quantum studio and desktop / desktop app · macos (pull_request) Has been cancelled
wai-pack / wai-pack · macos (pull_request) Has been cancelled
wai-ops-gpu integer operators on GPUs / Metal GPU and browser WebGPU (macos) (pull_request) Has been cancelled
WAI energy receipts / energy receipts · macos (pull_request) Has been cancelled
WAI conformance / cargo-class corpora · macos (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
standards workspaces build + test / macOS energy backends (pull_request) Has been cancelled
e093ae80d8
Co-Authored-By: Claude Opus 5.5 <[email protected]>
dcharlot closed this pull request 2026-10-22 12:02:20 -04:00
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 5s
wai-web reach builds / wai-web reach · does this change touch it (pull_request) Successful in 5s
joule-code conformance gate / Build + round-trip C1-C4 vectors (pull_request) Successful in 2m50s
wai-web reach builds / wai-web reach builds · the package of record, every output as recorded (pull_request) Successful in 2m43s
wai-web reach builds / wai-web reach builds · this tree's wai-web, every rung on its engine line (pull_request) Successful in 4m26s
joule-code conformance gate / joule-moq-ingest, every target (pull_request) Successful in 1m46s
joule-code conformance gate / joule-moq-relay, every target (pull_request) Successful in 1m51s
repository checks / lockfiles, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 1m13s
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 6s
WAI conformance / does this change touch it (pull_request) Successful in 6s
WAI energy receipts / does this change touch it (pull_request) Successful in 5s
wai-pack / wai-pack · does this change touch it (pull_request) Successful in 5s
wai-ops-gpu integer operators on GPUs / does this change touch it (pull_request) Successful in 6s
wai-quantum studio and desktop / does this change touch it (pull_request) Successful in 5s
wai-web wasm32 / does this change touch it (pull_request) Successful in 4s
joule-moq-room room service / joule-moq-room, every target (pull_request) Successful in 1m50s
WAI conformance / quantum core and corpus under wasm32-wasip2 (wasmtime) (pull_request) Successful in 2m16s
WAI conformance / cross-arch byte-equality · linux (pull_request) Successful in 32s
WAI energy receipts / energy receipts · linux (pull_request) Successful in 10m4s
wai-pack / wai-pack · linux (pull_request) Successful in 1m26s
wai-ops-gpu integer operators on GPUs / builds · no adapter is not a pass (linux) (pull_request) Successful in 1m51s
WAI conformance / cargo-class corpora · linux (pull_request) Successful in 19m11s
wai-quantum studio and desktop / studio · browser (pull_request) Successful in 2m26s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Successful in 3m52s
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Successful in 18s
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Successful in 11s
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Successful in 24s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 2m3s
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Successful in 17m40s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 4m55s
wai-pack / wai-pack · linux-arm64 (pull_request) Successful in 3m3s
wai-pack / wai-pack · independent verifier (Python) (pull_request) Successful in 8m11s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Successful in 11m40s
wai-pack / wai-pack · the floor through a media prober (pull_request) Successful in 46s
wai-pack / wai-pack · dependency licences (pull_request) Successful in 1m36s
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (pull_request) Successful in 8m51s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 37m14s
wai-quantum studio and desktop / desktop app · macos (pull_request) Has been cancelled
wai-pack / wai-pack · macos (pull_request) Has been cancelled
wai-ops-gpu integer operators on GPUs / Metal GPU and browser WebGPU (macos) (pull_request) Has been cancelled
WAI energy receipts / energy receipts · macos (pull_request) Has been cancelled
WAI conformance / cargo-class corpora · macos (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
standards workspaces build + test / macOS energy backends (pull_request) Has been cancelled

Pull request closed

Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Transaction-Science/open-standards!419
No description provided.