wai: live metering, a room session's units and joules per hop (L9) #317
Loading…
Reference in a new issue
No description provided.
Delete branch "wai/live-metering"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Based on #293 (
joule-moq/relay), which has since merged into main (91de95ee). I cut this branch fromorigin/joule-moq/relayand mergedorigin/maininto it (merges, never a rebase). It now includes main at91de95ee, with #293, #315 and wai-quantum 0.6.3, so the diff againstmainis this PR's alone.The relay change of this PR is joule-code-only commits:
joule-moq-relay: the per-hop session record of a WAI room... the session's opening tally in one initializer (clippy)... the metering test reads concurrently and ends its subscribers first... the metering test states what the record says of a closing sessionWhat this is
Slice L9 of the live work: metering and energy for a room session,
wai.live.metering.wai/extensions/live-metering.md(new): the per-hop session recordWMH1, the room-session receiptWMR1, verification, QoE observations, the C2PA mapping, and eighteen refusal reasons.wai/wai-rs/src/live_metering.rs, featurelive_metering(=live_compose+capture_take+telemetry). It iscbindgen:ignoreand adds no dependency. It covers:aggregate);verify_session);application/cmcdbatch body;capture_take::find_take(new) exposes capture-binding §9.2, so a take's committed capture can be counted.c2pa_emit(withlive_metering):SessionManifestwrites a receipt into a real signed C2PA manifest, andrecover_sessionreads it back.wai/live-conformance/metering/: 45 vectors from fixed seeds, plus one record the real relay signed in its test (relay-record.wmh,relay-ledger.wml).wai/live-conformance/live_metering_verify.py: the independent second implementation, standard library only.joule-moq-relay: signs the per-hop record of a room from its session summaries. New testtests/room_metering.rs.Registry and CI:
capabilities.json,vocab/v2.ttl);WMH1andWMR1;live/*authorities in the jwp-receipts §7.5 table;live-conformanceexclusion reason inint_kernels_vectors, updated.The design
Per-hop session record (
WMH1, §3)A relay, compositor or ingest gateway signs one record per room session. It holds:
HwShunttag is refused (energy_invalid).Room-session receipt (
WMR1, §4)It names:
It states:
classes_summed, and so is a total of class X that equals X's sum plus one or more other classes' sums;A verifier recomputes every number from the records it names (§5.1 steps 1–11).
Design choices to check
The metering power is a key-log authority,
live/meter, not a grant power.capture/storedoes for the store.energy/<class>for every class it totals (jwp-receipts §7.5).live/relay,live/compositororlive/ingest, plusenergy/<class>for a metered figure.Size bound:
B(M) = 552 + 180 × (M + 2)bytes.Privacy rules (§6.4).
qoe_not_suppressed.sid, timestamps, URLs and per-session metrics stay with the session and the room's service.sid, never a principal key.Joules attributed to a grant are an allocation, not a measurement.
leave(reason 2) or a grant exchange. The receipt counts what happened and names the grant.Funds come from a stated tariff. Prices are u32 millionths of a minor unit, per second of each unit and per event sent. Funds =
⌈N / 10^12⌉, saturating at 2^64 − 2; 2^64 − 1 means not computed.Composition-seconds and recorded-seconds.
CMCD checks.
application/cmcd: a batch is one report per LF-separated line, with no trailing LF, sorted keys and no URL encoding. delivery-telemetry's Event-mode reports are such lines already. CTA-5004-B cites RFC 8941, which RFC 9651 obsoletes, and the reports use no type RFC 9651 added.draft-wilaw-moq-cmcd-event-timeline-00,sfand the WAI custom keys are not on the permitted list, so they are not carried, ande=rris barred.The relay's session summary format moves to
/2. The summary gains:[[metering.grants]], keyed by token hash.A datagram's bytes are now counted in bytes received.
The
sched.rsedits are tally counters only: anobject_idonObjMeta, and per-track fetch bytes, incremental objects and queued-at-close counts. No ordering, timeout or flow logic changed. The base merge brought inConnection::diagnosticsandRelay::session_diagnosticswith no conflict.Implemented vs specified
cmcd_timeline_recordandcmcd_batch_body;c2pa_emitwith the real SDK:c2pa.environmental-sustainabilitycarriesenergy_kwhonly when one class covers the session whole;science.transaction.wai.live.meteringis always written;Valid, and recover the assertions.Corpus and verifiers
lesson: relay and compositor both on an on-chip counter, two takes, ten composition receipts, three logs (cell suppressed), a EUR tariff, signed by the metering key;signed_by_authority;mixed_classes: counter, model and estimate, three totals;ceilings_past: one grant past its joule ceiling, another past its funds ceiling;large_audience: 10,000 extra sessions, the same record size, 60 logs with the cell published;relay_recorded: the real relay's record, unmetered, so coverage is partial.joule-moq-relay'sroom_meteringtest signed it on the build host on 2026-10-18:include_bytes!and writes the relay room's lifecycle around its span;wai_live_metering_vectors relay-roomprints.live_metering_verify.py:rbcommands and exit statusAll cargo work ran on the build host through
~/kani-logs/remote/rb, with logs on the host. Each status isrb's, which is the command's own.rb . -- cargo build --no-default-features --features live_metering --lib --testsrb --pull . -- wai_live_metering_vectors relay-room; … gen ../live-conformance/meteringrecord_invalidvector restated role 4 in the receipt and was refused at the receipt. Generator fixed.rb --pull . -- … genrb --pull . -- WAI_METERING_FIXTURE_DIR=… cargo test -p joule-moq-relaymetering.rs, fixedroom_meteringamong them; the pair recordedrb --pull . -- gen with the relay's record; cargo test --lib live_metering::; --test live_meteringrb . -- python3 -I live-conformance/live_metering_verify.py live-conformance/meteringrb . -- cargo test --no-default-features --features "live_metering c2pa_emit" --lib -- c2pa_emit:: live_metering::tests::rb --pull . -- checks(see below)grepfinding no lockfile error. Every step in it was 0.rb . -- final checks(see below)159d7ad9:rb . -- cargo test -p joule-moq-relay×3room_metering. A publisher's close ended a subscriber's media subscription before the subscriber's own session ended, so the subscriber briefly held only a receipts track (class 8). The accounting is correct; the test raced. Fixed.rb . -- full relay suite; --test room_metering ×10; cargo clippy -p joule-moq-relay --all-targetsrb --pull . -- re-record the pair; genrb . -- … verify; --test live_metering; --lib live_metering::; live_metering_verify.pyrb . -- room_meteringbinary, 16 concurrent loops of 4 runs (64), with the failure-only span reportorigin/mainat91de95ee(#293, #315, wai-quantum 0.6.3; CHANGELOG conflict resolved keeping every entry):rb . -- cargo test -p joule-moq-relay; clippy; registry export --check; metering vectors verify; --test live_metering; --lib live_metering:: codecs::registry; int_kernels_vectors (cargo-class features); clippy live_metering; live_metering_verify.pyint_kernels_vectors8 passed; clean tree.Run 9's steps:
touch cbindgen.tomlthen a build:include/wai.hunchanged;wai registry export, then--check: 0, 0;--lib codecs::registry: 0 (14 passed);--test live_metering: 0;int_kernels_vectorswith the cargo-class step's exact features (neural_int,video_int,audio_int,world_int,splat_int,generate,score,staged,stage_measure,legacy_int,i32_forms): 0 (8 passed);live_meteringand for the relay: 0, with 4 notes on my code, fixed afterwards;scripts/check-lockfiles.sh: 0.Run 10's steps, all 0:
--lib live_metering::(9 passed);--test live_metering(4);wai_live_metering_vectors verify(both PASS lines);--test capture_take(4);c2pa_emit::withlive_metering(11);registry export --check;cargo test -p joule-moq-relay, every binary;Locally (light scripts), all 0:
check-tests-in-ci.py;check-workflow-concurrency.sh;check-spec-refs.py;check-wai-section-refs.py, after two references to delivery-telemetry's paragraph labels were moved to its §3;check-no-vanity-metrics.sh;check-comparative-prose.sh;check-orphan-sources.py.check-public-names.shneeds the secret denylist and runs on the push to main. I scanned the new text by hand.The CI failure on
d2fb5103, and its fixWhat failed. Job 34155,
joule-code conformance gate / joule-moq-relay, every target, failed attests/room_metering.rs:235, "every subscriber held layer 0 of the room's video". The test assumed no subscriber time outside layer class 0.Cause, reproduced under load. As a session closes, the relay ends its subscriptions one at a time, each when its request stream fails. A moment after the session's last media subscription has ended, but before its receipts or events subscription ends, is time on no media track. That time is class 8, as §3.3 defines.
I added a failure-only report of every subscriber session's layer spans, relative to the instant the test began closing them. I then ran the test 64 times (16 concurrent loops of 4) on the build host at a load average of about 97:
Decision: the classification is right, and the test's assumption was wrong. The session really held only a receipts and an events subscription in that interval.
The test now asserts what the record says. It requires:
On failure it reports every session's spans. All 64 runs passed.
Text. live-metering §3.3 and the relay README now say how a closing session is classed: the moments between subscription ends are classed by what is still held, typically microseconds of class 8 at a session's end.
What is left
e59bc14e.🤖 Generated with Claude Code