live room train D: co-watch rooms, the room's own service under age rules, same-origin room endpoints (#432) #451
Merged
dcharlot
merged 21 commits from 2026-10-25 00:42:25 -04:00
train/live-room-d into main 2026-10-24
relay tests: wait on what the relay holds, not on fixed sleeps and windows
All checks were successful
WAI conformance / quantum core and corpus under wasm32-wasip2 (wasmtime) (pull_request) Successful in 2m9s
WAI conformance / cross-arch byte-equality · linux (pull_request) Successful in 28s
WAI energy receipts / energy receipts · linux (pull_request) Successful in 11m8s
WAI conformance / cargo-class corpora · linux (pull_request) Successful in 19m52s
wai-ops-gpu integer operators on GPUs / builds · no adapter is not a pass (linux) (pull_request) Successful in 2m18s
wai-pack / wai-pack · linux (pull_request) Successful in 1m44s
wai-quantum studio and desktop / studio · browser (pull_request) Successful in 2m55s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Successful in 6m11s
joule-moq-room room service / joule-moq-room, every target (pull_request) Successful in 1m38s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 3m48s
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Successful in 15s
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Successful in 12s
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Successful in 26s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 1m30s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Successful in 7m19s
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (pull_request) Successful in 10m55s
wai-pack / wai-pack · linux-arm64 (pull_request) Successful in 1m12s
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Successful in 15m12s
wai-pack / wai-pack · the floor through a media prober (pull_request) Successful in 18s
wai-pack / wai-pack · dependency licences (pull_request) Successful in 1m10s
wai-pack / wai-pack · independent verifier (Python) (pull_request) Successful in 3m51s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 8m51s
WAI conformance / cargo-class corpora · macos (pull_request) Successful in 13m38s
WAI conformance / cross-arch byte-equality · macos (pull_request) Successful in 21s
WAI conformance / codec wrappers · fuzz smoke, text decode limits, C entry points, provenance, the semantic path (pull_request) Successful in 49s
WAI energy receipts / energy receipts · macos (pull_request) Successful in 4m51s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 41m53s
wai-ops-gpu integer operators on GPUs / Metal GPU and browser WebGPU (macos) (pull_request) Successful in 5m9s
wai-pack / wai-pack · macos (pull_request) Successful in 1m12s
wai-quantum studio and desktop / desktop app · macos (pull_request) Successful in 2m23s
floor.rs `the_relay_refuses_invalid_audience_inputs` failed under load (2 of 10 runs at load ~40-70), and not for want of readiness: the window was held and the subscriptions live. The relay forwarded the member's *second* vote and refused the first `input_over_limit`. Each group rides a stream of its own, which the relay reads in a task of its own, so two votes sent back to back can be taken in either order, and the limit counts them in the order taken. - floor.rs (inputs): the fixed 300 ms sleep becomes `Floor::holds_window` (new, minimal), and the second vote is sent once the watcher has the first, which proves its subscription live. The refusals are waited for by `refused_inputs()`, then a second of quiet catches anything forwarded wrongly. - floor.rs (the floor): the 3 s collect windows wait for the object in turn and for `refused()`, then a second of quiet. - age_serving, age_rising, age_room_service: each collect waits for every group it expects, since the groups arrive on their own streams in any order, rather than for the last group alone. Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-22
Merge remote-tracking branch 'origin/main' into train/live-room-d
Some checks failed
wai-pack / wai-pack · linux-arm64 (pull_request) Successful in 1m24s
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Successful in 23m59s
joule-moq-room room service / joule-moq-room · does this change touch it (pull_request) Successful in 8s
wai-pack / wai-pack · the floor through a media prober (pull_request) Successful in 37s
joule-moq-web browser client / joule-moq-web · does this change touch it (pull_request) Successful in 9s
wai-web reach builds / wai-web reach · does this change touch it (pull_request) Successful in 7s
wai-pack / wai-pack · dependency licences (pull_request) Successful in 1m44s
joule-code conformance gate / Build + round-trip C1-C4 vectors (pull_request) Successful in 2m58s
wai-pack / wai-pack · independent verifier (Python) (pull_request) Successful in 4m39s
joule-moq-room room service / joule-moq-room, every target (pull_request) Successful in 1m34s
WAI conformance / quantum core and corpus under wasm32-wasip2 (wasmtime) (pull_request) Successful in 2m24s
WAI conformance / cross-arch byte-equality · linux (pull_request) Successful in 30s
WAI energy receipts / energy receipts · linux (pull_request) Successful in 10m18s
wai-ops-gpu integer operators on GPUs / builds · no adapter is not a pass (linux) (pull_request) Successful in 2m41s
wai-pack / wai-pack · linux (pull_request) Successful in 1m36s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 51m44s
wai-quantum studio and desktop / studio · browser (pull_request) Successful in 2m47s
WAI conformance / cargo-class corpora · linux (pull_request) Successful in 20m32s
wai-web reach builds / wai-web reach builds · the package of record, every output as recorded (pull_request) Successful in 2m52s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Successful in 4m36s
wai-web reach builds / wai-web reach builds · this tree's wai-web, every rung on its engine line (pull_request) Successful in 4m51s
standards workspaces build + test / macOS energy backends (pull_request) Failing after 4m55s
WAI conformance / cargo-class corpora · macos (pull_request) Successful in 12m17s
WAI conformance / cross-arch byte-equality · macos (pull_request) Successful in 17s
WAI conformance / codec wrappers · fuzz smoke, text decode limits, C entry points, provenance, the semantic path (pull_request) Successful in 40s
WAI energy receipts / energy receipts · macos (pull_request) Successful in 3m55s
wai-ops-gpu integer operators on GPUs / Metal GPU and browser WebGPU (macos) (pull_request) Successful in 3m50s
wai-pack / wai-pack · macos (pull_request) Successful in 58s
wai-quantum studio and desktop / desktop app · macos (pull_request) Successful in 1m58s
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (pull_request) Successful in 10m4s
joule-moq-room: the lock file follows wai-quantum 0.6.14 from main
Main's wai-quantum 0.6.14 release updated every lock that carries it but this one (wai-rs's optional dependency reaches it), so `cargo build --locked` of the room service refused to run after the merge. Resolved with `cargo metadata`: the one version line changes. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Merge origin/main (
41c5bafb, #428 landed as aa61ab3b) into train/live-room-d
Co-Authored-By: Claude Opus 5.5 <[email protected]>
room page: take the endpoints a link names only on the page's own origin
The room client took its relay, room service, fixture, WebTransport URL, certificate hashes and issuer directory from its query string as given, and followed a relay's GOAWAY to any URI. A crafted link and a press of Join would send a joiner's camera and microphone to another origin. - web/room/endpoint.js: an endpoint is taken only on the page's own origin (`https://<host>`, or `wss://<host>` for a relay), never with user information; a page served from the loopback interface (a fixture page, the e2e harness) takes loopback addresses alone. - app.js: `service`, `ws`, `wt`, `fixture` and `issuer_directory` are held to it; `hashes` only on a loopback page. A refused endpoint is recorded (`snapshot().refused`) and shown, Join stays off, and the page connects to nothing: `enter()` checks the relay before it asks for the camera, a grant or a session. A GOAWAY naming another origin is refused: the page closes its session and says so. - web/room/endpoint.test.mjs (run by e2e/run.sh): own origin, another origin, scheme-relative, the page's host as user information, javascript:, a plain scheme on a secure page, an HTTP page, and loopback for the harness. - e2e scenario `origin`: a foreign `?ws=` is refused at Join and nothing connects (no WebSocket, request, grant or camera); a foreign `?service=`, scheme-relative, behind user information or as javascript:, is refused with Join off; a GOAWAY to another origin is refused and the session ends. The `service` scenario checks that every request and WebSocket of every page stays on the page's origin, as the production `connect-src` requires. Co-Authored-By: Claude Opus 5.5 <[email protected]>
joule-moq rating vectors test: is_multiple_of, as clippy asks
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Merge #428's head
9f63561b (wai-conformance: the age, direction and takedown steps get their own bodies back)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
joule-moq: the rating vectors test reads the room-service requests
tests/rating_vectors.rs reads every request of the age corpus's serving cases. The new `room_service` case gives some of them a session's proof of a room key (age-assurance §5.5): checked here as the relay checks it in SETUP, with the room's authority and clock as the room keys, and held to the corpus's outcome (the room's own service, served by threshold, or refused with the proof's reason). Co-Authored-By: Claude Opus 5.5 <[email protected]>
age × co: the room's own service is not held to the serving rule
A room with both `[age]` and `[co]` broke: co-performance's clock follows and logs every performer's objects over a relay session of its own, which holds no grant and no token, so age-assurance §5.5 served it nothing rated. - age-assurance §5.5: a session that proves possession of a room key (the authority's or the clock's) is the room's own service, served every object of the room. The proof is a GRANT_PROOF naming no grant (32 bytes of zero) over the session's challenge, checked as live-rooms §10.5.4 checks a proof; one that does not pass refuses the session with that section's reason; one that passes by a key that is no room key gives nothing. Co-performance §8 points to it; §11 and §12 name the reasons and the new vectors. - joule-moq: `grant_proof::NO_GRANT`, `add_room_key_proof`, `room_key_proofs`. - joule-moq-relay: SETUP checks room-key proofs; a session proving a key the relay holds as a room key (an authority's, for its rooms; `[floor]` or `[dvr]` room keys, for every room) has `SessionCtx::service`, and its threshold there is past every age. `ClientOptions::room_keys` presents such proofs. tests/age_room_service.rs: a room rated 18 with the floor held; the clock's and the authority's proofs served every group, the clock's token alone and a non-room key refused `below_threshold`, a forged clock proof and one over another challenge closed UNAUTHORIZED. - joule-moq-room: the clock's session proves both room keys it holds. - wai-rs `age_assurance::room_service`, the corpus case `room_service`, and `age_assurance_verify.py` taught the rule, with three new checks and four mutations, all caught. - e2e: scenario `ageco`, a room with both `[age]` and `[co]`. Co-Authored-By: Claude Opus 5.5 <[email protected]>
room page: close the co-performance section, and test the page's markup
Train B's merge of #424 (co-performance in the room) with #413 (the browser
sink, 173981739) left `<section id="co">` without its `</section>`: the page
opened five sections and closed four. The parser then nested the binding table
(`#binding-debug`) and the Log inside the hidden `#co`, so in a room without
co-performance neither ever showed.
- index.html: the `</section>` after the `#co-lead` controls.
- web/room/markup.test.mjs (run by e2e/run.sh): every element the page opens is
closed in order; the Log, the binding table and the co-watch section are not
inside `#co`; each mode's controls are inside its own section.
- e2e: in the service scenario (a room without `[co]`), `#log`, `#binding-debug`
and `#cowatch` are not descendants of `#co`, `#co` is hidden and the Log shows;
in the cowatch scenario, the co-watch section and the Log show outside `#co`.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Merge #432 (co-watch) into the live-room train
Co-watch merged by meaning onto the integrated live room (#428): - joule-moq-room: one config reader, `Config::settings()`, now fallible, carries `age`, `audience_input`, `latency_budget_ms`, `lead`, `ai` and the watch room's `watch` (from `watch_source`/`watch_duration_ms`), `watch_control` and `watch_window_ms`; one `public_prefix`. A watch room is refused together with `e2ee`, `[age]` or `[co]`. - One path for the clock's records: co-performance's `Item` (`Life`/`Meta`) and `emit_items`, with the clock's rate kept; co-watch's skew statements go out as `emit_meta`, its leaves through `emit_after` (a least `t`), and every record of the clock's chain reaches the watch log through `watch_record`. The separate `Lifecycle::Meta` is dropped. - Roles and grants: `meta` for participants in a room with co-performance or a watch room, `WATCHER_KINDS` for a watch room's viewers, `watch_control` the last attribute in key order. - Page: the co-watch mode beside co-performance and the binding; the e2e suite gains `cowatch` and keeps every scenario; run.sh runs all five node test files. - Workflows: each step whole, `paths:` unioned. CHANGELOG and REGISTRY rows kept from both; capabilities.json and v2.ttl regenerated by `registry export`. Co-Authored-By: Claude Opus 5.5 <[email protected]>
co-watch: reflow two paragraphs of §8
Some checks failed
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 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 conformance / does this change touch it (pull_request) Has been cancelled
WAI conformance / cargo-class corpora · linux (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 / quantum core and corpus under wasm32-wasip2 (wasmtime) (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 (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 / which workspaces this change touches (pull_request) Has been cancelled
standards workspaces build + test / macOS energy backends (pull_request) Has been cancelled
repository checks / lockfiles, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Has been cancelled
joule-moq-web browser client / joule-moq-web · does this change touch it (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 · does this change touch it (pull_request) Has been cancelled
joule-moq-room room service / joule-moq-room, every target (pull_request) Has been cancelled
joule-code conformance gate / Build + round-trip C1-C4 vectors (pull_request) Has been cancelled
joule-code conformance gate / joule-moq-relay, every target (pull_request) Has been cancelled
joule-code conformance gate / joule-moq-ingest, every target (pull_request) Has been cancelled
Co-Authored-By: Claude Opus 5.5 <[email protected]>
joule-moq-room, joule-moq-web: watch rooms in the READMEs, the example configuration and the workflows
Co-Authored-By: Claude Opus 5.5 <[email protected]>
cowatch e2e: each step waits for its control's instant on every page's room clock
Co-Authored-By: Claude Opus 5.5 <[email protected]>
room page: a watch room's page enters the co-watch log before it subscribes to the clock
Co-Authored-By: Claude Opus 5.5 <[email protected]>
joule-moq-web: the room page's co-watch mode and its end-to-end scenario; joule-moq-room: a watch room over HTTP and the relay
The page fetches a watch room's source and checks it by its content hash, enters the room's co-watch log by a proof of its key, applies each control at its instant, keeps the source at the controlled position by the catch-up rule, states what it presents and shows the room's sync. e2e scenario cowatch: three viewers, a play, a pause, a seek, a late joiner, the clock's skew statements in sync within the bound, and the room verified by co_watch_verify.py. The room service's test drives a watch room against a relay on loopback. Co-Authored-By: Claude Opus 5.5 <[email protected]>
joule-moq-room: watch rooms (WAI co-watch); joule-moq-web: the page's co-watch module
The room service serves a watch room's source, admits a viewer by a proof of its grant's key over the service's challenge, carries its controls and presentation statements in a log when co-watch §5 and §7.2 accept them, signs each window's skew statements and a viewer's leave when it falls silent. web/room/cowatch.js holds the rules the page applies (the policy, the controlled position, the catch-up rule, statements written byte for byte) and the player; its tests run them against live-conformance/cowatch/. Co-Authored-By: Claude Opus 5.5 <[email protected]>
wai.live.cowatch: registry entry and export, SPEC §5, CHANGELOG, corpus README and CI step
Co-Authored-By: Claude Opus 5.5 <[email protected]>
co_watch_verify.py: the co-watch corpus's second implementation, written from the extension's text
It verifies each room with live_rooms_verify.py, recomputes the controlled position, samples, percentiles and verdicts from §5 to §9, requires every mutated copy of the corpus to fail and catches each of its own checks dropped. Standard library only, run isolated. Co-Authored-By: Claude Opus 5.5 <[email protected]>
wai.live.cowatch: co-watch rooms — controls at a room-clock instant, the controlled position, signed presentation statements and recomputed skew
The extension text (wai/extensions/co-watch.md), the co_watch module of wai-rs behind its own feature, and the corpus live-conformance/cowatch/ written by wai_co_watch_vectors from fixed seeds: three accepted rooms, a gap, and refused vectors covering every reason of §12. Co-Authored-By: Claude Opus 5.5 <[email protected]>