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]>
796 lines
46 KiB
Markdown
796 lines
46 KiB
Markdown
# WAI Extension: Co-performance
|
||
|
||
> Status: Draft. Registers the record `wai.live.coperformance`: people and AI participants
|
||
> performing together in a room, and an audience joining in. A participant publishes an
|
||
> **intent**: the material it means to play next, ahead of the instant it plays, by the digest
|
||
> of an object or as a symbolic form (notes, a text line, a camera move), so the others can see
|
||
> what it is about to do. When the material plays, a verifier compares what played with what
|
||
> was announced, under a pinned rule, and a room key signs a **fidelity statement**: per
|
||
> participant, how many intents it announced and how many it kept, which anyone holding the
|
||
> room recomputes. A **turn schedule** says who holds the floor and for how long, in
|
||
> microseconds or in bars at a stated tempo, as solo turns or call-and-response pairs, and a
|
||
> **handover** passes the floor within a turn; an object a performer publishes outside its turn
|
||
> is refused by the relay or marked by the room clock, as the schedule states, and an object
|
||
> that arrives later than its grant's **latency budget** is marked, never mixed unmarked. An
|
||
> audience member whose grant carries the **audience-input power** submits signed inputs (a
|
||
> prompt, a sketch or a movement sample by digest, a vote) to a **selection window**; the room
|
||
> clock commits to the inputs once the window closes, a declared rule (a plurality vote, the
|
||
> top inputs by reactions, or a draw from a seed committed before the window opened) selects
|
||
> from what was committed, and every selected input is **credited** to the member who sent
|
||
> it. All of it rides in the room's events, as `meta` payloads that the room's authors sign, under
|
||
> one room-level **co-performance policy**, `WPP1`, that the room authority signs. No new
|
||
> transport. The reference implementation is the `co_performance` module of `wai-rs` (feature
|
||
> `co_performance`); the corpus is
|
||
> [`live-conformance/coperformance/`](../live-conformance/coperformance/), and its second
|
||
> implementation is `live-conformance/co_performance_verify.py`.
|
||
> Keywords **MUST**, **MUST NOT**, **SHOULD**, **MAY** are RFC 2119/8174.
|
||
>
|
||
> Texts read for this revision: the live family this extension builds on, as merged
|
||
> ([live-events](live-events.md), [live-rooms](live-rooms.md), [live-compose](live-compose.md),
|
||
> [live-metering](live-metering.md), [live-moderation](live-moderation.md),
|
||
> [synthesis](synthesis.md) §8 and [ai-usage](ai-usage.md)); RFC 8032 and FIPS 180-4. No
|
||
> outside text is pinned: the rules of this extension are WAI's own.
|
||
|
||
## 1. Scope
|
||
|
||
In scope: the co-performance policy, its signature and the policy in effect at each instant
|
||
(§3); how this extension's statements ride in a room's events, who may sign each, and the grant
|
||
attributes it reads (§4); intents, the forms they plan, what played, the pinned rules that
|
||
compare the two, and the fidelity statement (§5); turn schedules, handovers, the floor, refusal
|
||
and marking, latency budgets and marks (§6); audience inputs, their windows, the commitment,
|
||
the selection rules, the selection and its credits (§7); where each rule is enforced (§8); and
|
||
verification (§9).
|
||
|
||
Out of scope: a transport; how a participant decides what to play, and how a model produces it;
|
||
how a performer transcribes what it played into a symbolic form (§5.4 compares the forms it is
|
||
given); how a sink renders an intent, a schedule or a mark; the contribution record that
|
||
credits a work's makers sample by sample, which is a separate extension, `wai.media.contribution`,
|
||
whose slot §7.8 names; and the review of what an audience submits, which is
|
||
[live-moderation](live-moderation.md)'s (§7.4).
|
||
|
||
Relationship to the core spec: `wai.live.coperformance` is a **record** ([SPEC](../SPEC.md)
|
||
§5): signed records a WAI tool emits and verifies. It names no payload format and is not
|
||
dispatchable.
|
||
|
||
Relationship to [live-events](live-events.md): this extension changes nothing in it. Its
|
||
statements are payloads of the `meta` kind (§4.2 there), which live-events leaves to
|
||
applications, as [live-moderation](live-moderation.md)'s statement is, so every statement is
|
||
signed by its author, chained, stamped with the room clock and rate-bounded by its author's
|
||
policy (live-events §5, §6). An audience member's reaction to an input is a `reaction` event
|
||
(§4.5 there) whose target is the input's digest; a moderator's verdict on an input is a
|
||
`decision` event (§4.11 there).
|
||
|
||
Relationship to [live-rooms](live-rooms.md): the room descriptor's and the grant's layouts are
|
||
unchanged. A room states its co-performance policy in a record of its own, `WPP1` (§3), chained
|
||
like the review policy of live-moderation §3.4 and the room usage record of ai-usage §4.2. The
|
||
audience-input power and the latency budget are grant attributes (§4.4), which live-rooms §4.4
|
||
signs with the grant and leaves to the extensions that read them; a new power bit would be
|
||
refused by every room verifier of the current revision (live-rooms §3.4: no power above bit 4).
|
||
The direct power (live-rooms §3.3, bit 1) is the power to sign a schedule, a handover or a
|
||
window.
|
||
|
||
Relationship to [synthesis](synthesis.md) §8 and live-rooms §4.4: an AI participant is marked in
|
||
its grant, its chat, reactions and captions state their machine origin, and its tracks are
|
||
synthesized media. Its intents are `meta` events and carry no origin of their own: the grant in
|
||
effect at their `t` says whose they are.
|
||
|
||
## 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 and §9.2. A room key is
|
||
the room authority's key or the clock's.
|
||
- **Statement**: the payload of a `meta` event whose first four bytes are one of the magics of
|
||
§4.1. It is named by the digest of its event (live-events §3.2).
|
||
- **Performer**: a principal that publishes media. **Audience member**: a principal whose grant
|
||
carries the audience-input power (§4.4).
|
||
- **Object**: a MOQT object of a track of the room (live-rooms §6.1), named by its track (the
|
||
32-byte digest an anchor names a track by, live-events §3.1), its group and its object ID, and
|
||
by the SHA-256 of its payload. An object's **arrival** is the room-clock instant it reached the
|
||
relay; its **due** instant is the room-clock instant its material is for (§9.1).
|
||
- **Before**, of two events: earlier in the merged order.
|
||
- A **text** field is a u16 length and that many bytes of UTF-8 (RFC 3629): no overlong form, no
|
||
surrogate, nothing above U+10FFFF.
|
||
|
||
Integers are little-endian; a signed integer is two's complement. A digest is SHA-256 (FIPS
|
||
180-4), 32 bytes. Ed25519 is RFC 8032's, verified strictly ([SPEC](../SPEC.md) §7
|
||
*Signatures*): a key that fails step 1 or 2 is `bad_key`, and a signature that fails a later
|
||
step is `bad_signature`. *µs* is microseconds of room clock.
|
||
|
||
## 3. The co-performance policy
|
||
|
||
### 3.1 Layout
|
||
|
||
| field | size | meaning |
|
||
|---|---|---|
|
||
| `magic` | 4 | `WPP1` (ASCII) |
|
||
| `version` | 1 | 1 in this revision |
|
||
| `room` | 32 | the room id (live-rooms §3.2) |
|
||
| `prev` | 32 | the digest of the policy this one replaces (§3.4); 32 zero bytes for the first |
|
||
| `effective` | 8 | u64: the room-clock instant from which this policy applies; 0 for the first |
|
||
| `horizon_ms` | 4 | u32: how far ahead of its own `t` an intent may reach (§5.3), above 0 |
|
||
| `window_ms` | 4 | u32: the match window (§5.4): how far from its planned instant material may play and still be the intent's, above 0 |
|
||
| `time_tol_us` | 4 | u32: the timing tolerance of the notes rule (§5.5) |
|
||
| `angle_tol_mdeg` | 4 | u32: the angle tolerance of the camera rule (§5.5), in thousandths of a degree |
|
||
| `modes` | 1 | the turn modes a schedule may state (§6.5): bit 0 refuse, bit 1 mark; at least one, no other bit |
|
||
| `sig` | 64 | §3.2 |
|
||
|
||
### 3.2 Digest and signature
|
||
|
||
The **digest** of a policy is SHA-256 over its record from `magic` through `modes`. The
|
||
**signature** is Ed25519 by the room descriptor's `authority` over the 26 bytes
|
||
`wai:co-performance-policy` (ASCII) and 0x01, followed by the bytes the digest covers.
|
||
|
||
### 3.3 Reading and rules
|
||
|
||
A reader reads exactly the bytes it is given as one policy, in the order of §3.1, and refuses at
|
||
the first of these that holds:
|
||
|
||
1. Fewer than 4 bytes: `truncated`. A magic other than `WPP1`: `malformed`. No byte after the
|
||
magic: `truncated`. A version other than 1: `unknown_version`.
|
||
2. The bytes end before a field the layout requires: `truncated`.
|
||
3. Bytes after `sig`: `malformed`.
|
||
|
||
It then holds the policy to these rules, and refuses one that breaks any of them as
|
||
`co_policy_malformed`: `horizon_ms` and `window_ms` are above 0, and `modes` is 1, 2 or 3.
|
||
Then its `room` is the room id (`wrong_room`), its signature verifies (`bad_key`,
|
||
`bad_signature`), and its link holds (§3.4, `co_policy_unlinked`).
|
||
|
||
### 3.4 The policy in effect
|
||
|
||
A verifier is given a room's policies in a list. The first has `prev` zero and `effective` 0.
|
||
Each later one has as `prev` the digest of the one before it in the list, and an `effective`
|
||
above that one's. A policy that breaks this is refused `co_policy_unlinked`. The **policy in
|
||
effect** at room-clock instant *t* is the last of the list whose `effective` is at or below *t*.
|
||
|
||
A policy is replaced, never amended. A statement is read under the policy in effect at its
|
||
event's `t`, and an intent is compared (§5.4) under the policy it was announced under. A
|
||
statement of this extension at an instant with no policy in effect is refused `co_no_policy`.
|
||
|
||
## 4. Statements
|
||
|
||
### 4.1 Carriage
|
||
|
||
| magic | statement | section | who signs it |
|
||
|---|---|---|---|
|
||
| `WPI1` | intent | §5.1 | any principal of the room |
|
||
| `WPF1` | fidelity statement | §5.6 | a room key |
|
||
| `WPT1` | turn schedule | §6.1 | a room key, or a principal whose grant in effect has the direct power |
|
||
| `WPH1` | handover | §6.3 | the holder of the turn, a principal with the direct power, or a room key |
|
||
| `WPM1` | mark | §6.7 | a room key |
|
||
| `WPW1` | selection window | §7.2 | a room key, or a principal with the direct power |
|
||
| `WPA1` | audience input | §7.3 | an audience member |
|
||
| `WPK1` | commitment | §7.5 | a room key |
|
||
| `WPS1` | selection | §7.7 | a room key |
|
||
|
||
Each statement is the payload of a `meta` event: its magic, a `version` byte of 1, and the
|
||
fields its section lists. A `meta` payload whose first four bytes are none of these magics is
|
||
not this extension's, and a sink that does not implement it shows none of them: they are timed
|
||
metadata for its application (live-events §4.2). Every statement is bounded by its author's
|
||
policy (live-events §5, `max_payload`), 1024 bytes for a room key, which bounds the counts each
|
||
section allows.
|
||
|
||
### 4.2 Reading
|
||
|
||
A statement that ends before a field, that holds bytes after its last field, whose `version` is
|
||
not 1, or whose field holds a value its section does not allow, is `co_statement_malformed`. A
|
||
text field that is not UTF-8 is `co_statement_malformed`.
|
||
|
||
### 4.3 Who signs
|
||
|
||
A statement whose author is not one the table of §4.1 allows is `co_not_permitted`, but for a
|
||
handover, whose author §6.3 checks once it has found the turn (`handover_not_holder`), and an
|
||
audience input (`input_not_permitted`, §7.3). The
|
||
direct power is that of the author's grant in effect at its event's `t` (live-rooms §9.2 step
|
||
4); a room key holds every power. Since a statement is a `meta` event, the room verifier has
|
||
already held it to its author's grant: a principal whose policy does not permit `meta` cannot
|
||
send one (live-rooms §9.2 step 4, `kind_not_permitted`).
|
||
|
||
### 4.4 Attributes
|
||
|
||
This extension reads two grant attributes (live-rooms §4.4):
|
||
|
||
| key | value | meaning |
|
||
|---|---|---|
|
||
| `audience_input` | a decimal integer from 1 to 255 | the **audience-input power**: the principal may submit inputs (§7.3), at most this many to one window |
|
||
| `latency_budget_ms` | a decimal integer from 1 to 600 000 | the principal's **latency budget** (§6.6) |
|
||
|
||
A **decimal integer** is 1 to 6 of the digits `0`–`9`, the first not `0`. A grant whose
|
||
attribute of either key is not of its row is refused `co_attr_malformed`. A grant without
|
||
`audience_input` gives no audience-input power; one without `latency_budget_ms` gives no budget.
|
||
The authority states a budget per role by giving every grant of the role the same value; an AI
|
||
participant's role is where it matters most, since its output is computed and may be late.
|
||
|
||
## 5. Intents
|
||
|
||
### 5.1 The intent
|
||
|
||
| field | size | meaning |
|
||
|---|---|---|
|
||
| `magic`, `version` | 5 | `WPI1`, 1 |
|
||
| `track` | 32 | the track the material plays on (§2); 32 zero bytes for a text intent |
|
||
| `at` | 8 | u64: the room-clock instant the material plays from |
|
||
| `span` | 4 | u32: how long it lasts, in µs from `at`, above 0 |
|
||
| `form` | 1 | 1 object, 2 notes, 3 text, 4 camera |
|
||
| `body` | … | by `form` (§5.2) |
|
||
|
||
An intent's **author** is its event's principal, and its **horizon** is the stretch from its
|
||
event's `t` to `at` + `span`: what it says about the time ahead. The intent track of a
|
||
participant is its intents in its events track (live-rooms §6.1): every subscriber of the room
|
||
that subscribes to that track receives each intent when it is sent, before the material it
|
||
announces plays.
|
||
|
||
### 5.2 Forms
|
||
|
||
- **object** (1): `digest` (32), the SHA-256 of the payload of the object the author will
|
||
publish.
|
||
- **notes** (2): `count` (u8, 1 to 64), then each note: `onset` (u32, µs from `at`, below
|
||
`span`), `duration` (u32, above 0), `pitch` (u8, 0 to 127, a MIDI note number) and `velocity`
|
||
(u8, 1 to 127), in ascending order of (`onset`, `pitch`), no two notes with both equal. A
|
||
phrase is its notes.
|
||
- **text** (3): `text`, a text field of 1 to 500 bytes: a line the author will say, as a `chat`
|
||
or a `caption`.
|
||
- **camera** (4): `pan` (i32, −180 000 to 180 000) and `tilt` (i32, −90 000 to 90 000), in
|
||
thousandths of a degree, and `zoom` (u32, Q16.16, above 0): where the author's camera will be
|
||
pointed and how far zoomed, from `at` for `span`.
|
||
|
||
`track` is zero exactly for a text intent. A form outside 1 to 4, a body that does not read as
|
||
its form says, and a `track` that breaks the rule, are `co_statement_malformed`.
|
||
|
||
The bodies of forms 2 and 4 are also the **symbolic payloads** a performer publishes: an object
|
||
whose payload is a notes body (the count and the notes, with no `at`) or a camera body (12
|
||
bytes) states in symbols what it played (§5.4).
|
||
|
||
### 5.3 Rules
|
||
|
||
An intent is announced ahead, and for a short horizon:
|
||
|
||
1. `at` is above its event's `t`: otherwise `intent_not_ahead`.
|
||
2. `at` + `span` − `t` is at most `horizon_ms` × 1000 of the policy in effect at `t`:
|
||
otherwise `intent_beyond_horizon`.
|
||
|
||
### 5.4 What played
|
||
|
||
The intent's **window** is `window_ms` × 1000 µs of the policy in effect at its event's `t`.
|
||
Its **candidates** are:
|
||
|
||
- for forms 1, 2 and 4: the objects the verifier is given (§9.1) whose principal is the
|
||
intent's author and whose track is the intent's `track`, with a due instant within the window
|
||
of `at` (|`due` − `at`| at most the window);
|
||
- for form 3: the author's `chat` and `caption` events the room accepted, with a `t` within the
|
||
window of `at`.
|
||
|
||
What **played** is the candidate nearest `at`, by |`due` − `at`| or |`t` − `at`|, and of two
|
||
equally near, the first of the objects given, or the first event in the merged order. With no
|
||
candidate the intent was **missed**. Otherwise it was **kept** when what played matches it
|
||
under its form's rule (§5.5), and **diverged** when it does not.
|
||
|
||
For forms 2 and 4 the rule reads the played object's payload, which the verifier is given by
|
||
its digest (§9.1). A played object whose payload is not given is `payload_missing`, at the
|
||
intent: what played cannot be compared.
|
||
|
||
### 5.5 The pinned rules
|
||
|
||
- **object**: the played object's digest is the intent's `digest`.
|
||
- **notes**: the played payload reads as a notes body (§5.2), with the same `count`; and for
|
||
each *i*, in order, the two notes have the same `pitch`, their absolute onsets (`at` +
|
||
`onset` of the intent's, `due` + `onset` of the played object's) differ by at most
|
||
`time_tol_us`, and their durations by at most `time_tol_us`. `velocity` is not compared: it is
|
||
how a note is played, not which.
|
||
- **text**: the event's text is the intent's text, byte for byte.
|
||
- **camera**: the played payload reads as a camera body, its `pan` and `tilt` each differ from
|
||
the intent's by at most `angle_tol_mdeg`, and its `zoom` is the intent's.
|
||
|
||
`time_tol_us` and `angle_tol_mdeg` are those of the policy in effect at the intent's `t`. A
|
||
payload that does not read as the form is a diverged intent, not a malformed one: the object
|
||
is media, and it did not play what was announced.
|
||
|
||
### 5.6 The fidelity statement
|
||
|
||
A room key states, per participant, how often it did what it said it would:
|
||
|
||
| field | size | meaning |
|
||
|---|---|---|
|
||
| `magic`, `version` | 5 | `WPF1`, 1 |
|
||
| `through` | 8 | u64: the room-clock instant it is stated through, at or below its event's `t` |
|
||
| `count` | 1 | u8: the entries |
|
||
| `entries` | 52 × *n* | each: `principal` (32), `announced`, `kept`, `diverged`, `missed` (u32 each) |
|
||
|
||
A fidelity statement whose `through` is above its event's `t`, or whose entries are not in
|
||
strictly ascending order of `principal`, is `co_statement_malformed`.
|
||
|
||
It covers the intents before it whose `at` plus their window is at or below `through`, and it
|
||
reads them as of its own instant: their candidates are the objects whose arrival is at or below
|
||
its event's `t`, and the events before it. Its entries are, in ascending order of `principal`,
|
||
every author of a covered intent, with the number it announced and the number of each outcome.
|
||
A verifier recomputes the entries from the room; a statement whose entries differ, in any
|
||
principal or any count, is `fidelity_mismatch`. A participant kept *k* of *a* intents: a
|
||
fraction a sink may show beside it, as two integers.
|
||
|
||
## 6. Turns
|
||
|
||
### 6.1 The schedule
|
||
|
||
| field | size | meaning |
|
||
|---|---|---|
|
||
| `magic`, `version` | 5 | `WPT1`, 1 |
|
||
| `t0` | 8 | u64: the room's `t0` (live-rooms §3.1), for a relay that holds no descriptor (§8) |
|
||
| `from` | 8 | u64: the instant from which this schedule governs |
|
||
| `mode` | 1 | 1 refuse, 2 mark (§6.5) |
|
||
| `beat_us` | 4 | u32: µs per beat, the tempo; 0 when lengths are in µs |
|
||
| `beats_per_bar` | 1 | u8: 1 to 32 when `beat_us` is above 0; 0 otherwise |
|
||
| `segments` | 1 + … | a u8 count (1 to 4), then each segment: `start` (u64), `length` (u32), `turns` (u16, 1 to 1024), `pairs` (u8, 0 or 1), `holders` (a u8 count, 1 to 4, then each holder's key, 32 bytes) |
|
||
|
||
A segment's **turn length** *L* is `length` µs when `beat_us` is 0, and `length` ×
|
||
`beats_per_bar` × `beat_us` µs otherwise: `length` bars at the stated tempo. A schedule is
|
||
`co_statement_malformed` when: `t0` is not the descriptor's; `mode` is not 1 or 2; `beat_us` and
|
||
`beats_per_bar` break their rule; a segment's *L* is 0; a segment starts before `from`, or before
|
||
the end of the segment before it (its `start` + `turns` × *L*); an end does not fit a u64; two
|
||
holders of a segment are one key; or a segment with `pairs` 1 has an odd number of turns or one
|
||
holder. Then: `from` is at or after its event's `t`, since a schedule never changes the past
|
||
(`schedule_retroactive`); and `mode` is one the policy's `modes` allows (`mode_not_allowed`).
|
||
|
||
The **schedule in effect** at instant *x* is, of the schedules the room holds whose `from` is at
|
||
or below *x*, the last in the merged order. A later schedule replaces an earlier one from its
|
||
`from` on.
|
||
|
||
### 6.2 Turns and pairs
|
||
|
||
Turn *i* of a segment, from 0, runs from `start` + *i* × *L* up to, but not including,
|
||
`start` + (*i* + 1) × *L*, and its **scheduled holder** is the segment's holder *i* mod *n*, of
|
||
its *n* holders. The holders of a segment take turns in order, so two holders trading four-bar
|
||
phrases at 120 beats a minute are one segment: `beat_us` 500 000, `beats_per_bar` 4, `length`
|
||
4, two holders.
|
||
|
||
In a segment with `pairs` 1, turn 2*j* is a **call** and turn 2*j* + 1 its **response**: the
|
||
next holder answers what the call played. A verifier states each pair. The floor (§6.4) is held
|
||
the same way in either.
|
||
|
||
### 6.3 Handovers
|
||
|
||
| field | size | meaning |
|
||
|---|---|---|
|
||
| `magic`, `version` | 5 | `WPH1`, 1 |
|
||
| `at` | 8 | u64: the instant the floor passes, at or after its event's `t` |
|
||
| `to` | 32 | the principal that takes it |
|
||
|
||
A handover passes the turn that holds `at`, under the schedule in effect at `at` of those before
|
||
it, to `to`, from `at` to the turn's end; the schedule's next turn is unchanged. It is `handover_invalid` when
|
||
`at` is before its event's `t`, when no turn of the schedule in effect holds `at`, or when `to`
|
||
is not a holder of that turn's segment. Its author is the turn's holder at `at` (below), a
|
||
principal with the direct power, or a room key: otherwise `handover_not_holder`.
|
||
|
||
The **holder** of a turn at instant *x* is the `to` of the handover into that turn whose `at` is
|
||
the latest at or below *x* (of two at one `at`, the later in the merged order), or its
|
||
scheduled holder when there is none.
|
||
|
||
### 6.4 The floor
|
||
|
||
A segment **governs** its holders while it runs. An object of principal *P* is **out of turn**
|
||
when, at its arrival, the schedule in effect has a segment that is running and whose holders
|
||
include *P*, and the turn running then is held, at that instant, by another principal. An
|
||
object of a principal no running segment names, or that arrives while no segment runs, is not
|
||
governed: the floor is open. An accompanist that plays under every turn is left out of the
|
||
holders.
|
||
|
||
The floor governs the objects of performers' media tracks. It does not govern events: a
|
||
principal's intents and chat stay its own to send.
|
||
|
||
### 6.5 Refuse or mark
|
||
|
||
A schedule's `mode` says what happens to an out-of-turn object:
|
||
|
||
- **refuse** (1): the relay **MUST NOT** forward it. The relay is the component on the path of
|
||
every object before it fans out, so a refusal there is the floor held for every subscriber at
|
||
once (§8).
|
||
- **mark** (2): the relay forwards it, and the room clock **MUST** sign a mark (§6.7) naming it
|
||
as out of turn. Every sink and every compositor then receives it with the mark.
|
||
|
||
Which to state is the room's, through its policy's `modes` and each schedule's `mode`. Refuse
|
||
fits an exchange where the floor is the point: timed spoken turns, a game's moves. Mark fits
|
||
music: a pickup that starts before the bar line, or a phrase that rings past it, is part of the
|
||
music, and dropping it would cut the performance where the overlap is the performance; the mark
|
||
keeps the overlap visible and leaves a mixer to decide.
|
||
|
||
### 6.6 Latency budgets
|
||
|
||
An object of principal *P* is **late** when its arrival is after its due instant by more than
|
||
*P*'s budget: `latency_budget_ms` × 1000 µs of the grant in effect for *P* at the object's
|
||
arrival (§9.1). The relay, or the room clock acting for the room, **MUST** mark a late object
|
||
(§6.7) before it is mixed: a late object is forwarded with its mark, and a mixer that holds the
|
||
mark decides whether the object is still of use. A compositor ([live-compose](live-compose.md)
|
||
§7) **MUST NOT** mix an object it knows to be late without the mark, and a sink that mixes
|
||
locally takes the same mark. A late object is never mixed unmarked.
|
||
|
||
### 6.7 Marks
|
||
|
||
| field | size | meaning |
|
||
|---|---|---|
|
||
| `magic`, `version` | 5 | `WPM1`, 1 |
|
||
| `track` | 32 | the object's track |
|
||
| `group`, `object` | 8 each | u64: its group and object ID |
|
||
| `flags` | 1 | bit 0: out of turn; bit 1: late; at least one, no other bit |
|
||
|
||
A mark is the room clock's, signed as it forwards the object. A verifier checks it against the
|
||
objects it is given (§9.2 step 7): the object it names is one of them, and each flag it sets
|
||
holds of that object. Otherwise `mark_unfounded`. A mark can be no accusation the room's own
|
||
records do not support.
|
||
|
||
## 7. Audience co-creation
|
||
|
||
### 7.1 The audience-input power
|
||
|
||
A principal is an **audience member** while its grant in effect carries `audience_input`
|
||
(§4.4). Its grant's event policy permits `meta`, and, to react to inputs, `reaction`; the
|
||
policy's rate windows (live-events §6.3) bound its inputs as they bound every event, and
|
||
`audience_input` bounds how many it submits to one window (§7.3).
|
||
|
||
### 7.2 Selection windows
|
||
|
||
| field | size | meaning |
|
||
|---|---|---|
|
||
| `magic`, `version` | 5 | `WPW1`, 1 |
|
||
| `rule` | 1 | 1 plurality, 2 reactions, 3 draw (§7.6) |
|
||
| `types` | 1 | the input types it takes: bit 0 prompt, bit 1 sketch, bit 2 vote, bit 3 movement |
|
||
| `options` | 1 | for rule 1, the number of options, 2 to 32; otherwise 0 |
|
||
| `k` | 1 | how many it selects, 1 to 8 |
|
||
| `close` | 8 | u64: the instant it closes, above its event's `t` |
|
||
| `seed_commit` | 32 | for rule 3: SHA-256 over the 24 bytes `wai:co-performance-seed` (ASCII) and 0x01 and the 32-byte seed; otherwise 32 zero bytes |
|
||
| `question` | text | what it asks, 0 to 500 bytes |
|
||
|
||
A window of rule 1 takes votes alone (`types` 4); one of rules 2 and 3 takes any of prompts,
|
||
sketches and movement samples, and no vote. A window is `co_statement_malformed` when it breaks
|
||
these rules, when its `rule`, `options`, `k`, `seed_commit` or `question` is outside what the
|
||
table allows, or when its `close` is not above its event's `t`. A window is named by the digest
|
||
of its event.
|
||
|
||
### 7.3 Inputs
|
||
|
||
| field | size | meaning |
|
||
|---|---|---|
|
||
| `magic`, `version` | 5 | `WPA1`, 1 |
|
||
| `window` | 32 | the window's digest |
|
||
| `type` | 1 | 1 prompt, 2 sketch, 3 vote, 4 movement |
|
||
| `body` | … | a prompt: a text field of 1 to 500 bytes; a sketch or a movement sample: the digest (32) of the object that holds it; a vote: `option` (u8) |
|
||
|
||
An input is checked, in this order:
|
||
|
||
1. its author is an audience member at its `t`: otherwise `input_not_permitted`;
|
||
2. `window` names a window before it: otherwise `window_unknown`;
|
||
3. its `type` is one the window takes, and a vote's `option` is below the window's `options`:
|
||
otherwise `input_type_mismatch`;
|
||
4. counting it, its author has sent at most `audience_input` inputs naming that window:
|
||
otherwise `input_over_limit`.
|
||
|
||
A relay **MUST NOT** forward an input these checks refuse, nor one naming a window whose
|
||
commitment (§7.5) it has forwarded: the commitment fixed the window's inputs, and an input
|
||
the room holds before the commitment in the merged order, with a `t` below `close`, is one it
|
||
counts (§7.5, `commit_mismatch`). A relay holds an input to these rules with the grant its
|
||
author's session proved (live-rooms §10.5) and the windows and commitments of the events tracks
|
||
it forwards, and withholds an input it refuses, as live-moderation §8.1 withholds a record:
|
||
never replaced, so its author's chain shows the gap. An input whose `t` is at or after the
|
||
window's `close` is not refused: it was in flight when the window closed, and it is not
|
||
**eligible** (§7.5). A sketch's or a movement sample's object is published as any object of the
|
||
room, under the member's grant; the input names it, and a performer that uses it fetches it by
|
||
that name.
|
||
|
||
### 7.4 Moderation
|
||
|
||
[Live-moderation](live-moderation.md) applies to inputs as to any `meta` event: a review policy
|
||
may hold them for review (its §6.1), and a moderator decides on them (its §4). An input or a
|
||
reaction is **restricted** at an instant when, of the `decision` events the room accepted before
|
||
that instant whose target is its digest, the last denies or redacts it (live-events §4.11;
|
||
live-moderation §4.5 keeps that decision the standing one of its line). A restricted input is
|
||
not eligible, and a restricted reaction is not counted.
|
||
|
||
### 7.5 The commitment
|
||
|
||
Once a window has closed, a room key commits to its inputs, before any rule runs:
|
||
|
||
| field | size | meaning |
|
||
|---|---|---|
|
||
| `magic`, `version` | 5 | `WPK1`, 1 |
|
||
| `window` | 32 | the window's digest |
|
||
| `count` | 4 | u32: the eligible inputs |
|
||
| `root` | 32 | SHA-256 over the 26 bytes `wai:co-performance-commit` (ASCII) and 0x01, `window`, `count` (u32), and each eligible input's digest in the merged order |
|
||
|
||
The **eligible** inputs of a window are the inputs naming it that the room accepted, with a `t`
|
||
below `close`, that are not restricted at the commitment. A commitment is checked: `window`
|
||
names a window before it (`window_unknown`); its `t` is at or after `close` (`commit_early`);
|
||
the window has no commitment before it (`statement_repeated`); and its `count` and `root` are
|
||
those of the eligible inputs (`commit_mismatch`). The commitment fixes the set a rule runs over
|
||
before the rule runs, so no input can be added or removed after the outcome is known; for a
|
||
draw, the seed was committed when the window opened, before any input (§7.2).
|
||
|
||
### 7.6 The rules
|
||
|
||
Each rule is a function of the window, the eligible inputs, and the room's events before the
|
||
commitment:
|
||
|
||
- **plurality** (1): each member's last eligible vote, in the merged order, counts. The
|
||
options are ordered by their counts, the greatest first, and by option number between equal
|
||
counts; the selection is the first `k` of them with a count above 0.
|
||
- **reactions** (2): an eligible input's score is the number of principals whose last
|
||
`reaction` event targeting its digest, with a `t` below `close`, does not retract
|
||
(live-events §4.5, `flags` bit 1 clear) and is not restricted at the commitment. The inputs
|
||
are ordered by score, the greatest first, and in the merged order between equal scores; the
|
||
selection is the first `k`.
|
||
- **draw** (3): the inputs are ordered by SHA-256 over the 24 bytes `wai:co-performance-draw`
|
||
(ASCII) and 0x01, the seed, and the input's digest, the least first; the selection is the
|
||
first `k`.
|
||
|
||
A selection holds fewer than `k` when fewer are eligible (or, for plurality, fewer options were
|
||
voted for).
|
||
|
||
### 7.7 The selection
|
||
|
||
| field | size | meaning |
|
||
|---|---|---|
|
||
| `magic`, `version` | 5 | `WPS1`, 1 |
|
||
| `rule` | 1 | the window's rule, 1 to 3, so that the selection reads on its own |
|
||
| `window` | 32 | the window's digest |
|
||
| `commit` | 32 | the digest of the window's commitment |
|
||
| `seed` | 32 | for a draw, the seed; otherwise 32 zero bytes |
|
||
| `picks` | 1 + … | a u8 count, then each pick: for plurality, an option (u8); otherwise an input's digest (32) |
|
||
| `credits` | 1 + 69 × *m* | a u8 count, then each credit: `step` (u8, 1 or 2), `party` (32), `ref` (32), `count` (u32) (§7.8) |
|
||
|
||
A selection is checked, in this order:
|
||
|
||
1. `window` names a window before it (`window_unknown`); it is the window's first selection
|
||
(`statement_repeated`); and `commit` is the digest of the window's commitment, before it
|
||
(`selection_uncommitted`).
|
||
2. For a draw, SHA-256 over `wai:co-performance-seed`, 0x01 and `seed` is the window's
|
||
`seed_commit` (`seed_mismatch`); for the other rules `seed` is zero
|
||
(`co_statement_malformed`).
|
||
3. No pick names an input whose `t` is at or after `close`: such an input was submitted after
|
||
the window closed, and a selection that counts it is `input_late`.
|
||
4. Its `rule` is the window's, and its picks are that rule's selection over the committed
|
||
inputs (§7.6), in its order: otherwise `selection_mismatch`.
|
||
5. The credits are one per pick, in the picks' order (§7.8). A pick with no credit is
|
||
`input_uncredited`; a credit that is not the one its pick gives, or one past the last pick,
|
||
is `credit_mismatch`.
|
||
|
||
### 7.8 Credit and the contribution slot
|
||
|
||
Every input that reaches the performance is credited, and a selection is where it reaches it:
|
||
|
||
- a pick of rule 2 or 3, input *X*: `step` 1 (**author**), `party` the key of *X*'s author,
|
||
`ref` *X*'s digest, `count` 1. The member made material the performance uses.
|
||
- a pick of rule 1, option *o*: `step` 2 (**direct**), `party` SHA-256 over the 26 bytes
|
||
`wai:co-performance-voters` (ASCII) and 0x01 followed by the keys of the members whose
|
||
counted vote is *o*, in ascending order, `ref` SHA-256 over the 26 bytes
|
||
`wai:co-performance-option` (ASCII) and 0x01, the window's digest and *o*, and `count` the
|
||
number of those members. The members steered the performance together; each finds its own
|
||
key among them by replaying the room.
|
||
|
||
**The contribution slot.** A contribution record (`wai.media.contribution`, a separate
|
||
extension) credits the makers of a piece of media by party and by step. Each credit here maps
|
||
onto it: an author credit onto a party of role `audience` with the member's key and a step
|
||
whose act is `author`, over the material its `ref` names; a direct credit onto a step whose
|
||
act is `direct`, on behalf of the members its `party` names. This revision writes no
|
||
contribution record and depends on none; the credit is complete without one, and a record that
|
||
cites a selection takes its parties from these fields.
|
||
|
||
## 8. Where each rule is enforced
|
||
|
||
- **The relay** holds the floor (§6.5): it reads the schedules and handovers from the events
|
||
tracks it already forwards (as it reads decisions, live-moderation §6.3), puts each media
|
||
object's arrival on the room clock with the schedule's `t0`, and under refuse does not forward
|
||
an out-of-turn object. It does not forward an input §7.3 refuses, nor an event live-rooms
|
||
§9.2 refuses. Refusal is the relay's because only the relay sees each object before every
|
||
subscriber does. A relay **SHOULD** count what it held back in its per-session record
|
||
([live-metering](live-metering.md) §3): the objects out of turn, and the inputs by the reason
|
||
§7.3 gives each, or `window_committed` for one naming a window already committed, which is
|
||
the relay's alone and no verifier's.
|
||
- **The room clock** signs marks (§6.7), commitments, selections and fidelity statements: what
|
||
the room states about what happened. A clock that is the relay's key signs marks as it
|
||
forwards. A clock that is a session of its own on the relay takes each performer's objects
|
||
as the relay forwards them to it: an object's arrival is the instant the clock received it,
|
||
its due instant the object's timestamp, which a performer states on the room clock (§9.1), and
|
||
the clock signs each mark as it takes the object. Its log of the objects it took is the
|
||
objects of §9.1 for the room, so a verifier given the room and that log finds every mark
|
||
founded and no object out of turn or late without its mark. Such a clock's arrival trails the
|
||
relay's by the hop between them: a performer that sends only while it holds the floor, from a
|
||
little after its turn starts to a little before it ends, keeps its objects in its turn by both
|
||
instants, which a schedule that refuses needs (§12).
|
||
- **The room clock, over time.** It commits to a window once it has closed and an input sent
|
||
before the close has had time to arrive, and selects from it once the commitment is held; a
|
||
draw's seed is the clock's own, drawn when the window opens and kept until its selection. It
|
||
states fidelity through an instant some seconds behind its own, so that what played for the
|
||
intents it covers, and every event about them, has reached it. It holds the room as a
|
||
verifier does (every principal's chain and its own, the grants, the policy and its log), and
|
||
each of these statements is what the verifier will recompute from them.
|
||
- **A room key on request.** A room key states a schedule, a handover or a window as its own.
|
||
A room service whose clock does so for a participant that asks holds the request to that
|
||
participant's grant and powers as §4.3 holds the participant's own statement (the direct
|
||
power for a schedule or a window; the turn's holder, or the direct power, for a handover) and
|
||
authenticates it with the grant's proof of possession over HTTP (live-rooms §10.5.3). In the
|
||
room the statement is the room key's.
|
||
- **The room authority** signs the policy (§3), and grants the audience-input power and the
|
||
latency budgets (§4.4).
|
||
- **Sinks and compositors** show an intent with its author, never mix a late object without its
|
||
mark (§6.6), and show a selection with its credits.
|
||
|
||
After the fact, a verifier (§9) checks every one of these against the room: a relay that
|
||
forwarded an out-of-turn object under refuse, a clock that left one unmarked, or one that
|
||
marked an object that was neither, is found out.
|
||
|
||
## 9. Verification
|
||
|
||
### 9.1 Inputs
|
||
|
||
A co-performance verifier is given what a room verifier is given (live-rooms §9.1: a
|
||
descriptor, grants, a key-log head or none, and the chains), and:
|
||
|
||
- **policies**: the room's co-performance policies, in a list (§3.4);
|
||
- **objects**: the media objects the relay forwarded from the room's performers, each its
|
||
principal, its track, group and object ID, its digest, its arrival and its due instant. The
|
||
due instant is the room-clock instant its material is for: its capture instant on the room
|
||
clock, or the planned instant of the beat it plays on. A performer in a room with this
|
||
extension states it as the object's timestamp (a `WAI1` envelope's `target.timestamp_us`), on
|
||
the room clock. The objects are the caller's statement, as live-moderation §7.1's are: a
|
||
relay's log, a room clock's (§8), its recording or its DVR window states them;
|
||
- **payloads**: the symbolic payloads of objects, each named by its SHA-256.
|
||
|
||
The **grant in effect** for a principal at an instant, for an object, is the grant of its last
|
||
`join` or `grant_exchange` at or before that instant, unless a `leave` of it follows, when it
|
||
has none (live-rooms §7.2).
|
||
|
||
### 9.2 Steps
|
||
|
||
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, at `room` with the room verifier's own place. A gap
|
||
is not a refusal; the steps go on, and those that recompute from the whole room are not made
|
||
(below).
|
||
2. **The policies**, in the list's order (§3.3, §3.4): at the policy's index.
|
||
3. **The grants' attributes** (§4.4), in the grants' order: at the grant's index.
|
||
4. **The statements**, in the merged order: each `meta` event the room accepted whose payload
|
||
begins with a magic of §4.1, by its section: §4.2, then §3.4 (`co_no_policy`), then §4.3,
|
||
then the rules of its section, in the order each section gives. A commitment, a selection
|
||
and a fidelity statement are recomputed at their place in the merged order. At the record.
|
||
5. **The intents** (§5.4), in the merged order: each one's outcome, and `payload_missing`, at
|
||
the intent's record.
|
||
6. **The objects**, in the order given: out of turn under refuse (`turn_violated`); out of
|
||
turn under mark without a mark that sets bit 0 (`turn_unmarked`); late without a mark that
|
||
sets bit 1 (`late_unflagged`). At the object's index.
|
||
7. **The marks**, in the merged order (§6.7): `mark_unfounded`, at the record.
|
||
8. **The outcome** (§9.3).
|
||
|
||
With a gap, step 4 does not check a commitment's `count` and `root` (`commit_mismatch`), a
|
||
selection's picks and credits (step 3 onward of §7.7), or a fidelity statement's entries: each
|
||
depends on events the verifier may not hold. Every other check is made, over what it holds.
|
||
|
||
### 9.3 Outcomes and what a verification states
|
||
|
||
- **refuse**: the reason and where (§9.2).
|
||
- **gap**: no refusal, and the room has a gap: the gaps, under the code `delivery_gap`, and what
|
||
**accept** states, computed from what the verifier holds.
|
||
- **accept**: no refusal and no gap.
|
||
|
||
With accept or gap, the verifier states:
|
||
|
||
- the policies' digests;
|
||
- each intent, in the merged order: its digest, author, form, `at`, its outcome (`kept`,
|
||
`diverged` or `missed`) and what played (an object, by track, group and object ID, or an
|
||
event, by digest), or none;
|
||
- each participant's fidelity: announced, kept, diverged and missed, over every intent;
|
||
- each schedule, in the merged order: its digest, `mode`, `from`, and its turns, each with its
|
||
segment, index, start, end, scheduled holder and role (`solo`, `call` or `response`); and each
|
||
handover: its digest, `at`, `to`, and the turn it passes;
|
||
- each object, in the order given: whether the floor was open, or it was in turn or out of
|
||
turn, the schedule's mode, whether it was late, by how many µs it arrived after its due
|
||
instant (0 when not after), and the flags of the marks naming it;
|
||
- each window, in the merged order: its digest, rule, `close`, its eligible inputs, its
|
||
commitment, its picks, and its credits; with, for plurality, the count of each option, and,
|
||
for reactions, each eligible input's score.
|
||
|
||
## 10. Reasons
|
||
|
||
The codes this extension adds, each with where it is raised:
|
||
|
||
| reason | raised at |
|
||
|---|---|
|
||
| `co_policy_malformed` | §3.3 |
|
||
| `co_policy_unlinked` | §3.4 |
|
||
| `co_no_policy` | §3.4, §9.2 step 4 |
|
||
| `co_attr_malformed` | §4.4, §9.2 step 3 |
|
||
| `co_statement_malformed` | §4.2, §5.2, §5.6, §6.1, §7.2, §7.7 step 2 |
|
||
| `co_not_permitted` | §4.3 |
|
||
| `intent_not_ahead` | §5.3 |
|
||
| `intent_beyond_horizon` | §5.3 |
|
||
| `payload_missing` | §5.4, §9.2 step 5 |
|
||
| `fidelity_mismatch` | §5.6 |
|
||
| `schedule_retroactive` | §6.1 |
|
||
| `mode_not_allowed` | §6.1 |
|
||
| `handover_invalid` | §6.3 |
|
||
| `handover_not_holder` | §6.3 |
|
||
| `turn_violated` | §6.5, §9.2 step 6 |
|
||
| `turn_unmarked` | §6.5, §9.2 step 6 |
|
||
| `late_unflagged` | §6.6, §9.2 step 6 |
|
||
| `mark_unfounded` | §6.7, §9.2 step 7 |
|
||
| `input_not_permitted` | §7.3 step 1 |
|
||
| `window_unknown` | §7.3 step 2, §7.5, §7.7 step 1 |
|
||
| `input_type_mismatch` | §7.3 step 3 |
|
||
| `input_over_limit` | §7.3 step 4 |
|
||
| `statement_repeated` | §7.5, §7.7 step 1 |
|
||
| `commit_early` | §7.5 |
|
||
| `commit_mismatch` | §7.5 |
|
||
| `selection_uncommitted` | §7.7 step 1 |
|
||
| `seed_mismatch` | §7.7 step 2 |
|
||
| `input_late` | §7.7 step 3 |
|
||
| `selection_mismatch` | §7.7 step 4 |
|
||
| `input_uncredited` | §7.7 step 5 |
|
||
| `credit_mismatch` | §7.7 step 5 |
|
||
|
||
A co-performance verifier also raises every code a room verifier raises (live-rooms §11 and the
|
||
live-events codes it lists), at the room; `truncated`, `malformed` and `unknown_version` for a
|
||
policy that does not read; `wrong_room`, `bad_key` and `bad_signature` for a policy of another
|
||
room or whose signature fails; and `delivery_gap` for a gap, which is not a refusal.
|
||
|
||
## 11. Conformance
|
||
|
||
[`live-conformance/coperformance/`](../live-conformance/coperformance/) holds the corpus, in
|
||
one file, `vectors.json`, written from fixed seeds by `wai_co_performance_vectors gen`. It has
|
||
rooms, each a room (descriptor, grants, key-log head or none, chains), its policies, objects and
|
||
payloads, and the outcome §9.3 gives them with everything it states:
|
||
|
||
- **Accepted**: a jam room, in which a person and an AI participant trade four-bar phrases as
|
||
call-and-response pairs at a stated tempo under a schedule that marks, and a later schedule
|
||
that refuses; the AI participant announces intents of every form, kept, diverged and missed;
|
||
an object out of turn and marked, a late object marked, a handover by the director, and the
|
||
clock's fidelity statement; and an audience room, with a plurality vote, a window selected by
|
||
reactions and a seeded draw, an input denied by a moderator and left out, an input sent after
|
||
its window closed and left out, each window committed, selected and credited.
|
||
- **Gap**: the audience room with one audience member's record not delivered.
|
||
- **Refused**: each reason of §10 at least once, each vector a control of one defect: among
|
||
them a fidelity statement counting as kept an intent that played something else, an object
|
||
out of turn under refuse, an input sent after its window closed and selected, a selection that
|
||
does not recompute from the committed inputs, a selected input with no credit, and a late
|
||
object with no mark; and policies that do not read or are of another room.
|
||
|
||
A conforming verifier gives every room the outcome, the reason, the place, and everything else
|
||
the corpus states. `wai_co_performance_vectors verify` and the test `tests/co_performance.rs`
|
||
check that the file regenerates byte for byte, that `wai-rs` gives every room its outcome, that
|
||
every reason of §10 appears, and that §10 lists exactly the reference's codes.
|
||
`live-conformance/co_performance_verify.py` is the second implementation, written from this
|
||
text: it shares no code with `wai-rs`; it verifies each room with the live-rooms corpus's second
|
||
implementation (`live_rooms_verify.py`) and every signature with WAI's strict rule
|
||
(`claim-conformance/_ed25519.py`); and it mutates copies of the corpus and requires every copy
|
||
to fail.
|
||
|
||
## 12. Security and privacy considerations
|
||
|
||
- Every statement is signed by its author and rides in its author's chain, and every policy is
|
||
signed by the room authority. A relay can withhold a statement but alter none, and a withheld
|
||
statement is a gap in its author's chain.
|
||
- An intent binds its author to what it announced, in advance and under its key. A participant
|
||
that announces one thing and plays another is not refused for it: it diverged, and the
|
||
fidelity it is credited with says so. A fidelity statement that credits more than the room
|
||
holds is refused.
|
||
- The objects, their arrivals and their due instants are the caller's statement, as a relay's
|
||
log states them. A relay that misstates an arrival misstates the floor and the budget; its
|
||
signed per-hop record ([live-metering](live-metering.md) §3) and the subscribers' own
|
||
observations of arrival are the evidence against it. A clock that is a session of its own
|
||
takes each arrival a hop after the relay does (§8): under a schedule that refuses, an object
|
||
the relay forwarded within that hop of a turn's end is in the clock's log out of turn, which
|
||
the relay's own log of arrivals, where one is kept, settles. A due instant is a function of the
|
||
object's timestamp and the room clock, and a subscriber holding the object recomputes it.
|
||
- A performer's symbolic payload states in symbols what its object played. A party holding both
|
||
can check one against the other; this extension compares the forms and does not transcribe
|
||
media.
|
||
- The commitment is made before the rule runs and covers exactly the eligible inputs, each
|
||
signed by its member before the window closed; the seed of a draw is committed before any
|
||
input exists. A selection that does not recompute from them is refused, so an outcome cannot
|
||
be chosen after the inputs are seen. What remains to the clock is to omit an input from the
|
||
room, which leaves a gap in the member's chain (live-events §6.2), or to commit before an
|
||
input in flight arrives, which the input's `t` below `close` shows.
|
||
- A sketch or a movement sample is named by digest, and its object is fetched only by a party
|
||
the room's grants let subscribe; a movement sample can identify the person who made it, and it
|
||
is personal data. Taking part in a room under a grant is taking part in its mechanics, and this
|
||
extension adds no consent step to them. Where a regime asks the operator for a lawful basis for
|
||
processing what an audience submits, the operator records it as a `lawful-basis` claim
|
||
([jwp-receipts](jwp-receipts.md) §2.4.6).
|
||
- A vote is credited to its voters together, by a digest of their keys: a member proves it voted
|
||
by replaying the room, and the credit lists no key in clear.
|
||
- An audience member's inputs are linkable to one another through its key, as every event is
|
||
(live-events §11). A member that needs inputs unlinkable across rooms uses a key per room.
|