wai: wai.capture.take — a device's master recording bound to a live room (L4) #289
Loading…
Reference in a new issue
No description provided.
Delete branch "wai/capture-take"
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?
Slice L4 of WAI's live and interactive work:
wai.capture.take, which binds a device's full-quality master recording to a live room. It is for a lesson or an audition, where the live call is low quality and the master is made on the performer's own device and uploaded afterwards. While the device captures, it publishes rolling hash commitments of its master into the room's event track, where the room clock stamps them and the relay and every other participant hold them. After the session, the upload is checked against those commitments, so it can prove it is one continuous, unedited take committed while it was being captured. The take also carries each subject's consent, with a guardian's for a minor.Stacked. This PR is stacked on #284 (L2,
wai.live.room, branchwai/live-rooms), which is stacked on #279 (L1,wai.live.events). Its base ismain, so until those two merge, its diff includes theirs. The commits of this slice are the ones after440f7dae. Not merged; for review.What it adds
wai/extensions/capture-binding.md. The extension text, in the house style (status header, the texts read, RFC 2119 keywords, a reasons table, conformance, and security and privacy).record_startaddressed to that device.SHA-256("wai:capture-take\x01" ‖ room ‖ device ‖ digest of the record_start).subject, so a per-room device key can capture for a known principal. The attributeminorlets the room authority mark that principal a minor.record_stopto the device after the start.SHA-256("wai:out-node\x01" ‖ l ‖ r), over leaves of the take's own domain (wai:take-leaf\x01).output_tree.rsgainstree_hash, a publicnode,consistency_proofandverify_consistency. These are additive; the output tree's own bytes do not change.take_commitpayload that L1 reserved.n, phase (open, running, close),prev(the previous commitment's event digest), the chunk range added, the running root, the device's offset to the room clock, and the consistency proof.record_start; the closing one comes after therecord_stop.WUR1receipts of its state machine: open, progress, resume, sealed.capture/store(registered in jwp-receipts §7.5).WTM1). Signed by the device. It lists the chunks, the final root, everytake_commitdigest, the subjects with their consent digests, and the claims and purposes it makes.WCN1).live,record,distribute,train), the room it is for, a window, an optional guardian, and a nonce.revoked_modules(jwp-receipts §7.6 updated).single-take, in order:delivery_gap, L1's code);aligned: chunk 0's mapped start is within the jitter of the count-in instant.complete: every committed chunk was uploaded, and the store sealed the upload.publishable{purpose}, per subject: consent missing, guardian missing for a minor, consent revoked, consent expired, or purpose not consented.complete, notsingle-take(§9.4 says so). The spec also says that a party promised an unedited single take needs both claims, becausesingle-takealone holds for a partial upload.single-takeandalignedhold.c2pa.livevideo.segmentwithsequenceNumber,streamId,continuityMethodandpreviousManifestId;emsgwithurn:c2pa:verifiable-segment-info/fseg, a COSE_Sign1 oversequenceNumber,bmffHashandmanifestId, and the device key as an Ed25519 session key inc2pa.session-keys, bound bysignerBinding;science.transaction.wai.takeassertion that points a C2PA validator at the room's commitments;c2pa.ai-disclosure, mapped from a caption'soriginand model digest;c2pa.environmental-sustainability, only from a measured figure, under energy-binding's rules.wai/wai-rs/src/capture_take.rs(featurecapture_take = ["live_room"],cbindgen:ignore). It implements everything above:verify_takeoverlive_room::verify_room, with every claim and the consent checks;position(§10) andstream_id(§11.1).Its unit tests cover the tree and its consistency proofs, the commitment payload, consent signing, the stream id, the position mapping and distinct reason codes. It adds no dependency.
include/wai.his unchanged (see Runs).wai/live-conformance/takes/: 38 vectors from fixed seeds (wai_capture_take_vectors gen), with a README carrying the provenance linetests/corpus_provenance.rsrequires.lesson(the director's remoterecord_startwith a count-in, ten chunks, commitments every 2 s, two markers mapped into the master,record_stop, the upload sealed in DE within the DE/FR pin, the performer's consent),resumed_upload(open, progress, resume, progress, sealed; chunk 3 sent twice) andminor_guardian. All three stand.publishable{train}failspurpose_not_consentedbecause the performer did not consent to it.commit_gap:delivery_gap;spliced_chunk:chunk_altered;reordered_chunk:chunk_reordered;extra_chunk:chunk_extra;missing_chunk:completefailschunk_missing, andsingle-takeholds;late_commit:commit_late;root_not_extending;misaligned;consent_missing,consent_expired,consent_revoked;minor_without_guardian:guardian_missing;device_not_bound.wai/live-conformance/capture_take_verify.py. Independent and standard library only:_ed25519.py;live_rooms_verify.py, which useslive_events_verify.pyfor the events;It agrees on every vector, reason, place, verdict, digest and marker position. It then mutates copies of the corpus (18 defects: bytes of parts, signatures, descriptors and consents, a dropped part, seal or commitment, a later evaluation instant, and changed stated roots, ids, positions, verdicts, places and reasons) and requires every copy to fail. All 18 do.
Changes to L2 (
wai.live.room). The brief says the commit interval is "set by the room's recording policy", and L2's policy had no such field, so the descriptor's recording policy gainscommit_interval_msandjitter_ms:modeis 0;live_room.rs,live_rooms_verify.py, and a new descriptor vectordescriptor_recording_jitter;subject,minor).L1's §4.12 now points at the payload defined here.
CI and registry.
capture takestep in the independent-verifiers job ofwai-conformance.ymlruns:capture_take::andoutput_tree::;tests/capture_take.rs;capture_take_verify.py.WTM1,WUR1andWCN1as wire identifiers.wai registry exportwas re-run on the build host (registry/capabilities.json,vocab/v2.ttl).C2PA mapping: specified, not implemented
The mapping is specified, and its implementation deferred, except for the stream id (an RFC 9562 version-8 UUID of the take id), which is implemented and stated in the corpus.
wai-rshas no CBOR or COSE encoder.c2pa_emituses writes whole-asset manifests and fragmented-BMFF manifests (one manifest in the initialization segment and a Merkle tree over the fragments). It has no live-video segment assertion, noc2pa.session-keysand no Verifiable Segment Info.Deferred
science.transaction.wai.takeassertion. It needs CBOR and COSE.tis its author's claim, bounded by the room's merged order and the timing rules.c2pa.environmental-sustainability(L9).Runs
All cargo work ran on the build host through
~/kani-logs/remote/rb <worktree> -- '<command>', under memguard, with output written to logs on the host and grepped there. Nothing was compiled or tested on the dev Mac.rb --pullwith these three commands. rb exited 1 because the unit tests failed; the two corpus generators exited 0.cargo test --no-default-features --features capture_take --lib -- capture_take:: output_tree:: live_room::: two tests failed, both on wrong expectations in the tests themselves:Both tests are fixed, and §4.3 now states that a commitment carries its size.
wai_live_room_vectors genandwai_capture_take_vectors gen: exit 0. Every take vector reached the outcome it was written for.rb --pull: exit 0. Each command below exited 0:wai registry exporttouch cbindgen.toml && cargo build --no-default-features --features capture_take --lib, thengit diff --quiet -- include/wai.h: the header is identicalcargo test --no-default-features --features capture_take --lib -- capture_take:: output_tree:: live_room:: live_events::: 30 passedcargo test --no-default-features --features capture_take --test capture_take --test live_room --test live_events: 4 + 4 + 4 passedwai_capture_take_vectors verify ../live-conformance/takesandwai_live_room_vectors verify ../live-conformance/rooms: both regenerate byte for byte, and every vector gets its stated outcomecargo test --no-default-features --lib -- codecs::: 53 passed, including the wire-identifier and section testscargo test --no-default-features --test carriage --test manifest_builders --test cli_wrap_intent --test cli_registry --test corpus_provenance: all passed, includingregistry export --checkand the provenance linecargo clippy --no-default-features --features capture_take --lib --tests --bins: no warning in any file this PR adds or changes. The single warning onoutput_tree.rsis at line 310, in that module's existingproof_roundtriptest.rb:cargo test --no-default-features --features neural_int,video_int,audio_int,world_int,splat_int,generate,score,staged,stage_measure,legacy_int --test int_kernels_vectors(the coordinator's addition): exit 0, 8 passed.Locally (light scripts, standard library):
python3 -I live-conformance/live_events_verify.py live-conformance/events: PASS, 45 vectors.python3 -I live-conformance/live_rooms_verify.py live-conformance/rooms: PASS, 64 vectors, with the regenerated rooms.python3 -I live-conformance/capture_take_verify.py live-conformance/takes: PASS, 38 vectors; all 18 mutated copies fail.check-lockfilesand its self-test;check-tests-in-ciand its self-test;check-workflow-concurrencyand its self-test;pr-touches-pathsself-test;check-spec-refsand its self-test;check-wai-section-refs;check-no-vanity-metrics;check-comparative-prose.check-public-nameswas not run, becauseWAI_NAME_DENYLISTis not set here. Instead the diff was searched for third-party names, and none were found.Merge:
wai/live-roomsat440f7daewas merged into this branch first (merge, not rebase), so it carries L2's fix forint_kernels_vectors(thelive-conformanceexclusion, which coverstakes/).🤖 Generated with Claude Code