open-standards/.github/workflows/joule-moq-web.yml
Transaction Science 7234c82352 joule-moq-web: the room page shows a host's screen and controls it under its grant (WAI remote-desktop)
web/room/remote.js: input statements, floor statements and latency records written byte for byte,
every statement read, the host's rules and the floor replayed over a room's records, the table
from keyboard codes to HID usages; and RemoteViewer, which draws the host's screen (the page's
WebCodecs decoder, AVC or HEVC in Annex B, every frame verified against its receipt first), shows
what the host captures and who holds the floor, batches pointer, keys, composed text and pasted
text into input statements under the grant's classes, asks for and gives back the floor, and signs
a latency record of each applied input's frame at the display refresh that showed it.
remote.test.mjs checks it against wai/live-conformance/remote/, the host agent's key table among
it. The page draws a room with no composition descriptor, and names a screen track by its
namespace.

e2e: the remote scenario runs wai-remote-host with a pattern source (the platform's hardware
encoder on macOS), two pages, the floor asked for and given, a click and a key applied with their
latency records, an input with no control power refused, and the session the host records
verified by remote_desktop_verify.py.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-26 02:47:39 -04:00

216 lines
12 KiB
YAML

name: joule-moq-web browser client
# The browser MoQ client (joule-code/joule-code-rs/crates/joule-moq-web) against
# joule-moq-relay on loopback, in the host's installed browser, driven by the automation
# package of e2e/package.json, with a fake camera and microphone
# (crates/joule-moq-web/e2e/run.sh):
#
# compose live-conformance/compose in the page: every layout, mix, descriptor and
# composition case through wai-live-web's wasm build, and wai-rs's own
# corpus check run in the browser;
# webtransport two participants and the room's clock over WebTransport: media both ways,
# every object verified against its receipt (the relay's door checks each
# too), the composed canvas's tiles equal to the host's layout for the same
# descriptor and set, the mix in an AudioWorklet, captions with their origin
# label, and a late joiner served earlier groups by FETCH;
# websocket the same over the WebSocket binding, forced;
# goaway the relay shut down with GOAWAY naming a second: every page moves and
# closes its old session before the timeout;
# service a room service (joule-moq-room) keeps the room, as in production: pages
# make their own keys and enter from their buttons, the service grants and
# its clock authors the lifecycle, the relay (WebSocket only, behind a
# same-origin proxy) honours the grants by the service's authority key;
# age a room that admits by age (WAI age-assurance §5.6): pages answer the room
# service's Privacy Pass challenges with tokens they obtain by blind issuance
# from a test issuer over the corpus's published keys; a viewer at 16 is served
# the audio and refused the video its performer rates 18 (below_threshold), a
# viewer at 18 is served both, a token of 13 is refused by the service, a token
# spent twice is refused, and the admission receipt verifies and suppresses;
# e2ee an end-to-end encrypted room (WAI confidentiality) kept by a room service:
# pages key through the service's log, seal every frame and open every object
# before it is decoded; a viewer outside the group opens nothing, every change
# a relay could make is refused with its reason, a late joiner opens nothing
# sealed before its join, a member that stops publishing is removed and opens
# no later epoch, and the relay's DVR store keeps nothing of the room;
# co a room with co-performance (WAI co-performance) kept by a room service behind a
# relay that holds the floor and the audience's inputs: a lead starts turns from
# its page, a performer out of turn is marked by the clock and, under a schedule
# that refuses, held back by the relay; an AI participant's lines show ahead of
# when they play, one kept and one not; a late object is marked; the audience
# votes and sends prompts from the page, the relay withholding each input the
# rules refuse; the clock commits and selects, credited; and the room with the
# clock's log of objects verifies the same in the page and on the host.
# ageco a room that admits by age and keeps co-performance: the room's clock, the
# room's own service by its proof of the room's keys (age-assurance §5.5),
# logs both performers' rated tracks and marks an object out of turn, while a
# viewer at 16 is still refused the video rated 18.
# origin the endpoints a link may name: a relay or a room service on another origin
# is refused and nothing connects; a GOAWAY naming another origin is refused
# and the session ends; and every page of the service scenario connects to its
# own origin alone.
# binding the browser sink (WAI browser-sink): live-conformance/browser in the page,
# a wasm-route decode verified against its receipts with every frame bound,
# the binding through clone, transfer, copyTo and re-encode, the user agent's
# decoder's frames bound through the side table, no codec string outside the
# WebCodecs Codec Registry put to the user agent, a measured figure per decode
# beside powerEfficient, and a room whose tiles show verified, bound frames.
# cowatch a watch room (WAI co-watch) kept by a room service: three viewers check the
# source by its content hash and enter by a proof of their keys; a play, a
# pause and a seek applied by every page at their instants, a late joiner
# brought to the room's position, the clock's skew statements in sync within
# the bound, and the room verified by co_watch_verify.py.
# remote a remote desktop room (WAI remote-desktop) the host agent keeps: a page
# decodes the host's screen, asks for the floor, clicks and types, and signs
# latency records; an input without the control power is refused by the host;
# and the session the host records verified by remote_desktop_verify.py.
#
# Before the browser, wai-live-web's own tests: the room's records a page reads, co-performance's
# among them, each statement read and the corpus verified through the crate.
#
# The macos runner is the host with the browser installed; the tests drive that browser
# (its executable named by BROWSER_BIN), not a bundled one. The automation package comes
# from e2e/package-lock.json with install scripts off. Each browser runs under the host's
# memguard where it exists. Nothing listens beyond loopback.
#
# Push filters match the push's delta, so every path the job reads is listed.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on:
push:
branches: [main]
paths:
- 'joule-code/joule-code-rs/crates/joule-moq-web/**'
- 'joule-code/joule-code-rs/crates/joule-moq/**'
- 'joule-code/joule-code-rs/crates/joule-moq-mls/**'
- 'joule-code/joule-code-rs/crates/joule-moq-relay/**'
- 'joule-code/joule-code-rs/crates/joule-moq-room/**'
- 'joule-code/joule-code-rs/crates/jwp/**'
- 'joule-code/joule-code-rs/Cargo.toml'
- 'joule-code/joule-code-rs/Cargo.lock'
- 'joule-code/joule-code-rs/vendor/**'
- 'wai/wai-live-web/**'
- 'wai/wai-rs/**'
- 'wai/wai-ops/**'
- 'wai/live-conformance/compose/**'
- 'wai/live-conformance/presentation/**'
# The page's Privacy Pass client test reads RFC 9578 A.2's vector from the age corpus, and
# the age scenario's issuer its published test keys.
- 'wai/live-conformance/age/**'
- 'wai/live-conformance/coperformance/**'
- 'wai/live-conformance/browser/**'
- 'wai/wai-rs/tests/vectors/int_video/**'
# The page's co-watch module is tested against the co-watch corpus, and the cowatch
# scenario verifies its room with the corpus's second implementation.
- 'wai/live-conformance/cowatch/**'
- 'wai/live-conformance/co_watch_verify.py'
# The page's remote desktop module is tested against the remote corpus, and the remote
# scenario runs the host agent and verifies its session with the corpus's second
# implementation.
- 'joule-code/joule-code-rs/crates/joule-moq-remote/**'
- 'wai/live-conformance/remote/**'
- 'wai/live-conformance/remote_desktop_verify.py'
- '.github/workflows/joule-moq-web.yml'
pull_request:
paths:
- 'joule-code/joule-code-rs/crates/joule-moq-web/**'
- 'joule-code/joule-code-rs/crates/joule-moq/**'
- 'joule-code/joule-code-rs/crates/joule-moq-mls/**'
- 'joule-code/joule-code-rs/crates/joule-moq-relay/**'
- 'joule-code/joule-code-rs/crates/joule-moq-room/**'
- 'joule-code/joule-code-rs/crates/jwp/**'
- 'joule-code/joule-code-rs/Cargo.toml'
- 'joule-code/joule-code-rs/Cargo.lock'
- 'joule-code/joule-code-rs/vendor/**'
- 'wai/wai-live-web/**'
- 'wai/wai-rs/**'
- 'wai/wai-ops/**'
- 'wai/live-conformance/compose/**'
- 'wai/live-conformance/presentation/**'
# The page's Privacy Pass client test reads RFC 9578 A.2's vector from the age corpus, and
# the age scenario's issuer its published test keys.
- 'wai/live-conformance/age/**'
- 'wai/live-conformance/coperformance/**'
- 'wai/live-conformance/browser/**'
- 'wai/wai-rs/tests/vectors/int_video/**'
# The page's co-watch module is tested against the co-watch corpus, and the cowatch
# scenario verifies its room with the corpus's second implementation.
- 'wai/live-conformance/cowatch/**'
- 'wai/live-conformance/co_watch_verify.py'
# The page's remote desktop module is tested against the remote corpus, and the remote
# scenario runs the host agent and verifies its session with the corpus's second
# implementation.
- 'joule-code/joule-code-rs/crates/joule-moq-remote/**'
- 'wai/live-conformance/remote/**'
- 'wai/live-conformance/remote_desktop_verify.py'
- '.github/workflows/joule-moq-web.yml'
jobs:
# Forgejo matches `paths:` against the push delta, so a rebase fires this workflow
# whatever the pull request changes. This job reads the pull request's own diff against
# the paths above (scripts/pr-touches-paths.py, which fails open).
relevant:
name: joule-moq-web · does this change touch it
runs-on: linux
outputs:
run: ${{ steps.gate.outputs.run }}
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- id: gate
run: |
set -euo pipefail
r=$(python3 scripts/pr-touches-paths.py .github/workflows/joule-moq-web.yml \
"${{ github.event.pull_request.base.sha }}" \
"${{ github.event.pull_request.head.sha }}")
echo "this change touches the browser client: $r"
echo "run=$r" >> "$GITHUB_OUTPUT"
e2e:
name: joule-moq-web · browser end-to-end (macos)
needs: relevant
if: needs.relevant.outputs.run == 'true'
runs-on: macos
timeout-minutes: 90
steps:
- uses: actions/checkout@v5
# launchd's PATH omits cargo on the macOS host runner.
- name: Put cargo on PATH
shell: bash
run: echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
# Pinned to the wasm-bindgen both crates pin: the CLI and the crate must match.
- name: wasm32 target and wasm-bindgen CLI 0.2.127
shell: bash
run: |
set -euo pipefail
rustup target add wasm32-unknown-unknown
cargo install --locked wasm-bindgen-cli --version 0.2.127
# The page's codec is joule-moq built without its runtime; this is the build that
# holds the feature split.
- name: joule-moq without its runtime
shell: bash
working-directory: joule-code/joule-code-rs
run: nice -n 10 cargo check --locked -p joule-moq --no-default-features
# The page's records library, natively: its co-performance reading checked against the
# corpus live-conformance/coperformance (src/co.rs) beside its own cases.
- name: wai-live-web, its own tests
shell: bash
working-directory: wai/wai-live-web
run: nice -n 10 cargo test --locked --lib
- name: The browser end-to-end tests
shell: bash
env:
BROWSER_BIN: /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
MOQ_WEB_OUT: ${{ runner.temp }}/moq-web
run: |
set -euo pipefail
test -x "$BROWSER_BIN" || { echo "::error::this runner has no browser at $BROWSER_BIN"; exit 1; }
mg=/Volumes/macos_4TB_external_1/wai-build/bin/memguard
if [ -x "$mg" ]; then export MEMGUARD="$mg"; fi
nice -n 10 bash joule-code/joule-code-rs/crates/joule-moq-web/e2e/run.sh 2>&1 | tee "$RUNNER_TEMP/moq-web-e2e.log"
grep -qE '^room e2e: 1 of 1 runs passed; ' "$RUNNER_TEMP/moq-web-e2e.log" \
|| { echo "::error::the browser end-to-end run did not pass"; exit 1; }
if grep -q '^FAIL' "$RUNNER_TEMP/moq-web-e2e.log"; then echo "::error::a check failed"; exit 1; fi