WAI Extension: Co-performance
Mirrored from the canonical text at commit b1681667 ().
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, asmetapayloads 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 theco_performancemodule ofwai-rs(featureco_performance); the corpus islive-conformance/coperformance/, and its second implementation islive-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-rooms, live-compose, live-metering, live-moderation, synthesis §8 and ai-usage); 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’s (§7.4).
Relationship to the core spec: wai.live.coperformance is a record (SPEC
§5): signed records a WAI tool emits and verifies. It names no payload format and is not
dispatchable.
Relationship to live-events: 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’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: 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 §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
metaevent 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 §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:
- Fewer than 4 bytes:
truncated. A magic other thanWPP1:malformed. No byte after the magic:truncated. A version other than 1:unknown_version. - The bytes end before a field the layout requires:
truncated. - 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 fromat, belowspan),duration(u32, above 0),pitch(u8, 0 to 127, a MIDI note number) andvelocity(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 achator acaption. - camera (4):
pan(i32, −180 000 to 180 000) andtilt(i32, −90 000 to 90 000), in thousandths of a degree, andzoom(u32, Q16.16, above 0): where the author’s camera will be pointed and how far zoomed, fromatforspan.
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:
atis above its event’st: otherwiseintent_not_ahead.at+span−tis at mosthorizon_ms× 1000 of the policy in effect att: otherwiseintent_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 ofat(|due−at| at most the window); - for form 3: the author’s
chatandcaptionevents the room accepted, with atwithin the window ofat.
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 samepitch, their absolute onsets (at+onsetof the intent’s,due+onsetof the played object’s) differ by at mosttime_tol_us, and their durations by at mosttime_tol_us.velocityis 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
panandtilteach differ from the intent’s by at mostangle_tol_mdeg, and itszoomis 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 2j is a call and turn 2j + 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
§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:
- its author is an audience member at its
t: otherwiseinput_not_permitted; windownames a window before it: otherwisewindow_unknown;- its
typeis one the window takes, and a vote’soptionis below the window’soptions: otherwiseinput_type_mismatch; - counting it, its author has sent at most
audience_inputinputs naming that window: otherwiseinput_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 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
kof them with a count above 0. - reactions (2): an eligible input’s score is the number of principals whose last
reactionevent targeting its digest, with atbelowclose, does not retract (live-events §4.5,flagsbit 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 firstk. - 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 firstk.
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:
windownames a window before it (window_unknown); it is the window’s first selection (statement_repeated); andcommitis the digest of the window’s commitment, before it (selection_uncommitted).- For a draw, SHA-256 over
wai:co-performance-seed, 0x01 andseedis the window’sseed_commit(seed_mismatch); for the other rulesseedis zero (co_statement_malformed). - No pick names an input whose
tis at or afterclose: such an input was submitted after the window closed, and a selection that counts it isinput_late. - Its
ruleis the window’s, and its picks are that rule’s selection over the committed inputs (§7.6), in its order: otherwiseselection_mismatch. - 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, iscredit_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:
step1 (author),partythe key of X’s author,refX’s digest,count1. The member made material the performance uses. - a pick of rule 1, option o:
step2 (direct),partySHA-256 over the 26 byteswai:co-performance-voters(ASCII) and 0x01 followed by the keys of the members whose counted vote is o, in ascending order,refSHA-256 over the 26 byteswai:co-performance-option(ASCII) and 0x01, the window’s digest and o, andcountthe 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 §3): the objects out of turn, and the inputs by the reason §7.3 gives each, orwindow_committedfor 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
WAI1envelope’starget.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:
- The room (live-rooms §9): a refusal, at
roomwith 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). - The policies, in the list’s order (§3.3, §3.4): at the policy’s index.
- The grants’ attributes (§4.4), in the grants’ order: at the grant’s index.
- The statements, in the merged order: each
metaevent 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. - The intents (§5.4), in the merged order: each one’s outcome, and
payload_missing, at the intent’s record. - 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. - The marks, in the merged order (§6.7):
mark_unfounded, at the record. - 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,divergedormissed) 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,callorresponse); 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/ 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 §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
tbelowcloseshows. - 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-basisclaim (jwp-receipts §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.