open-standards/wai/live-conformance/rooms/README.md
Transaction Science 78b45a5d1d live_rooms_verify.py and the rooms README: the seek power and §10.6's seek requests
The second implementation allows powers bit 4, states each accepted
grant's seek, and gives every request of seek its outcome by §10.6's
rule from the room's grants as §4.3 checks them; the README describes
the dvr room, the new refusals and the seek section.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-19 11:45:51 -04:00

133 lines
8.8 KiB
Markdown

# live-conformance/rooms — live rooms
The corpus of [`extensions/live-rooms.md`](../../extensions/live-rooms.md) §12: rooms, each
a signed `WRD1` descriptor, its `WRG1` grants, the head of its authority's key log, and its
principals' `WEV1` chains, with the outcome a room verifier gives them; and the records of
the registered publish profiles.
**Room equivalence.** For each vector, a conforming room verifier given its `descriptor`,
its `grants` in order, its `key_log_head` (or none) and its `chains` (each one principal's
records in stream order) reaches the outcome the vector states (§9.4):
- `refuse`: the `reason` and where, `at`: `["descriptor"]`, `["key_log"]`,
`["grant", i]`, or `["record", c, i]` for record `i` of chain `c`;
- `gap`: no record refused, and the `gaps` (principal, first and last seq not held), under
the code `delivery_gap`, with the `room` id and the merged `order`;
- `accept`: the `room` id; every grant's digest (`grant_digests`); the merged `order`
(§9.2), each entry `[chain, record, grant]` with the digest of the grant the record was
verified under, or `null` for a room key's record; the room `state` (§9.3); the `tally`
of the room's polls (live-events §7); and, per grant, the `moqt` scopes and `reval`
interval it maps onto (§10.2), and whether it holds the seek power, `seek` (§3.3).
A verifier that reaches another outcome, reason, place, gap, digest, order, state, tally,
scope or seek power does not conform.
**Seeking back.** For each request of `seek`, a conforming relay, given the grant the
request names, the track's live edge and the request's start Location, serves it or
refuses it `seek_not_granted` as the request states (§10.6): a request whose start group
is below the live edge seeks back, and is served only under a grant whose `powers` has
bit 4.
**Provenance.** Generated here: `wai_live_room_vectors gen` writes every descriptor, grant,
key-log head and record from rooms authored in code, from fixed seeds. Each key is SHA-256
of a fixed label and the principal's index, each nonce and viewer id likewise, every time
is a constant (the rooms' `t0` is 2026-10-16T00:00:00Z), and Ed25519 signing is
deterministic (RFC 8032), so the file is the same bytes on every machine. The generator
asserts that each vector reaches the outcome it was written to have before it writes it.
The key-log heads are sealed by the reference's key log (jwp-receipts §7.2) under a fixed
root. The origins are `.example` names (RFC 2606). No recorded data.
## Layout
One file, `vectors.json`:
- `principals`: the keys, by name (informative).
- `registered_profiles`: each profile §8.2 registers, by `name`, with its `record` (§8.1)
in hexadecimal; a verifier compares its own table with them.
- `vectors`: each with `name`, `about`, `descriptor` and `grants` (hexadecimal),
`key_log_head` (a key-log revision's JSON text, or `null`), `chains` (lists of records
in hexadecimal), `outcome`, and as above `reason`, `at`, `room`, `gaps`,
`grant_digests`, `order`, `state`, `tally` and `moqt`.
- `proofs` (§10.5): each with `name`, `about`, `binding` (`moqt` or `http`), what is
presented — for `moqt`, the AUTHORIZATION TOKEN's value `token` (the grant's record) and
the `GRANT_PROOF` option values `proofs`, in hexadecimal; for `http`, the request's
`headers`, `authorization` (`Bearer` and the grant's record in base64url) and
`wai-room-proof` (the proof record in base64url) where it has one — then what the
verifier holds: the `challenge` it issued for this session or request, the `consumed`
challenges, and the `verb` and `target` it received; and the `outcome`, `accept` or
`refuse` with its `reason` (§10.5.4).
- `seek` (§10.6): each with `name`, `about`, the grant the request rests on — `vector`, the
name of the accepted room, and `grant`, its index among that room's `grants` — the
track's `live_edge` (the group of its Largest Object), the request's `start` Location
`[group, object]`, the `request`, `fetch` (a FETCH) or `fill` (a SUBSCRIBE's fill), and
the `outcome`, `serve` or `refuse` with its `reason`.
`state` has `present` (each principal and its grant in effect, by principal), `published`
(each own track: principal, media kind, profile name, number of layers, by principal and
media code), `slots` (each slot in the descriptor's order: its live publisher, standby,
and the live publisher's precedence), `tracks` (the rendered Full Track Names of every
track a subscriber can reach, §6.1, sorted), and `timeline` (each change of a principal's
grant in effect, with its `t`).
Vectors:
- accepted rooms: `lesson` (a director's remote record control of a performer, an audience
under single-use grants, review and recording policies with a region pin, a key-log
head revoking a grant and a viewer the room never admits), `broadcast` (one publisher,
viewers voting, a moderator's decision, no key log, the authority its own clock),
`takeover`, `redundant_failover`, `promote_viewer`, and `dvr` (a broadcast whose relay
keeps a DVR window: one publisher, and an audience whose role holds the seek power,
viewer_a's grant holding it and viewer_b's narrowing it away; its grants are 0 the
host's, 1 viewer_a's, 2 viewer_b's);
- `room_gap`: a chain with a record not delivered;
- one negative control per refusal, each a single defect: the descriptor truncated, with
trailing bytes, of another version, with a flipped signature bit, with a tick of 0,
with a registered profile changed, with a role beyond the ceiling, with a recording
policy whose jitter exceeds its commit interval, and with a role whose powers set bit 5;
the key log missing, of another origin, re-signed wrongly, not listing the authority,
and revoking it; a grant truncated, signed by another key, of another room, permitting a lifecycle
kind, with oversized attributes, too long, beyond its role, beyond the room's ceiling,
chained to nothing, chained to another principal's, with the seek power its role does
not hold, and with powers setting bit 5; and in the records, a bad
signature, the wrong room, a kind not in the grant, a participant's lifecycle event,
events outside a grant's window, a principal not present, a revoked grant and a
revoked viewer, a reused single-use grant, an unknown grant, another principal's grant,
a second join, the participant limit, a malformed detail, every publish refusal, every
exchange refusal, a takeover without higher precedence, both failover refusals, a
command without its power, a grant's rate and payload bounds, and a stated tally the
replay does not reach;
- proofs, under the broadcast room's host's grant: over MOQT, one accepted, and refused a
grant with no proof, the accepted proof presented again on a later session, a proof over
another session's challenge, one by the moderator's key, one naming the host's key and
signed by the moderator's, one naming the moderator's grant, one for another relay, and
three records that do not read; over HTTP, a WHIP `POST` and the session's `DELETE`
accepted, and refused the Bearer token alone, the `POST`'s proof sent again, a `POST`'s
proof on a `DELETE`, and a MOQT proof presented over HTTP;
- seek requests, under the `dvr` room's grants, on a track whose live edge is group 120
unless stated: served, a FETCH from group 90 and a fill from group 100 under viewer_a's
grant, which holds the seek power; refused `seek_not_granted`, the same FETCH and fill
under viewer_b's, which does not, a FETCH from (119, 7) under it, and a FETCH from group
90 under the host's, whose role lacks the power; and served under viewer_b's, a FETCH
from (120, 0) and from (120, 3), a fill from (120, 0) joining the group in progress, and
a FETCH from (0, 0) of a track whose live edge is group 0.
## Running
```
cd ../../wai-rs
cargo run --no-default-features --features live_room --bin wai_live_room_vectors -- verify ../live-conformance/rooms
cargo run --no-default-features --features live_room --bin wai_live_room_vectors -- gen ../live-conformance/rooms # regenerate
cargo test --no-default-features --features live_room --test live_room
cd ../live-conformance
python3 live_rooms_verify.py rooms
```
`wai_live_room_vectors verify` and `tests/live_room.rs` require the file to regenerate
byte for byte and `wai-rs` to give every vector, every proof and every seek request its
outcome; the test also requires every reason of §11 to refuse a vector, a proof or a seek
request, `seek` to hold requests served and refused, and §11 to list exactly the reasons
the reference reports.
`live_rooms_verify.py` is the second implementation, written from the extension's text and
sharing no code with `wai-rs`: it verifies every event with `live_events_verify.py`, the
live-events corpus's own second implementation, gives every vector, proof and seek request
the same outcome, and requires the same coverage. Pass = an exit status of 0 from each.