# WAI Extension: Capture Binding > Status: Draft. Registers the record `wai.capture.take`: the binding of a device's > full-quality **master** recording to a live room, so that the master, uploaded after > the session, proves it is one continuous take committed while it was being captured. > While it records, the device publishes rolling commitments to its master, `take_commit` > events ([live-events](live-events.md) §4.12) in the room's event track, where they are > stamped with the room clock and held by the relay and by every other participant. After > the session the device uploads the master, resumably and by content address, the store > records the upload in signed receipts, and the device signs a **take manifest** listing > the chunks, their root, every commitment and the consent of every principal the master > records. A verifier checks the manifest against the room's events and gives each claim > its verdict: `single-take`, `aligned`, `complete` and `publishable` for each purpose. > The reference case is a lesson or an audition, where the live call is low quality and > the master is made on the performer's own device. No new transport: the commitments are > events, and its C2PA live video output is a take stream of > [live-provenance](live-provenance.md) (§11). The > reference implementation is the `capture_take` module of `wai-rs` (feature > `capture_take`); the corpus is [`live-conformance/takes/`](../live-conformance/takes/), > and its second implementation is `live-conformance/capture_take_verify.py`. > Keywords **MUST**, **MUST NOT**, **SHOULD**, **MAY** are RFC 2119/8174. > > Texts read for this revision: RFC 6962 §2.1 and RFC 9162 §2.1.4 (the Merkle tree hash > and its consistency proofs); ISO/IEC 23000-19 (CMAF) and ISO/IEC 14496-12; and the C2PA > Technical Specification, version 2.4: §13.2.1, §18.15, §18.25, §18.26, §18.28 and §19. > A section number of that specification in this text is that version's. ## 1. Scope In scope: how a device and a take bind to the principal they capture for, the take id, and what a `record_start` and `record_stop` pair brackets (§3); the chunks of a master and the tree over them (§4); the `take_commit` payload and the chain of a take's commitments (§5); the upload, by content address, and the store's signed receipts (§6); the take manifest (§7); the consent record (§8); verification of a take and the verdict of each claim (§9); the mapping from a room-clock instant to a position in the master, and its bound (§10); and the mapping of a take and its CMAF output onto C2PA live video (§11). Out of scope: a transport; the master's codec and container (a take commits to its bytes, whatever they encode); how a store is operated; the room's own recording ([live-recording](live-recording.md)); per-object receipts of the relay that witness when a commitment was seen (a later extension); and what a party does with a verdict. Relationship to the core spec: `wai.capture.take` is a **record** ([SPEC](../SPEC.md) §5): a signed record a WAI tool emits and verifies. It names no payload format and is not dispatchable. ## 2. Terms - **Room**, **room clock**, **principal**, **event**, **chain**, **grant**, **grant in effect**, **room key**, **merged order**: as in live-events §2 and live-rooms §2, §9.2. - **Device**: the Ed25519 key (RFC 8032) of the device that captures. It takes part in the room under a grant (live-rooms §4) as a principal: it signs its `take_commit` events and its take manifest. - **Master**: the encoded recording the device makes, at the quality it captures. - **Device clock**: the clock the device stamps its capture with, in microseconds. Its epoch is the device's. - **Offset**: the room clock less the device clock, in microseconds, as the device measures it: a device-clock instant *d* is the room-clock instant *d* + offset. - **Chunk**: a contiguous run of the master's bytes (§4.1). - **Take**: one continuous capture on one device, answering one `record_start` (§3). - **Store**: the service the master is uploaded to, named by its Ed25519 key (§6). - **Subject**: a principal the master depicts or records. - **Purpose**: a use a subject consents to: `live` (1), `record` (2), `distribute` (4), `train` (8), each a bit of a purposes byte. - *I* and *J*: the room's `commit_interval_ms` and `jitter_ms` (live-rooms §3.7), each times 1000, in microseconds. Integers are little-endian. A digest is SHA-256 (FIPS 180-4), 32 bytes. An event's digest is live-events §3.2's. ## 3. The device, its principal and the take ### 3.1 Binding a device to a principal A device captures for one principal, which its grant names: the device's grant in effect **binds** it to principal *P* when the grant's attributes (live-rooms §4.4) hold the key `subject` whose value is *P*'s key as 64 lowercase hexadecimal digits, or, when they hold no `subject`, when *P* is the device's own key. The room authority signs the grant, so the binding is the authority's statement that this device captures for *P*: a device can be a key of its own (one per room, live-events §11) and still capture for a principal its participants know. A principal is **marked a minor** in a take when the device's grant that binds it holds the attribute `minor` with the value `true`, or when its consent record says so (§8.1). ### 3.2 The take id A take is named by its **take id**, derived and never chosen: SHA-256 over the 18 bytes `wai:capture-take` (ASCII) and 0x01, then the room id, the device's key, and the digest of the `record_start` event the take answers. One `record_start` addressed to one device names one take. ### 3.3 What a take brackets A take answers a `control` event (live-events §4.9) whose command is `record_start` and whose `target` is the device, sent by a principal with the record power (live-rooms §3.3, §9.2 step 5). Its **start** is that event. Its **capture instant** is the room-clock instant `t` + `count_in_ms` × 1000 of the start: the count-in fixes when capture begins, for every participant alike. Its **stop** is the first `control` event in the merged order after the start whose command is `record_stop` and whose `target` is the device; a take need not have one. A second `record_start` to the same device before the stop starts another take. The device's first commitment of the take **opens** it, in answer to the start; its last **closes** it, after the stop (§5.3). A room whose recording policy has `mode` 0 is never recorded, and no take in it is permitted (§9.1). The interval *I* and the jitter *J* a take is held to are the room's (live-rooms §3.7), so no device chooses its own. ## 4. Chunks and the take tree ### 4.1 Chunks The device cuts its encoded master into chunks, by bytes or by duration, in the order the master's bytes run. Where the master is CMAF (ISO/IEC 23000-19), chunk 0 is the CMAF header followed by the first CMAF segment, and each later chunk is one CMAF segment, so that the header, whose edit lists could reorder what a segment presents, is committed with the first chunk. A chunk is described by 64 bytes, its **descriptor**: | offset | size | field | meaning | |---|---|---|---| | 0 | 8 | `index` | u64: the chunk's position in the master, from 0 | | 8 | 8 | `start` | u64: the device-clock instant its capture begins | | 16 | 8 | `end` | u64: the device-clock instant its capture ends (exclusive) | | 24 | 8 | `len` | u64: its length in bytes, at least 1 | | 32 | 32 | `hash` | the digest of its bytes | A chunk holds the encoded samples captured from `start` to `end`. Its capture range is the device's statement; the commitments fix it while the capture runs (§5), and the verdicts hold the device to it (§9.4). ### 4.2 The take tree The take tree is the tree of [staged-measurement](staged-measurement.md) Appendix C: RFC 6962's Merkle tree hash over SHA-256, its node that appendix's, over leaves of the take's own domain: ``` leaf = SHA-256("wai:take-leaf\x01" ‖ descriptor) 64-byte descriptor, §4.1 node = SHA-256("wai:out-node\x01" ‖ left ‖ right) staged-measurement Appendix C ``` The **root** over the descriptors of chunks 0 to *n* − 1, *n* ≥ 1, is RFC 6962 §2.1's `MTH`: one leaf is itself; otherwise `node(MTH(D[0:k]), MTH(D[k:n]))`, with *k* the largest power of two below *n*. No leaf is duplicated, so a root and its size fix the list of descriptors. The root over no chunk is 32 zero bytes. A **commitment** is a size and its root; as there, a root alone is not one. The leaf binds each chunk's index, its capture range, its length and its hash, so a commitment fixes when each chunk was captured as well as what it holds. ### 4.3 Consistency A commitment (*n*, root′) **extends** (*m*, root) when the first *m* chunks under root′ are the chunks under root. The proof of it is RFC 9162 §2.1.4.1's consistency proof, the `PROOF(m, D[n])` of RFC 6962 §2.1.2, a list of nodes, checked as RFC 9162 §2.1.4.2 checks one, with the node of §4.2 in place of its `HASH(0x01 ‖ …)`. RFC 9162 leaves two cases to the caller, and this extension decides them: every commitment extends (0, zero), with an empty proof; and (*m*, root′) extends (*m*, root), with an empty proof, exactly when the roots are equal. Otherwise 0 < *m* < *n* and the proof is not empty. A witness that holds only the previous commitment checks that the next extends it: the relay and every participant can check, while the take runs, that the device never rewrites what it has committed. A proof does not fix either size, as an audit path does not (staged-measurement Appendix C): a commitment states its size, and a verifier that holds the chunks recomputes every root under the size stated (§9.4). ## 5. Commitments ### 5.1 The `take_commit` payload Every *I* while it captures, the device emits a `take_commit` event (live-events §4.12) in the room, on its own events track. Its payload: | field | size | meaning | |---|---|---| | `take` | 32 | the take id (§3.2) | | `n` | 4 | u32: the commitment's number in the take, from 0 | | `phase` | 1 | 1 open, 2 running, 3 close | | `prev` | 32 | the digest of the take's commitment `n` − 1; 32 zero bytes when `n` is 0 | | `first` | 8 | u64: the index of the first chunk this commitment adds | | `size` | 8 | u64: the chunks committed so far; this commitment adds chunks `first` to `size` − 1 | | `root` | 32 | the root over chunks 0 to `size` − 1 (§4.2) | | `offset` | 8 | i64, two's complement: the offset (§2) the device measures at the event | | `proof_len` | 1 | the number of nodes in `proof`, at most 64 | | `proof` | 32 × `proof_len` | that (`size`, `root`) extends the previous commitment (`first`, its root) (§4.3) | The fixed part is 126 bytes, so a proof of up to 28 nodes fits live-events' default `max_payload` of 1024. The chunk range it names, `first` to `size` − 1, is what a witness that missed the previous commitment still learns. ### 5.2 Reading a payload A reader reads the payload in the order of §5.1 and refuses it as `commit_malformed`: when it ends before a field or holds bytes after `proof`; when `phase` is not 1, 2 or 3; when `size` is below `first`; when `proof_len` is above 64; or when `size` is 0 and `root` is not 32 zero bytes. A room verifier (live-rooms §9) carries the payload opaque, as live-events §4.12 has it; a take verifier reads it (§9.2). ### 5.3 The chain of a take's commitments A take's commitments are the `take_commit` events of the device whose `take` is the take id, in the order of the device's chain. They form their own chain: - The **opening** commitment is the first: `n` 0, `phase` open, `prev` zero, `first` and `size` 0, and no proof. The device emits it when the start reaches it, before its capture begins. - Each later commitment `n` has `n` one above the previous one's, `prev` the previous one's digest, `first` the previous one's `size`, and `phase` running or close. It adds the chunks the device has completed since the previous one, perhaps none, and proves that its commitment extends the previous one (§4.3). - The **closing** commitment, `phase` close, is the last: after the stop, it adds the chunks captured up to the stop, and no commitment of the take follows it. The chain needs nothing a witness does not hold: `n` and `prev` make a commitment that was not delivered a gap in the take, as live-events §6.2 makes it one in the device's chain, and the proof makes a rewritten history a commitment that does not extend. ## 6. Upload ### 6.1 Parts After the session the device uploads its master to a store, chunk by chunk, each chunk a **part**: its index and its bytes, addressed by the chunk's hash. The upload is resumable: a part that did not arrive is sent again, and a part sent twice is the same part. A verifier fetches the parts from the store by hash and is given them as a list of (index, bytes) in the order it received them. ### 6.2 The upload receipt The store records the upload's state in signed **upload receipts**, `WUR1`, one at each change of state: | offset | size | field | meaning | |---|---|---|---| | 0 | 4 | `magic` | `WUR1` (ASCII) | | 4 | 1 | `version` | 1 in this revision | | 5 | 32 | `store` | the store's Ed25519 key | | 37 | 32 | `take` | the take id | | 69 | 8 | `seq` | u64: the receipt's position in the upload's chain, from 0 | | 77 | 32 | `prev` | the digest of receipt `seq` − 1; 32 zero bytes when `seq` is 0 | | 109 | 8 | `at` | u64: the store's clock, in microseconds since 1970-01-01T00:00:00Z | | 117 | 1 | `state` | 1 open, 2 progress, 3 resume, 4 sealed | | 118 | 8 | `held` | u64: the chunks the store holds, from index 0 with none missing | | 126 | 32 | `root` | the root over those chunks (§4.2), zero when `held` is 0 | | 158 | 2 | `region` | ISO 3166-1 alpha-2: where the store keeps the chunks | | 160 | 64 | `sig` | Ed25519 by `store` | The **digest** of a receipt is SHA-256 over its bytes before `sig`. The **signature** is Ed25519 by `store` over the 17 bytes `wai:take-upload` (ASCII) and 0x01, followed by the bytes the digest covers, verified strictly (SPEC §7 *Signatures*). A reader reads exactly the bytes it is given, as live-events §3.3 reads a record with `WUR1` for `WEV1`: `truncated`, `malformed` or `unknown_version`. ### 6.3 The state machine The receipts of an upload are a chain, and its states move only this way: - receipt 0 is `open`, with `held` 0; - `progress`: `held` above the previous receipt's, as parts arrive; - `resume`: `held` equal to the previous receipt's: an upload interrupted and resumed, which continues from the chunk at `held`; - `sealed`: `held` equal to the previous receipt's; the store holds the master, and no receipt follows. A verifier, given the receipts in a list, checks each in the list's order and refuses at the first that fails, naming it by its index: the record reads (§6.2); its `state` is 1 to 4, its `region` two capitals `A`–`Z`, and its `root` zero when `held` is 0, otherwise `upload_receipt_invalid`; its `store` and `take` are the manifest's (§7), `upload_receipt_invalid`; its signature verifies (`bad_key`, `bad_signature`); its `seq` is its index, its `prev` the previous receipt's digest, its `at` not below the previous one's, its `region` receipt 0's, and its `state` one the list above allows after the previous receipt, otherwise `upload_receipt_invalid`; its `held` is at most the manifest's chunk count and its `root` the root over the manifest's first `held` chunks, otherwise `upload_receipt_mismatch`; and, where the room's recording policy pins regions (live-rooms §3.7), its `region` is one of them, otherwise `store_outside_region`. ### 6.4 The store A room whose descriptor names an `origin` (live-rooms §5) names its stores in that origin's key log: a store's key is an entry of the head whose `may_sign` holds the authority `capture/store` ([jwp-receipts](jwp-receipts.md) §7.5) and whose `status` is not `revoked`. A verifier refuses a take whose store the head does not so list as `store_not_listed`. In a room with no origin the store is the one the manifest names, and its receipts are its statement alone. ## 7. The take manifest ### 7.1 Layout When the upload is complete, the device signs a take manifest, `WTM1`: | field | size | meaning | |---|---|---| | `magic` | 4 | `WTM1` (ASCII) | | `version` | 1 | 1 in this revision | | `room` | 32 | the room id | | `device` | 32 | the device's key, which signs the manifest | | `principal` | 32 | the principal the take is captured for (§3.1) | | `start` | 32 | the digest of the take's start (§3.3) | | `take` | 32 | the take id (§3.2) | | `store` | 32 | the store's key (§6) | | `claims` | 1 | the claims the manifest makes: bit 0 `single-take`, bit 1 `aligned`, bit 2 `complete` | | `purposes` | 1 | the purposes it claims the master `publishable` for (§2) | | `chunks` | 4 + 64 × *n* | a u32 count *n*, then each chunk's descriptor (§4.1), in index order | | `root` | 32 | the root over the *n* chunks (§4.2) | | `commits` | 4 + 32 × *m* | a u32 count *m*, then the digest of each of the take's commitments, in the order of `n` | | `depicted` | 1 + 64 × *k* | a u8 count *k*, then each subject: its key (32) and the digest of its consent record (32; zero for none) (§8) | | `sig` | 64 | Ed25519 by `device` | The **digest** of a manifest is SHA-256 over its bytes before `sig`; its **signature** is Ed25519 by `device` over the 21 bytes `wai:capture-manifest` (ASCII) and 0x01, followed by the bytes the digest covers, verified strictly (SPEC §7 *Signatures*). ### 7.2 Reading and rules A reader reads exactly the bytes it is given, as live-events §3.3 reads a record with `WTM1` for `WEV1` (`truncated`, `malformed`, `unknown_version`), and refuses a manifest that breaks any of these rules as `manifest_invalid`: - `claims` sets no bit above bit 2, and `purposes` none above bit 3; - *n* is at least 1, each chunk's `index` is its position in the list, and each `len` is at least 1; - `root` is the root over the chunks; - *m* is at least 1, and no digest is listed twice; - *k* is 1 to 32, the subjects' keys in strictly ascending byte order, and `principal` is one of them; - `take` is the take id of `room`, `device` and `start` (§3.2). It then refuses a manifest whose `room` is not the room's as `wrong_room`, and one whose signature does not verify as `bad_key` or `bad_signature`. The manifest lists every subject the master depicts or records, the take's principal among them, and binds each to its consent. A claim bit or a purpose bit states what the device claims; a verifier gives every claim its verdict whether the manifest makes it or not (§9.4). ## 8. Consent ### 8.1 The consent record A subject's consent is a signed record, `WCN1`: | offset | size | field | meaning | |---|---|---|---| | 0 | 4 | `magic` | `WCN1` (ASCII) | | 4 | 1 | `version` | 1 in this revision | | 5 | 32 | `subject` | the subject's key | | 37 | 1 | `minor` | 1 when the subject is a minor, otherwise 0 | | 38 | 32 | `guardian` | the key of the subject's guardian; 32 zero bytes for none | | 70 | 1 | `purposes` | the purposes consented to (§2) | | 71 | 32 | `room` | the room it is for; 32 zero bytes for any room | | 103 | 8 | `nbf` | u64: seconds since 1970-01-01T00:00:00Z from which it holds | | 111 | 8 | `exp` | u64: the second before which it holds | | 119 | 16 | `nonce` | distinguishes two consents with the same fields | | 135 | 64 | `sig` | Ed25519 by the guardian when `guardian` is not zero, by the subject otherwise | The **digest** of a consent is SHA-256 over its bytes before `sig`. The **signature** is over the 20 bytes `wai:capture-consent` (ASCII) and 0x01, followed by the bytes the digest covers, verified strictly (SPEC §7 *Signatures*): a minor's consent is the guardian's act, any other the subject's own. ### 8.2 Reading A verifier, given consent records in a list, checks each in its order and refuses at the first that fails, naming it by its index: the record reads, as live-events §3.3 reads one with `WCN1` for `WEV1`; it holds these rules, otherwise `consent_invalid`: `minor` is 0 or 1, `guardian` is zero when `minor` is 0, `guardian` is not `subject`, `purposes` is not 0 and sets no bit above bit 3, and `nbf` is below `exp`; and its signature verifies (`bad_key`, `bad_signature`). A record marked a minor with no guardian reads: it is a record no minor's consent can rest on (§9.4). ### 8.3 Revocation A consent is revoked through the room authority's key log, as a grant is (live-rooms §5): its digest is added to the head's `revoked_modules`, which is append-only, so a revocation stays. A subject or guardian that withdraws consent asks the authority to list it. In a room whose descriptor names no origin no consent can be revoked, and a room that records subjects **SHOULD** name one. ## 9. Verification ### 9.1 Inputs and steps A take verifier is given the room (a descriptor, grants, the head of the authority's key log or none, and the chains, as live-rooms §9.1), the take manifest, the parts (§6.1), the upload receipts, the consent records, and an **evaluation instant** `at`, in seconds since 1970-01-01T00:00:00Z: the time its verdicts are about. It runs these steps and stops at the first refusal, which it reports with the reason and where: 1. **The room** (live-rooms §9): a refusal is the take's, at `room`, with the room's own place. A gap is not a refusal: the steps go on over the records the room verifier holds, and §9.4 says what a gap fails. 2. **The manifest** (§7.2): at `manifest`. 3. **The take in the room** (§9.2): at `manifest`, or at the record it names. 4. **The store** (§6.4): at `store`. **The receipts** (§6.3): at the receipt's index. 5. **The consents** (§8.2): at the record's index. 6. **The verdicts** (§9.4). ### 9.2 The take in the room Over the records the room verifier accepted, in the merged order (live-rooms §9.2), with each one's grant: 1. The recording policy's `mode` is not 0: otherwise `recording_not_permitted`. 2. The manifest's `start` is the digest of an accepted record that is a `control` event whose command is `record_start` and whose `target` is the manifest's `device`: otherwise `take_unknown`. 3. Every accepted `take_commit` event of the device has a payload §5.2 reads: otherwise `commit_malformed`, at that record. Those whose `take` is the take id are the take's **held commitments**, in the device's chain order. 4. At each held commitment, the device's grant in effect binds it to the manifest's `principal` (§3.1): otherwise `device_not_bound`, at that record. 5. The held commitments are the manifest's `commits`, in its order, with those not held left out; a listed digest that is not held is the digest of no accepted record, and the room verifier reports a gap in the device's chain. Otherwise `manifest_mismatch`. A commitment listed and not held is so a commitment the relay did not deliver, and the device's chain shows the gap; a manifest cannot list one the room holds out of its place, leave one out, or list one no gap can account for. ### 9.3 Mapping and timing The take's clock mapping is the offset *o* of its opening commitment: a device-clock instant *d* maps to the room-clock instant *d* + *o*, computed without overflow (the reference computes in 128-bit integers). A chunk's **mapped end** is its `end` mapped. For each commitment, its `t` is the room-clock instant of its event; the capture instant, the start and the stop are §3.3's. Every comparison in this section is between room-clock instants, in microseconds. ### 9.4 The verdicts Each claim holds, or fails with the first reason below that applies to it. **`single-take`** holds when the master is one continuous take, every chunk of it committed while it was captured, and every part uploaded is the chunk committed at its index: 1. `delivery_gap`: a commitment the manifest lists is not held. 2. `commit_chain_broken`: the held commitments are not a chain of §5.3: the first is not an opening one, a later one's `n`, `prev` or `first` does not follow its predecessor, a later one is an opening one, or a commitment follows a closing one. 3. `root_not_extending`: a commitment does not extend its predecessor (§4.3): its proof does not verify, a proof is given where §4.3 gives none, or a commitment that adds no chunk changes the root. 4. `take_not_closed`: the last held commitment is not a closing one, or the take has no stop. 5. `manifest_not_committed`: the manifest's chunk count is not the closing commitment's `size`, or, for a held commitment whose `size` is above 0, the root over the manifest's first `size` chunks is not its `root`. 6. `clock_unstable`: a commitment's offset differs from *o* by more than *J*. 7. `capture_discontinuous`: a chunk's `start` is not below its `end`, or a chunk's `start` is not the previous chunk's `end`. 8. `chunk_too_long`: a chunk's `end` − `start` is above *I*. 9. `commit_early`: the opening commitment's `t` is below the start's; the closing one's is below the stop's; or a commitment's `t` + *J* is below the mapped end of a chunk it adds (it committed a chunk before the chunk was captured). 10. `commit_late`: a commitment's `t` is above the mapped end of a chunk it adds plus *I* plus *J*. 11. Over the parts, in the order given, the first that is not the chunk committed at its index: `chunk_extra` when its index is not below the chunk count; `chunk_reordered` when its bytes' digest and length are another committed chunk's; `chunk_altered` otherwise. By rules 8 and 10, every byte of the master was committed within 2 × *I* + *J* of its capture on the take's clock mapping, and by rule 6 that mapping held to within *J* throughout. A part missing from the upload does not fail `single-take`: what was uploaded is what was committed, and the commitments verify. It fails `complete`. A party promised an unedited single take needs `single-take` and `complete` together: `single-take` alone holds of an upload that left chunks out. **`aligned`** holds when capture began at the instant the count-in fixed: 1. `delivery_gap`: the first commitment the manifest lists is not held. 2. `commit_chain_broken`: the first held commitment is not an opening one. 3. `manifest_not_committed`: no held commitment adds a chunk, or the root over the manifest's first `size` chunks is not the root of the first held commitment that does. 4. `misaligned`: chunk 0's `start`, mapped, differs from the capture instant by more than *J*. **`complete`** holds when the upload covers every committed chunk and the store sealed it: with *s* and *r* the `size` and `root` of the last held commitment, 1. `manifest_not_committed`: *s* is 0, *s* is above the manifest's chunk count, or the root over the manifest's first *s* chunks is not *r*. 2. `chunk_missing`: an index below *s* has no part whose bytes' digest and length are that chunk's. 3. `upload_not_sealed`: the last receipt is not `sealed`, or its `held` is not *s*. **`publishable`** for purpose *p* holds when every subject the manifest lists consented to the recording and to *p*. For each subject, in the manifest's order, the first that fails: 1. `consent_missing`: its consent digest is zero, or no consent record given has it, or that record's `subject` is not this subject, or its `room` is neither zero nor the room id. 2. `guardian_missing`: the subject is marked a minor (§3.1: by the record's `minor`, or, for the take's principal, by the `minor` attribute of the device's grant in effect at its first held commitment) and the record's `guardian` is zero. 3. `consent_revoked`: the head's `revoked_modules` lists the record's digest (§8.3). 4. `consent_expired`: the record does not hold (`nbf` ≤ *s* < `exp`) at the start's instant, *s* = ⌊(`t0` + `t`) / 10^6⌋ of the start, or at `at`. 5. `purpose_not_consented`: its `purposes` lack `record`, or lack *p*. ### 9.5 Outcome A take verification gives one outcome: - **refuse**: the reason and where (§9.1). - **verdicts**: the verdict of `single-take`, `aligned`, `complete`, and `publishable` for each of the four purposes, each `holds` or its reason; and whether the manifest **stands**: every claim it makes, and `publishable` for every purpose it names, holds. ## 10. Dual-tier binding The live tracks of the room and the master share the room clock: every event of the room has its `t` on it (live-events §3.1), and the take's mapping places the master on it (§9.3). A room-clock instant *t* within the take maps to the master: - the device-clock instant is *d* = *t* − *o*; - the chunk is the one whose `start` ≤ *d* < `end`; - the position is *d* − that chunk's `start` into the chunk, and *d* − chunk 0's `start` into the master. An instant before chunk 0's mapped start or at or after the last chunk's mapped end is outside the master. A marker set on the live call at room time *t* (live-events §4.8) so lands at its instant in the master. The bound: where `single-take` and `aligned` hold, the mapping strays from the device's own clock mapping at any commitment by at most *J* (rule 6), and the master's start from the capture instant by at most *J*. The marker's `t` is its author's statement of the room clock (live-events §11), and a marker set by watching the live call lags what it marks by that call's own delay; a marker anchored to a live object (live-events §3.1) names the object instead, and a sink that knows that object's capture instant on the room clock maps that instant. ## 11. C2PA live video This section maps a take onto the C2PA Technical Specification's live video provenance (§19 there). A take's CMAF master is a stream of kind `take` of [live-provenance](live-provenance.md) (§3.1 there), which writes and verifies the C2PA structures; this section states what a take puts in them. The reference implementation is `live-provenance`'s: the `live_provenance` module of `wai-rs` writes a take's initialization segment and segments under both of §19's methods and verifies them, and its corpus holds a take (`live-conformance/c2pa-live/take-both`). The take's commitments are the WAI side's witness, which §9 verifies; the C2PA output is what a C2PA validator reads, and each is checked on its own. ### 11.1 What maps to what - **The stream.** A take is one live video session. Its `streamId` (§19.3.2) is the take id's first 16 bytes as an RFC 9562 UUID of version 8: byte 6's high four bits set to `1000`, byte 8's high two bits to `10`, written in the 8-4-4-4-12 lowercase form. The corpus states it for each take. - **The segments.** Where the master is CMAF, chunk *i* holds segment *i*, and chunk 0 holds the initialization segment before it (§4.1), with the C2PA Manifest Box §19.2.3 requires. The segment's `sequenceNumber`, in both methods, is the `mfhd` `sequence_number` (ISO/IEC 14496-12) of its first movie fragment, the device numbering its movie fragments from 1 across the take (live-provenance §3.3): *i* + 1 where each segment is one movie fragment. - **The hashes.** A chunk's `hash` covers every byte of the chunk, its C2PA boxes included; the segment's `c2pa.hash.bmff.v3` excludes them by §19's exclusions. The two are different digests of different spans. ### 11.2 Per-segment manifests (§19.3) Each segment carries a C2PA Manifest Box whose manifest holds, as live-provenance §6 lists them, a `c2pa.hash.bmff.v3` over the segment, `c2pa.actions.v2` (each segment's manifest is a standard manifest, which §15.10.1.2 there requires to hold one) whose one action is `c2pa.created` with the IPTC digital source type `digitalCapture` (§18.15.2), and the assertion `c2pa.livevideo.segment` with `sequenceNumber` as above, `streamId` the take's, `continuityMethod` `c2pa.manifestId`, and `previousManifestId` the manifest identifier of the segment before it, or of the initialization segment's manifest for segment 0. Every manifest of the take holds the assertion `science.transaction.wai.take` (live-provenance §5.3), a map of `take` (the take id), `room` (the room id) and `device` (the device's key), each a byte string; a segment's also holds `commit`, the digest of the device's last `take_commit` before the segment was written, and the initialization segment's `epoch`, the room-clock instant of the master's media time 0 (§10), so that a C2PA validator finds the room events that witness the take. ### 11.3 Verifiable Segment Info (§19.4) - **The session key** (§18.25, `c2pa.session-keys`, in the initialization segment's manifest): the device's key, as an OKP COSE_Key of curve Ed25519, EdDSA being among §13.2.1's algorithms, with `kid` the key's 32 bytes; `minSequenceNumber` 1; `createdAt` the opening commitment's instant, `t0` + `t`; `validityPeriod` from then to the `exp` of the device's grant; and `signerBinding`, a COSE_Sign1 by the session key over the claim signer's end-entity certificate, as §18.25 has it. The device's key is then bound to the room by its grant and to the C2PA signer by the binding. - **Each segment** carries an `emsg` box of version 0, `scheme_id_uri` `urn:c2pa:verifiable-segment-info`, `value` `fseg`, `presentation_time_delta` 0, `timescale` and `event_duration` covering the segment, `id` the `sequenceNumber`, and `message_data` a COSE_Sign1_Tagged by the session key whose payload holds `sequenceNumber`, `bmffHash` (the segment's, excluding its C2PA `emsg` boxes, with no `merkle` field) and `manifestId` (§19.4.1, §19.4.2), as live-provenance §7.2 writes it. - A COSE Sig_structure begins with the context string `Signature1`, and every message WAI signs begins with a `wai:` domain, so no signature of one is a signature of the other. ### 11.4 A room's CMAF output A CMAF rendition of a room's live tracks is a stream of kind `track` of live-provenance, as its own live video session: its own `streamId`, its own session key, its own claim signer, each segment tied to the JWP receipt of its group (§8 there). That is the publisher's or the packager's provenance of its output, not the device's of its take. ### 11.5 AI disclosure and sustainability - **AI disclosure** (§18.28, `c2pa.ai-disclosure`). A take's manifest has no field for a trained model, and its C2PA output carries no `c2pa.ai-disclosure`: its source type is a digital capture. A room track or a compositor's output that is labelled machine-made discloses its models as live-provenance §9 has it, and captions a model produced as [timed-text](timed-text.md) §10.3 has it. - **Sustainability** (§18.26, `c2pa.environmental-sustainability`). An emitter writes `energy_kwh` only from a measured figure with its class, under [energy-binding](energy-binding.md)'s rules, and never `carbon_kgco2e` or `water_litres` derived from a figure and a factor. A take carries no energy figure in this revision, so its output carries no such assertion. ## 12. Reasons The codes this extension adds, each with where it is raised: | reason | raised at | |---|---| | `recording_not_permitted` | §9.2 step 1 | | `take_unknown` | §9.2 step 2 | | `commit_malformed` | §5.2, §9.2 step 3 | | `device_not_bound` | §3.1, §9.2 step 4 | | `manifest_invalid` | §7.2 | | `manifest_mismatch` | §9.2 step 5 | | `store_not_listed` | §6.4 | | `upload_receipt_invalid` | §6.3 | | `upload_receipt_mismatch` | §6.3 | | `store_outside_region` | §6.3 | | `consent_invalid` | §8.2 | | `commit_chain_broken` | §9.4 `single-take` 2, `aligned` 2 | | `root_not_extending` | §4.3, §9.4 `single-take` 3 | | `take_not_closed` | §9.4 `single-take` 4 | | `manifest_not_committed` | §9.4 `single-take` 5, `aligned` 3, `complete` 1 | | `clock_unstable` | §9.4 `single-take` 6 | | `capture_discontinuous` | §9.4 `single-take` 7 | | `chunk_too_long` | §9.4 `single-take` 8 | | `commit_early` | §9.4 `single-take` 9 | | `commit_late` | §9.4 `single-take` 10 | | `chunk_extra` | §9.4 `single-take` 11 | | `chunk_reordered` | §9.4 `single-take` 11 | | `chunk_altered` | §9.4 `single-take` 11 | | `misaligned` | §9.4 `aligned` 4 | | `chunk_missing` | §9.4 `complete` 2 | | `upload_not_sealed` | §9.4 `complete` 3 | | `consent_missing` | §9.4 `publishable` 1 | | `guardian_missing` | §9.4 `publishable` 2 | | `consent_revoked` | §9.4 `publishable` 3 | | `consent_expired` | §9.4 `publishable` 4 | | `purpose_not_consented` | §9.4 `publishable` 5 | The first eleven are refusals; the rest are verdicts. A take verifier also raises every code a room verifier raises (live-rooms §11 and the live-events codes it lists), at `room`; `truncated`, `malformed`, `unknown_version`, `bad_key` and `bad_signature` for a manifest, a receipt or a consent record that does not read or whose signature fails; `wrong_room` for a manifest of another room; and `delivery_gap` as a verdict. ## 13. Conformance [`live-conformance/takes/`](../live-conformance/takes/) holds the corpus, in one file, `vectors.json`, written from fixed seeds by `wai_capture_take_vectors gen`. Each vector is a room, a manifest, parts, receipts, consent records and an evaluation instant, with the outcome §9.5 gives them; one with verdicts also states the take id, its commitment count and digests, its size and root, its stream id (§11.1), and the master position of every marker of the room within the take (§10). - **Good takes**: a lesson take (the director's remote `record_start` with a count-in, the performer's chunks and commitments, the director's markers, `record_stop`, the upload and the manifest), whose every claim holds but `publishable` for `train`, which the performer did not consent to; the same take with an upload interrupted and resumed; and a minor's take under a guardian's consent. - **Negatives**, each a control of one defect: a commitment not delivered, a spliced chunk, a reordered one, an extra one never committed, a missing one (`complete` fails, `single-take` holds), a late commitment, a root that does not extend its predecessor, a capture that ignored the count-in, a consent missing, expired or revoked, a minor without a guardian, a device not bound to the principal; and every other reason of §12, with a manifest of another room and one whose signature fails. A conforming verifier gives every vector the outcome, the reason, the place, the verdicts and the data the corpus states. `wai_capture_take_vectors verify` and the test `tests/capture_take.rs` check that the file regenerates byte for byte, that `wai-rs` gives every vector its outcome, that every reason of §12 appears in the corpus, and that §12 lists exactly the reference's codes. `live-conformance/capture_take_verify.py` is the second implementation, written from this text: it shares no code with `wai-rs`, verifies signatures with WAI's strict rule written from RFC 8032 (`claim-conformance/_ed25519.py`), verifies the room with the live-rooms corpus's own second implementation (`live_rooms_verify.py`, which verifies each event with `live_events_verify.py`), and gives every vector the same outcome; it also mutates copies of the corpus and requires every copy to fail. ## 14. Security and privacy considerations - The commitments are events: signed by the device, chained, stamped with the room clock and held by every participant. A relay can drop one, which the take's chain and the device's chain both show, but cannot alter one. A device that edits its master after committing it cannot upload the edit as its take: the edited chunk is not the committed one, and a history it rewrites does not extend what it committed. - A commitment's `t` is its author's statement (live-events §11). A device that pre-dates its commitments is held by the room's other records: its events take their place in the merged order among everyone's, and the per-hop receipts of a later extension witness when each was seen. The take's timing rules bound everything to the room clock from the start's instant on, within *I* and *J*. - The capture range of a chunk is the device's statement. The rules bind it to the room clock at every commitment (rules 6 to 10 of `single-take`) and to the count-in (`aligned`), so a device can neither stretch a chunk over time it did not capture nor commit a chunk before its capture ends; it can still capture something other than the room, which no commitment proves. - Content addressing makes a part its own check: a part whose bytes do not hash to its chunk's `hash` is not that chunk, whoever stored it. - The store's receipts state the upload's state machine; they cannot make a part the committed chunk, and a store's statement of `held` is checked against the manifest's chunks. The region pin binds where the store keeps the master, as the room's policy requires (live-rooms §3.7). - A consent is the subject's act, or a guardian's for a minor, and revocable through the key log as a grant is. `publishable` reads the head the caller gives: a revocation the head does not yet list is not seen. A minor is marked by the room authority in the device's grant, and by the record itself, so a minor's consent signed by the minor alone does not stand. - A take links the device's key, the principal it captures for and every subject's consent. A manifest names subjects by key; who a key is stays with the room authority and the store that keep the master. The master is personal data of everyone it records: the region pin and the room's `retention_days` (live-rooms §3.7) apply to it, and a consent's `purposes` and `exp` bound what may be done with it.