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. # 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. # # 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/**' - '.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/**' - '.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