Merging the train mixed three adjacent independent-verifier steps:
- age assurance also ran direction's tests;
- direction ran takedown's tests;
- takedown was left with a name and no run.
The runner refused the job as invalid ('Invalid run/uses syntax' for the
takedown step). Each step now has exactly the body its own PR gave it:
age #409, direction #411, takedown #420.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
C3's synthesis subject `samples` (a picture a party presents and does not
publish) met takedown's exhaustive match once both slices were in one tree
(E0004). Such samples are no item a holder serves or keeps for others, so a
closure claim whose subject they are adds no subject and needs no purge:
takedown §4.2 rule 3, the reference and its second implementation say so, and
the corpus's shared world gains such a claim, so every case exercises it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
ad-presentation (on main) registers WPC1 for a player's presentation claim, and
co-performance registered WPC1 again for its commitment, so the registry's
library test (wire_identifiers_are_in_their_documents) refuses the train:
"WPC1 (magic) is listed twice". The commitment takes WPK1, unused anywhere:
the extension's magic table and §7.5 layout, wai-rs's constant, the relay's
floor reader, the independent verifier, the SPEC §5 paragraph, the changelog
entry and two comments. The corpus is regenerated by its generator (only the
commitments' bytes and what signs or digests them change) and the registry
export with it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
By meaning: the relay keeps PP's rating functions (take_ratings, gate,
group_age) and T1's track_restarts side by side. A track restarted after
TRACK_ENDED drops its cache, owners and Largest Location as T1 has it; the
rating claims held for it stay, since a claim only raises an age and the
publisher carries its claims again on the new PUBLISH. CHANGELOG kept both.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
By meaning:
- Page: app.js keeps the age parameters and paragraph and adds debug=binding;
imports, state and roomTest hooks of age, co and binding side by side.
index.html carries the co section and the binding table, each with its CSS.
- room.e2e.mjs carries scenarioBinding whole beside scenarioAge and
scenarioCo; default list `...,age,co` with a room, `binding` always;
run.sh's node unit tests are every page module's (capsule, media,
presentation, privacy-pass, binding).
- wai-live-web: the wai dependency takes co_performance and browser_sink;
the crate doc lists both.
- joule-moq-web README: one node-test bullet for capsule, media and binding,
then privacy-pass and ad-presentation; scenarios age, co and binding.
- REGISTRY prose names exploration and browser-sink together; the shared
lists keep both sides; export regenerated.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Relay, by meaning: the DVR store's module text keeps both the minors' room
(AGE) and the takedown (TKD) paragraphs; a session's object loop keeps CO4b's
floor and audience-input filtering and TKD's takedown track feeding the store;
tests/dvr.rs lists both rules. Shared lists kept both sides (workflow step
and path, CHANGELOG, REGISTRY, SPEC §5, live-conformance index and
provenance line, features, registry constants, lib.rs); export regenerated.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
By meaning:
- joule-moq GrantHead: one attribute reader (CO4b's grant_attrs, checked
arithmetic, UTF-8 keys and values) in place of AGE's byte-slice reader;
`minor` and `age` are read from the same attrs CO4b keeps, with AGE's rules.
- joule-moq-room: roles(co, audience_input) is CO4b's role set, fed to PP's
descriptor(); grant(age) and grant_with(age, ai) keep both callers (the
age-admitted grant and the AI participant's); PP's refuses_publisher check;
attributes in key order age, ai, audience_input / latency_budget_ms, order.
The HTTP proof challenge (live-rooms §10.5.3) is proof_challenge(), beside
PP's Privacy Pass challenge(); both route tables and doc tables kept.
- Page: both imports; the grant request carries `ai` and, on a 401, the
Privacy Pass token; snapshot keeps age and co. startRoomService takes both
`co` and `extra`. room.e2e.mjs carries scenarioAge and scenarioCo whole,
`age,co` in the default list.
- SPEC: CO4b's revised close of the co-performance paragraph, then AGE's
and exploration's paragraphs as they were.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Room service, by meaning: Settings carries both [age] and [co]; the descriptor
builder (PP's) takes the room's roles, and roles() gives the viewer the
audience kinds when the room has the audience-input power (CO4); a grant
states `age` first, then CO4's `latency_budget_ms` / `audience_input`, then
`order` (live-rooms §4.4 ascending keys); GET /room carries both `age` and
`co`; CO4's tests call grant() with no age. Relay: ratings and the floor are
both fields of the relay. wai features: age_assurance and co_performance.
Shared lists kept both sides; registry export regenerated.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The synthesis corpus's cases carry both priors and unmarked frames; the exact-upscale corpus's
policies carry synthesis's second_layer member. Written by wai_synthesis_vectors gen,
wai_upscale_vectors gen and wai registry export.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Resolved by meaning: synthesis::Case holds both the priors (exact-upscale) and the unmarked
frames (§7.6); synthesis §11.1 lists both inputs and the three policy members; the
synthesis-mark corpus's cases hold no prior; exact-upscale passes synthesis's second_layer
policy through; the live-conformance table gains the upscale row in its generator column; both
CI steps and both CHANGELOG entries are kept. Generated files are main's here and are
regenerated in the next commit.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
influence_verify.py is the second implementation of extensions/influence.md: it verifies each
session with contribution_verify.py, regenerates every conditioned step, reads usage statements
with ai_usage_verify.py and statements with settlement_verify.py (now amended for kind 2 lines
and their bases), and computes every share, line and report itself. Registers
wai.media.influence (registry, wire identifiers, SPEC §5, CHANGELOG, CI step, export); the
settlement corpus's reserved-line vector becomes a line whose basis is not given.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
With wai.delivery.settlement on main, the reserved 'served' slot is filled: the upstream
of an acknowledgement or declaration whose item lists name a covered object is a holder
(role served), and the downstream of a hop's acknowledgement is one too (role received);
a sink is never held to purge. Acknowledgements read under settlement §3.6 with their
item lists recomputed (evidence_invalid otherwise), and count for the matched class too.
The corpus gains two acknowledgements (a viewer's from a cache outside the stores, and
that cache's from the relay), a case whose cache never purged (purge_missing) and one
whose item list does not recompute; the Python verifier reads them with
settlement_verify.py and gains two mutations and two droppable checks.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The extension text (extensions/influence.md), the influence module of wai-rs behind the
feature influence, its corpus generator and committed corpus; settlement gains attribution
lines (kind 2) recomputed from a basis, and contribution notes the sibling record.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The record and its seven document kinds (wai-takedown, -clocks, -policy, -notice,
-match, -purge, -counts) in codecs/registry.rs, the export regenerated
(capabilities.json, vocab/v2.ttl, REGISTRY.md's table); SPEC §5's record list and a
paragraph; the CHANGELOG entry; the wai-conformance step (the module's unit tests,
tests/takedown.rs, takedown_verify.py) and its path; live-conformance/README.md; and a
pointer from live-moderation §1 to the sibling record.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A takedown (WAI takedown §10.1) rides the track [K, takedown], 0, signed by K, a key in
dvr.takedown_keys or auth.authorities. The store erases every object whose content
hash its exact or matched set names, and every object of a track its exact set names
from a group on (erased reason 3), in every room; never stores one again (an identical
copy on another track included); withholds them from every FETCH, from the store and
the cache, to moderators too; logs the takedown in takedowns.wdl, verified again on
start; and signs a purge with the relay's key under purges/<link>.json, once.
Also: the takedown module's clippy notes (type aliases, slice::from_ref), and the
Python verifier memoises its signature checks.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
extensions/takedown.md registers wai.media.takedown: a service's signed takedown of a
source (object, group, track or take) under a policy that pins a registry of regime
clocks (registry/takedown-clocks.json, read from Public Law 119-12 s3(a)(3), the IT
Rules 2021 rule 3(1)(d) and 3(2)(b) as amended by G.S.R. 120(E), Regulation (EU)
2024/1083 Art. 18(4) and Regulation (EU) 2022/2065 Art. 16(6)). The closure is
computed from deriving-edge records, synthesis origin claims and group receipts and
held to the takedown's stated set; perceptual matches are a class apart; holders sign
purges and the time to removal is held to the clock (purge_missing, deadline_missed,
window_not_observed); counts per clock, ground, category and source recompute.
The wai-rs module (feature takedown), its corpus (live-conformance/takedown, 31 cases
and 2 counts) and live-conformance/takedown_verify.py agree on every case.
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]>
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]>
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]>
The transmission and reconciliation arms now follow the reconstruct-session arm
in receipts::step6, so the patch's context no longer matched and the falsify
shard refused it. Same one-line mutation, same case it must fail.
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]>
Boxes at both sides of every size bound and every column and row bound, past
the edges and at the limits of a signed integer; each interpolation mode rising
and falling, with floors below zero. The size control now keeps its position,
so it fails on the size bound alone.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The record's row and its four document kinds in the registry and its export,
the CI step that runs the reference's tests and direction_verify.py, and the
live-conformance listing; int_kernels_vectors' exclusion names the corpus's
generated layers and rendered frames by their SHA-256.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The extension text (extensions/direction.md), the wai-rs module behind the
feature direction (records, the framing measure, interpolation, the pinned
render, governance, verification), its corpus generator and test, the corpus
live-conformance/direction/vectors.json and its second implementation,
direction_verify.py.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
With [floor] in its configuration, the relay reads each room's turn
schedules (WPT1) and handovers (WPH1) from the events tracks it already
forwards, as it reads moderation decisions, and holds the floor: a media
object a performer publishes out of turn under a schedule that refuses is
not forwarded and is counted. A schedule is honoured from a room key (the
authorities' and the configured ones) or a principal whose session proved
a grant with the direct power, when it reads, verifies and changes no past;
a handover from the turn's holder, a director or a room key, to a holder
of its segment. Unit tests of the reading and the floor, and
tests/floor.rs through a relay on loopback: the object in turn reaches the
subscriber, the other does not, and a handover passes the floor.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A [co] section of the service's configuration: with audience_input above
0, every viewer's grant carries the attribute audience_input and may send
meta events and reactions, and the viewers' grants are served with the
room (co.audience) so a page verifies their inputs; with
latency_budget_ms above 0, every publisher's grant carries it. Either
gives each room a WPP1 policy the authority signs (co.policy). A unit
test checks the grants and the policy, and that two viewers' votes in a
window the clock opens, commits and selects verify as co-performance §9
verifies a room.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A page whose numbers do not keep 1920.0 apart from 1920 writes it as an integer, so the
malformed-bound case is a true fraction; §5 says so.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The capability and its ten magics in the reference registry, and the
export regenerated (registry/capabilities.json, vocab/v2.ttl and
REGISTRY.md's table); SPEC §5's paragraph and record list; the corpus's
README with its provenance; the live-conformance index; the sidecar
exclusion's reason for live-conformance; and a step in the independent
verifiers job that runs the module's unit tests, tests/co_performance.rs
and co_performance_verify.py, with the extension in the workflow's paths.
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]>
The verifier is weakened on purpose, one check at a time, and the corpus
must catch each weakened copy. Room verifications are cached by their
inputs, since they do not depend on the checks dropped. The extension
names the window's, the fidelity statement's and the credit's malformed
values explicitly.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Written from extensions/co-performance.md alone: the policy, statements,
intents and their pinned rules, the fidelity statement, schedules,
handovers, the floor, budgets and marks, windows, inputs, commitments,
the three rules and the credits. Each room through live_rooms_verify.py;
mutated copies of the corpus must each fail.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
web/room/binding.js: the side table a page carries contentBinding in until a registry
lists the member (decoder and timestamp, clone, transfer, reset, a bound; ambiguity
refused), the capability report built in two passes so that only codec strings the
WebCodecs Codec Registry lists are put to the user agent, and the state of the frame on
screen. The room routes each track before it decodes it, binds every frame its video
decoders output to the verified object and group receipt, and shows the state of each
tile's frame with ?debug=binding. web/binding/ runs live-conformance/browser in the page,
the wasm route's verified decode, the binding through clone, transfer, copyTo and
re-encode, the metadata probe and a capability report from the user agent's answers.
e2e: scenario binding, energy.py for the host's per-process counter, and the decode wasm
built by run.sh.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The extension text (extensions/co-performance.md), the reference module
(wai-rs feature co_performance) and its corpus generator: the room
authority's WPP1 policy; intents compared with what played under pinned
rules and the clock's fidelity statement recomputed; turn schedules in
microseconds or bars, call-and-response pairs, handovers, the floor,
latency budgets and marks; audience inputs to selection windows, the
commitment, three selection rules and their credits.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
wai-live-web exports the browser_sink functions (routeReport, evaluateConstraints,
readBinding, makeBinding, frameTier, frameState) and, behind its feature int_video,
decodeBound: a wai.video.int_motion envelope verified against its group and object
receipts, decoded by the integer decoder and every frame bound; an object that does not
verify is not decoded, and one decoded without receipts is unbound. joule-moq-web's
verifier now returns a verified object's content hash and its group receipt's link hash,
which a frame decoded from it names. Reason's list is a function, so nothing of the module
reaches include/wai.h.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Written from extensions/browser-sink.md: it reads §4.1's decoders, §4.2's registry rows,
§11's reasons and capability-constraints §3.1's register from the texts, each
capability's class from the registry export, and recomputes every route, constraint
evaluation, energy entry, binding, chain signature and link hash, frame state and side
table. Answers for codec strings no registry row lists are changed every way and never
read; every mutated copy of the corpus fails.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
extensions/browser-sink.md: a page routes each object to the user agent's decoder, a
native host's or WAI's wasm decoder; the user agent is asked only about codec strings the
WebCodecs Codec Registry lists, and its decoder is taken only where it is exact for the
tier the object needs. capability-constraints gains its register (§3.1) and its
evaluation in a page. A measured figure is reported beside the user agent's
powerEfficient, which stays its word. Every decoded frame carries contentBinding: the
content hash of the receipted object, the link hash of its group receipt, and the tier.
wai-rs: the browser_sink module (feature browser_sink), its corpus generator
wai_browser_sink_vectors, and tests/browser_sink.rs. The corpus is
live-conformance/browser/.
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 int_sr module (WSR1 prior, wai-upscale:sr2/sr4 lowered to one wai-ops/1 graph), the
exact_upscale module (upscale record with residual and target, presentation statement),
synthesis's samples subject and SR evidence, the corpus generator, the second implementation,
the training tool and its trained priors.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Reconciling is part of the delivery service a distributor operates over WAI.
The section still says what a reconciliation discloses and to whom, keeps the
fresh-key option, and points a distributor that a regime asks for a lawful
basis to the lawful-basis claim (jwp-receipts §2.4.6).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The runner's test cases and WAI receipts end to end against each relay the
interoperability runner's registry lists, at each draft both speak, over QUIC
and WebTransport; and other implementations' test clients against
joule-moq-relay. Relays and clients are named by what they offer; each result
that is not a pass names its cause.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The -18 table gains a requester's FIN on namespace requests, the -16 notes the
Request IDs admitted across streams; the relay README states how it ends a
publisher's tracks and treats a closed control stream; -14 is listed as not
implemented. The conformance workflow runs the -18 and -16 independent
decoders beside -22's. Crate docs and descriptions name the three drafts.
joule-code and wai CHANGELOG entries.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
-16 §9.1 asks for each new Request ID to be "the next in sequence", but its
SUBSCRIBE_NAMESPACE rides a bidirectional stream of its own while the other
requests ride the control stream, and QUIC does not order one stream against
another. A public relay network's -16 endpoint opened a SUBSCRIBE_NAMESPACE
(ID 1) and sent a PUBLISH_NAMESPACE (ID 3) on the control stream; the 3 came
first and the bridge closed the session. The bridge now admits a new ID of the
peer's parity within the grant and leaves the gap for the one in flight; an ID
seen before still closes the session with INVALID_REQUEST_ID, and one past the
grant with TOO_MANY_REQUESTS. a_raw_16_session sends 4 before 2 and then 4
again.
wai-receipts collects until every object has arrived (by alias, group and
object), not a count of streams: a relay may carry a subgroup on more streams
than its publisher opened, and the count stopped the subscriber before the last
group.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Two races met when an interop test client finishes publishing: it sends
PUBLISH_DONE and its last data streams, then closes its control stream (an -18
§3.3 protocol violation) and its session. The relay closed the session on the
control stream's FIN at once, cutting streams still arriving, and its session
clean-up ended the publisher's tracks with GOING_AWAY, racing the PUBLISH_DONE
that had just come in, so subscribers saw GOING_AWAY and a reset stream.
Now a closed control stream closes the session once nothing of the peer's
publishing is left (every track it published ended, every data stream of its
read), or after PUBLISH_DONE_STREAM_WAIT. Session clean-up leaves each track to
its own request stream, which ends it with the PUBLISH_DONE status it read, or
GOING_AWAY; and a PUBLISH_DONE waiting on streams stops waiting once the
publisher's session is gone. Tested at -18 with a session written from -18's
messages, and over -22, -18 and -16 with a close after PUBLISH_DONE.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
-18 cancels SUBSCRIBE_NAMESPACE and SUBSCRIBE_TRACKS by "closing the stream
with either a FIN or RESET_STREAM" (§6.1) and withdraws PUBLISH_NAMESPACE by
cancelling the request (§6.2); it has no FIN that leaves a request standing.
-22 has one (§6.4.2.2). An -18 test client that withdraws its namespace with a
FIN saw no NAMESPACE_DONE through the relay. The -18 bridge now reads an -18
requester's FIN on those three requests as a cancellation, and withholds a -22
requester's FIN on them from an -18 peer, which would read it as one. Tested
with an -18 session written from -18's messages, a -22 publisher and a -22
client over -18.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A publisher that ends its track (PUBLISH_DONE TRACK_ENDED) and a second that
publishes the same track afresh from group 0: the relay kept the first
instance's cache, its Largest Location and which upstream each subgroup came
from, so the second's group 0 was dropped as a duplicate and the subscriber was
told the first's Largest Location. An interop test client that runs a
publish-only test and then a publish-and-subscribe test on the same track met
exactly this. When nobody publishes a track its publisher ended, a new upstream
now starts it clean; a track the DVR store keeps is kept whole, and a track
whose publisher's session dropped (no TRACK_ENDED) keeps its cache.
An upstream's PUBLISH_DONE status is kept while the relay waits for its last
streams, so a session that closes in that wait ends the track with what the
publisher said; and upstream_gone ends a track only for a session that
published it, so the second call for one session is a no-op.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A publisher's PUBLISH_DONE and its data streams are not ordered with each other
(draft-ietf-moq-transport-22 §9.9). The relay ended the track downstream on the
message and dropped the alias, so a stream still in flight was lost: an -18
test client over WebTransport saw PUBLISH_DONE and no object. The relay now
counts each upstream data stream as its reading ends and, after PUBLISH_DONE,
waits until it has read the count, or three seconds; 0 and the unknown count
end at once. publish_done_streams tests the order at -22, -18 (QUIC and
WebTransport) and -16, a count no stream meets, and 0 and 2^64-1.
The -18 and -16 bridges log each message in and out at debug level, and the
interop client answers requests a namespace-only publisher is sent (a
TRACK_STATUS probe) with DOES_NOT_EXIST. scripts/clients.sh runs other
implementations' interop test clients against the relay on loopback;
public.py keeps each run's debug log.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
tests/draft18_vectors.rs and tests/draft16_vectors.rs generate each draft's
corpus (every control message, filters, FETCH types, streams, datagrams,
the drafts' own examples, and negative vectors for what each draft
refuses that a later one reads). moqt18_check.py and moqt16_check.py
decode them from the drafts' text alone. The READMEs table every
difference; moq-streaming-format says which drafts the reference speaks.
joule-moq-interop: the PUBLISH flow, WebTransport without verification,
and inclusion and property survival reported apart.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A client for any MOQT relay at -22, -18 or -16, with the runner's
test-client interface (TAP 14) and its draft-18 test cases, and
wai-receipts: a WAI track, its receipts track and a catalog through the
relay, every object verified at the subscriber against the publisher's
signed receipts, with --tamper payload|receipt|in-flight to show the check
fails. scripts/local.sh runs it against joule-moq-relay on loopback.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Draft::select takes the most preferred draft a client offers (-22 first),
as the relay's TLS does. The relay client takes extra Setup Options
(AUTHORITY and PATH for a moqt:// URI), and the relay tests can run any
session at -18 or -16.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The relay offers moqt-22, moqt-18 and moqt-16 by ALPN and by
WT-Available-Protocols, and transport::bridge puts a session of another
draft behind its -22 interface: -18 stream by stream, -16 by routing
each request of its one control stream to a stream of its own (Request ID
renumbering, MAX_REQUEST_ID credit, cancel messages). The client side is
the same wrap. tests/drafts.rs runs the two-publisher scenario with WAI
receipts at -18, at -16 and across mixed drafts, FETCH from the cache at
-18 and -16, a refusal by name, and raw -18 and -16 sessions.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
-16 writes every field with QUIC's integer (RFC 9000 §16), carries every
request and response on one bidirectional control stream by Request ID,
encodes Message Parameters as Key-Value-Pairs, and numbers some codes
differently (PUBLISH_DONE EXPIRED/TOO_FAR_BEHIND, stream reset
UNKNOWN_OBJECT_STATUS). Its module is its own; draft16::bridge maps each
message and object to and from -22, re-encoding the pairs inside
Immutable Extensions.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The Draft enum speaks -22 (default), -18 and -16 by ALPN. -18 is a module of
its own: its Setup Options, Message Parameters (SUBSCRIPTION_FILTER),
control messages (GOAWAY's Request ID, FETCH types with an exclusive end,
PUBLISH_BLOCKED, Table 5's PUBLISH_OK), codes, and the data plane, which is
-22's but for End of Timed-Out Range. draft18::bridge maps each message to
and from -22's model and names what one draft cannot say.
Co-Authored-By: Claude Opus 5.5 <[email protected]>