Skip to main content

WAI Extension: Shoppable Events

Mirrored from the canonical text at commit c5e7133e ().

Status: Draft. Registers the record wai.live.offer: an offer a room presents (an item, a price, a window of time) that a viewer can act on, with every step on a signed record, so that who is owed what recomputes. An offer is the payload WOF1 of a signed meta event of live-events, by a room key or a principal the room’s authority admits, anchored to the media object the programme shows it at (§5). It names the merchant’s item by the merchant’s own identifier and by the digest of the item’s signed description (§4), states its price as an integer of minor units, its window in the room clock, the regions it may be shown in, and its stock as a count or as unknown, never as zero when unknown (offer_stock_zero_when_unknown). A sink presents an offer from what the room carried and sends nothing until the viewer acts (§6). A viewer who acts signs an intent under a key made for the session (§7); the merchant’s endpoint answers it with a signed order or a signed refusal (§8), and a refund is a signed reversal (§9). The purchase is the merchant’s: this extension processes no payment. A merchant’s signed tariff states the share of an order the room’s operator, the creator and the delivery path are each owed, and a ledger’s signed statement lists what each is owed line by line, each line naming its order, its intent and its offer; a verifier given the records recomputes every line (§10). An order with no verifiable intent earns nothing, and a reversal takes back exactly what its order earned. No new transport and no new cryptography. The reference implementation is the shoppable_events module of wai-rs (feature shoppable_events); the corpus is live-conformance/offers/, and its second implementation is live-conformance/shoppable_events_verify.py. Keywords MUST, MUST NOT, SHOULD, MAY are RFC 2119/8174.

Texts read for this revision: WAI’s own, and the mandates of this repository’s payments standard as its op-agentic crate reads them (openpay/crates/op-agentic/src/mandate.rs), with its metered holds (openpay/crates/op-metered). RFC 8032, RFC 8259, RFC 8785, RFC 4648 and FIPS 180-4, as the texts this one builds on cite them. ISO 4217 and ISO 3166 were not read: a currency is bound here only as three capitals with a stated exponent, and a region only by the structure of its code (rung-price §2). No consumer-protection statute or regulation was read as its text for this revision; §13 states what the extension ensures and what it does not.

1. Scope

In scope: the item description, its digest and signature (§4); the offer, its carriage in a meta event, its members, its stock, its region rule, who may offer, and how it is read (§5); what a sink presents, the text of a price, the region aggregated in the sink, what leaves a sink and when, and the key an intent is signed with (§6); the intent (§7); the hand-off, the order, the refusal and the payment an intent may name (§8); the reversal (§9); the tariff, what an order earns, the statement and its verification (§10).

Out of scope: a transport; payment, its authorisation, capture and refund, which are the merchant’s and its payment service’s; delivery of goods, tax, returns and every other term of a sale, which the merchant’s own terms state; how a merchant keeps stock; how a sink learns where it is; how a sink lays an offer out on a screen beyond what §6.1 requires it to show; the room, its grants and its event policy, which live-rooms and live-events define and this extension reads and does not change; and the invoice of receipt-ledger, which prices delivery, rooms and ads and to which this extension adds no source.

Relationship to the core spec: wai.live.offer is a record (SPEC §5): signed records a WAI tool emits and verifies. It names no payload format and is not dispatchable.

2. Terms

3. Why these records

  1. An offer is part of the programme. It is an event of the room, signed as every event is, chained in its author’s chain and anchored to the object the programme shows it at (live-events §3.1). A relay can drop it and cannot alter it, and a recording holds it where it was shown.
  2. A price is what was signed. The price a viewer sees, the price the intent accepts and the price the order charges are one integer of minor units in one currency, and each record is held to the one before it.
  3. Unknown is not zero. A stock of zero says an item is sold out; a stock that is not known says nothing. An offer writes the two apart (§5.3), and one that writes unknown as zero is refused.
  4. A viewer who does not act is not seen. Everything a sink needs to present an offer arrives in the room. Nothing about an offer leaves a sink until its viewer acts, and then one request does, to the merchant, under a key made for the session (§6.4, §6.5).
  5. The sale is the merchant’s. The standard carries the viewer’s intent to the merchant and the merchant’s signed answer back. Payment is taken, or not, by the merchant.
  6. What is owed follows from the records. A share is a tariff’s rate times an order’s total, rounded down once, and every line of a statement names the order, the intent and the offer it came from. An order that cites no intent a verifier can verify earns nothing.

4. The item description

4.1 Members

A description is a document of exactly these members:

memberform
kind"wai-offer-item"
version1
merchanta key: the merchant
ida token of at most 64 bytes: the merchant’s own identifier of the item
titlea string of 1 to 120 bytes of UTF-8 holding no code point below U+0020 and not U+007F: what the item is
sellera string of the same form, of 1 to 64 bytes: the name the merchant sells under
endpointa string of 9 to 256 bytes, each from ! to ~ (0x21–0x7E), beginning https:// or http:// and holding no ?, #, @ or \: where an intent for the item is handed off (§8.6)
siga signature (§4.2)

4.2 Digest and signature

The digest of a description is SHA-256 over the canonical form of the description without sig, written as 64 lowercase hexadecimal digits. The signature is Ed25519 by merchant over wai:offer-item (ASCII) and 0x01, followed by that same canonical form, verified strictly as SPEC §7 Signatures verifies every signature. A signature over any other bytes fails.

Every document of this extension is digested and signed the same way, each under its own domain: the canonical form without sig, after the domain’s bytes and 0x01.

4.3 Reading

A reader is given a text. It refuses it offer_item_malformed when the text holds more than 4 096 bytes, is not a document, or its members are not exactly §4.1’s, each of its form; and then offer_item_signature_invalid for its signature.

A description is carried in the room: as a meta event (live-events §4.2) whose payload is the four bytes WOI1 followed by the description’s canonical form, by any principal its policy permits meta. What makes a description the merchant’s is its signature, whoever carries it. A sink holds the description of an offer when it has read one, from the room, whose digest is the offer’s description.

5. The offer

5.1 Carriage

An offer is the payload of a meta event (live-events §4.2): the four bytes WOF1 (ASCII) followed by the canonical form of the offer document (§5.2), in UTF-8, and nothing else. The offer has no signature of its own: the event’s is its signature (live-events §3.2), so the price, the window and every other member are signed with the event, by its principal, in that principal’s chain.

The event MUST carry an anchor (live-events §3.1): the object of the programme the offer appears at. A sink presents the offer from the instant it presents that object (§6.1).

The id of an offer is the digest of its event (live-events §3.2), written as 64 lowercase hexadecimal digits. An intent, an order and a statement’s line name an offer by its id.

5.2 Members

memberform
kind"wai-offer"
version1
merchanta key: the merchant whose item it is
itema token of at most 64 bytes: the item’s id (§4.1)
descriptiona digest: the item’s description’s (§4.2)
amountan integer from 0 to 1 000 000 000: the price of one, in minor units
currencythree capitals A–Z
exponentan integer from 0 to 6
startan integer: the room clock from which the offer is open
endan integer above start: the room clock from which it is closed
show"everywhere", "only" or "except" (§5.4)
regions0 to 32 region codes, strictly ascending by their bytes, with no subdivision code whose country it also lists; none exactly when show is "everywhere"
stockan object of stated and count (§5.3)

An offer is open at room clock t when start ≤ t < end.

5.3 Stock

memberform
statedtrue or false
countan integer when stated is true: how many the merchant stated it had when the offer was signed; null when stated is false

A stock that is not known is written {"count": null, "stated": false} and in no other way. An offer whose stated is false and whose count is 0 is refused offer_stock_zero_when_unknown (§5.6): one reader would take it as unknown and another as sold out. A stated count is the merchant’s statement at one instant, and whether an item can still be had is the merchant’s answer to an intent (§8).

5.4 The region rule

show and regions state where the offer may be presented, as a territory statement’s do (audience-decision §5.1): everywhere, only in regions, or everywhere but in regions. A sink holds its location to the rule after aggregating it (§6.3), and the rule is applied to that aggregation, written as audience-decision §4.3 writes evidence, an object of region (a region code or null) and resolved:

An unresolved location is never eligible under "only" or "except": a sink that cannot decide the rule does not present the offer.

5.5 Who may offer

An offer’s event is its principal’s. A verifier is given the admitted keys of the room at the event, and refuses an offer whose principal is not among them (offer_author_not_admitted). They are:

The attribute is the authority’s signed statement that the principal may present offers in the room. A grant admits when its policy permits meta and its attrs hold that entry. Which grant is in effect, and that it is the authority’s and good at t, are live-rooms §9’s to decide; this extension reads only the two facts above from a grant that section accepted.

5.6 Reading an offer

A reader is given the room id, an event’s record, the admitted keys, and the item’s description as a text or none. In this order; the first check that fails names the refusal:

  1. The record reads (live-events §3.3): otherwise offer_event_malformed.
  2. Its kind is meta: otherwise offer_not_meta.
  3. Its room is the room id: otherwise offer_wrong_room.
  4. Its signature verifies (live-events §3.2): otherwise offer_signature_invalid. An offer whose price, window or any other byte was changed after it was signed fails here.
  5. Its payload begins WOF1: otherwise offer_no_magic.
  6. The rest of the payload is a document: otherwise offer_malformed. Its stock, when it is an object whose stated is false and whose count is 0: offer_stock_zero_when_unknown. Its members are exactly §5.2’s and §5.3’s, each of its form, and the rest of the payload is the document’s canonical form, byte for byte: otherwise offer_malformed.
  7. The event has an anchor: otherwise offer_not_anchored.
  8. Its principal is an admitted key (§5.5): otherwise offer_author_not_admitted.
  9. With a description: it reads (§4.3), with its own reason otherwise; and its digest is the offer’s description, its merchant the offer’s and its id the offer’s item: otherwise offer_item_mismatch.

A room’s verifier (live-events §6) carries the event as any meta event and holds it to its principal’s policy, its chain and its rate. An offer’s payload is within the policy’s max_payload as any payload is; §5.2’s bounds keep an offer within the default.

6. The sink

6.1 What a sink presents

A sink MAY present an offer, and one that does MUST present it only while all of these hold: the offer reads (§5.6); the sink holds its description (§4.3) and the description reads and matches (§5.6 step 9), otherwise offer_item_not_held; the sink has presented the object the event’s anchor names, or one after it on that track; the offer is open at the room clock of what the sink is presenting, otherwise offer_outside_window; and the offer is eligible for the sink’s aggregated location (§5.4, §6.3), otherwise offer_region_excluded.

A sink that presents an offer MUST show, together and legibly, for as long as it invites the viewer to act:

Of stock, a sink shows the stated count, or that none is left when the stated count is 0, in which case it MUST NOT invite the viewer to act. Where stock is not stated it shows nothing of stock: it MUST NOT show an item as sold out, as available, or as scarce.

A sink MUST NOT act for the viewer: an intent is signed only on the viewer’s own action on the presented offer, after the price was shown.

6.2 The text of a price

The price text of an amount a, an exponent e and a currency c is: the decimal digits of ⌊a / 10^e⌋ with no leading zero (0 for zero); when e is above 0, a full stop (U+002E) and the e decimal digits of a mod 10^e, with leading zeros; a space (U+0020); and c. An amount of 1250 with exponent 2 and currency XTS is 12.50 XTS. A sink shows the price text, and MAY show beside it the same amount in a form its locale writes.

6.3 The region, aggregated in the sink

A sink’s location never leaves the sink. It is aggregated against the offer’s regions exactly as audience-decision §4.3 aggregates a session’s location against listed codes: to the one listed code that holds it, to outside, or to unresolved. The evidence an intent carries is that aggregation and nothing else. How a sink comes to hold a location is out of scope.

6.4 What leaves a sink, and when

Until the viewer acts on an offer, nothing about it leaves the sink. A sink MUST NOT:

When the viewer acts, the sink signs one intent (§7) and hands it off (§8). That request carries the intent and the offer’s event and nothing else of the viewer: no cookie, no stored identifier, no referrer, no address beyond what carrying a request needs. The intent names the viewer by the viewer key alone.

6.5 The viewer key

A sink MUST sign intents with an Ed25519 key it makes from its platform’s random generator for one playback session of one room at most, and MAY make one for each intent. It MUST NOT use the key in another room or another session, derive it from an identifier of the viewer, the device or an account, or use the key of its room grant for it. The key is fresh for a session as the session identifier sid of delivery-telemetry §8 is, and says no more of who the viewer is. Who the buyer is, where the sale needs it, is between the viewer and the merchant, after the hand-off and outside this extension.

7. The intent

7.1 Members

An intent is a document of exactly these members:

memberform
kind"wai-offer-intent"
version1
viewera key: the viewer key (§6.5)
room64 lowercase hexadecimal digits: the room id
offera digest: the offer’s id (§5.1)
merchant, item, amount, currency, exponentthe offer’s, as the viewer was shown them
quantityan integer from 1 to 1 000
positiona position (§2): the object the sink was presenting when the viewer acted
tan integer: the room clock of that object
evidencean object of region and resolved (§6.3); resolved is false only with a region of null
paymentnull, or an object of exactly mandate, a digest (§8.5)
nonce32 lowercase hexadecimal digits the sink draws at random
siga signature

The digest and the signature are §4.2’s, by viewer, under the domain wai:offer-intent.

7.2 Reading and rules

A reader is given an intent’s text and the offer it is for, read as §5.6 reads it. In this order:

  1. The text holds at most 65 536 bytes, is a document, and its members are exactly §7.1’s, each of its form, where evidence is an object that holds region and resolved, each of its form: otherwise intent_malformed.
  2. evidence holds no member but region and resolved: otherwise intent_location_not_aggregated.
  3. Its signature verifies: otherwise intent_signature_invalid.
  4. Its room is the room of the offer’s event and its offer is the offer’s id: otherwise intent_offer_mismatch. An intent signed for another offer fails here.
  5. Its merchant, item, amount, currency and exponent are the offer’s: otherwise intent_price_mismatch.
  6. A region that is not null is one of the offer’s regions: otherwise intent_location_not_aggregated, since it says more of where the viewer is than the aggregation gives.
  7. The offer is open at t (§5.2): otherwise offer_outside_window.
  8. The offer is eligible for the evidence (§5.4): otherwise offer_region_excluded.
  9. Where the offer’s stock is stated, quantity is at most its count: otherwise intent_exceeds_stock.

An intent that passes is verified against its offer. t and position are the sink’s statement of where the viewer was in the programme, signed by the viewer key, and an order and a statement’s line carry them from the intent unchanged.

8. The hand-off

8.1 The request

The sink gives the merchant a hand-off: a document of exactly kind ("wai-offer-handoff"), version (1), intent (the intent, as an object) and offer (the offer’s event record in base64url without padding, RFC 4648 §5). A merchant that cannot read it refuses it handoff_malformed; one that holds no offer of its own with that record’s digest, handoff_offer_unknown. Neither is signed: there is no intent to name.

A merchant reads the offer from its own copy, since it knows the room and who was admitted, and reads the intent against it (§7.2). It answers a verified intent with an order (§8.2) or with a refusal (§8.3), and an intent that does not verify with a refusal whose reason is §7.2’s.

8.2 The order

An order is a document of exactly these members:

memberform
kind"wai-offer-order"
version1
merchanta key: the merchant that accepts
room, offer, viewer, position, currency, paymentthe intent’s
intenta digest: the intent’s
totalan integer: the intent’s amount times its quantity, in minor units
issueda time: when the merchant accepted
siga signature, by merchant, under the domain wai:offer-order

An order states that the merchant accepted the intent at that total. It does not state that payment was taken or that anything was delivered.

8.3 The refusal

A refusal is a document of exactly kind ("wai-offer-refusal"), version (1), merchant (a key), intent (a digest), reason (a token: a reason of §11 the intent was refused for, or a token of the merchant’s own, such as merchant_declined), issued (a time) and sig, by merchant, under the domain wai:offer-refusal. A reader refuses a text that is not one refusal_malformed, one whose signature fails refusal_signature_invalid, and one whose intent is not the digest of the intent it was given for, or whose merchant is not that intent’s, refusal_intent_mismatch. A refusal earns nothing and is no claim on anyone.

8.4 Reading an order

A reader is given an order’s text, the offer (§5.6) and intents as texts. In this order:

  1. The text holds at most 65 536 bytes, is a document, and its members are exactly §8.2’s, each of its form: otherwise order_malformed.
  2. Its signature verifies: otherwise order_signature_invalid.
  3. One of the given intents reads, has the digest the order’s intent names, and is verified against the offer (§7.2): otherwise order_without_intent.
  4. Its viewer is that intent’s: otherwise order_viewer_mismatch. An order that cites the intent of one viewer key and names another fails here.
  5. Its merchant, room, offer, position, currency and payment are that intent’s, and its total is that intent’s amount times its quantity: otherwise order_intent_mismatch.

An order that passes is verified.

8.5 Payment

This extension takes no payment and defines no payment form. Where the viewer pays under this repository’s payments standard, the intent’s payment names the mandate the viewer’s payment agent gave: mandate is SHA-256 over the bytes of the mandate’s presentation, as that standard’s op-agentic reads a mandate (a verifiable credential in SD-JWT form, closed over one checkout or open over a set of them). The order carries the same payment, so the merchant’s acceptance is bound to the mandate it was offered. Whether the mandate verifies, what its constraints allow, a hold placed for the total and captured at or under it (that standard’s metered holds), and the payment itself are that standard’s and the merchant’s, and no check of this extension reads a mandate. A payment of null says payment is arranged between the viewer and the merchant by other means.

8.6 Over HTTP (mapping)

Field semantics only. A sink hands off by one POST to the description’s endpoint, with content-type: application/json and the hand-off’s canonical form as the body. The merchant answers 200 with an order or a refusal in its canonical form, or 400 for a hand-off it does not sign an answer to (§8.1). The sink sends no credentials, no cookie and no referrer with it (§6.4). It MUST NOT hand off to an http:// endpoint whose host is not a loopback address. It reads the answer as §8.4 or §8.3 reads it before it shows the viewer an outcome, and shows an order only when it is verified. What follows (payment, an account, delivery) is between the viewer and the merchant.

9. The reversal

A reversal is a document of exactly kind ("wai-offer-reversal"), version (1), merchant (a key), order (a digest), total (an integer), currency (three capitals), issued (a time) and sig, by merchant, under the domain wai:offer-reversal. It states that the merchant took the whole order back: a refund, a cancellation, a return.

A reader given a reversal’s text and an order’s refuses the reversal reversal_malformed when it is not one (at most 65 536 bytes); reversal_signature_invalid for its signature; and reversal_order_mismatch when its order is not the order’s digest, or its merchant, total or currency is not the order’s. A reversal reverses a whole order. Part of an order taken back is not written in this revision: a merchant that refunds part reverses the order and its own terms state the rest.

10. The tariff and the statement

10.1 The tariff

A tariff is a document of exactly these members:

memberform
kind"wai-offer-tariff"
version1
merchanta key: the merchant that owes the shares
room64 lowercase hexadecimal digits: the room whose offers it covers
currencythree capitals
start, endtimes, start below end: it covers the orders issued from start and before end
sharesan object of exactly operator, creator and delivery, each an object of payee (a key) and ppm (0 to 1 000 000); the three ppm sum to at most 1 000 000
siga signature, by merchant, under the domain wai:offer-tariff

The three roles are the room’s operator, the creator whose programme presented the offer, and the delivery path that carried it. The tariff names one payee for each, by key; how the parties agree its rates, and who the delivery payee pays in turn, are their own contracts’. A reader refuses a text that is not a tariff (at most 65 536 bytes) tariff_malformed, and then tariff_signature_invalid.

10.2 What an order earns

A ledger is given a room id, the admitted keys, a tariff, and records: offers’ events, and the texts of intents, orders and reversals. A text or a record that does not read is passed over. An order earns under the tariff when all of these hold:

  1. it reads and its signature verifies (§8.4 steps 1 and 2);
  2. its room is the room id and the tariff’s room, its merchant the tariff’s, its currency the tariff’s, and the tariff’s start ≤ its issued < the tariff’s end;
  3. its offer is the id of one of the given events, which reads as an offer (§5.6, with no description), and the order is verified against it with the given intents (§8.4);
  4. no other order of the given texts that meets 1 to 3 cites its intent and has a digest below its own, as bytes: one intent earns once.

An order with no verifiable intent does not meet 3 and earns nothing. For each role whose ppm is above 0, an order that earns gives one credit line of

⌊total × ppm / 1 000 000⌋

minor units to the role’s payee, computed in integers wide enough not to overflow. Each line is rounded down once; what the rounding leaves stays with the merchant and is added to no other line.

A reversal applies to an order that earns when it reads against it (§9). Of the reversals that apply to one order, the one of the least digest is counted, and gives one debit line for each of the order’s credit lines, of the same amount to the same payee. So a reversed order’s lines sum to nothing, and a reversal of an order that earned nothing gives no line.

10.3 The statement

A statement is a document of exactly these members:

memberform
kind"wai-offer-statement"
version1
ledgera key: who computed it
merchant, room, currencythe tariff’s
tariffa digest: the tariff’s
lines0 to 3 000 lines
owedan object of exactly operator, creator and delivery, each an integer: the role’s credits less its debits
siga signature, by ledger, under the domain wai:offer-statement

A line is an object of exactly order, intent and offer (digests: the order, and the intent and the offer it cites), reversal (null for a credit; the reversal’s digest for a debit), role ("operator", "creator" or "delivery"), payee (a key), entry ("credit" or "debit") and amount (an integer of minor units).

Lines are in order: by the bytes of order; within an order its credits and then its debits; within each, operator, creator, delivery. So every line names the records it came from, and a payee that holds them recomputes its own.

10.4 Verification

A verifier is given what a ledger is (§10.2), the tariff as a text, and a statement’s text. In this order:

  1. The statement holds at most 1 048 576 bytes, is a document, and its members and its lines’ are exactly §10.3’s, each of its form, reversal being null exactly when entry is "credit": otherwise statement_malformed.
  2. The tariff reads (§10.1), with its own reason otherwise.
  3. The statement’s tariff is the tariff’s digest, and its merchant, room and currency are the tariff’s, and its room the room id: otherwise statement_tariff_mismatch.
  4. Its signature verifies: otherwise statement_signature_invalid.
  5. For each line, in order: its order is the digest of a given order that reads and whose signature verifies, otherwise attribution_line_without_order; and that order earns (§10.2), otherwise attribution_order_earns_nothing.
  6. Its lines and owed are what §10.2 gives, line for line: otherwise statement_not_recomputed.

A statement that passes is recomputed. It is the recomputable figures a payment to a payee rests on; it moves no money and is no invoice of receipt-ledger’s.

11. Reasons

reasonraised at
offer_item_malformed§4.3
offer_item_signature_invalid§4.3
offer_event_malformed§5.6 step 1
offer_not_meta§5.6 step 2
offer_wrong_room§5.6 step 3
offer_signature_invalid§5.6 step 4
offer_no_magic§5.6 step 5
offer_stock_zero_when_unknown§5.3, §5.6 step 6
offer_malformed§5.6 step 6
offer_not_anchored§5.6 step 7
offer_author_not_admitted§5.5, §5.6 step 8
offer_item_mismatch§5.6 step 9
offer_item_not_held§6.1
offer_outside_window§6.1, §7.2 step 7
offer_region_excluded§6.1, §7.2 step 8
intent_malformed§7.2 step 1
intent_location_not_aggregated§7.2 steps 2 and 6
intent_signature_invalid§7.2 step 3
intent_offer_mismatch§7.2 step 4
intent_price_mismatch§7.2 step 5
intent_exceeds_stock§7.2 step 9
handoff_malformed§8.1
handoff_offer_unknown§8.1
order_malformed§8.4 step 1
order_signature_invalid§8.4 step 2
order_without_intent§8.4 step 3
order_viewer_mismatch§8.4 step 4
order_intent_mismatch§8.4 step 5
refusal_malformed§8.3
refusal_signature_invalid§8.3
refusal_intent_mismatch§8.3
reversal_malformed§9
reversal_signature_invalid§9
reversal_order_mismatch§9
tariff_malformed§10.1
tariff_signature_invalid§10.1
statement_malformed§10.4 step 1
statement_tariff_mismatch§10.4 step 3
statement_signature_invalid§10.4 step 4
attribution_line_without_order§10.4 step 5
attribution_order_earns_nothing§10.4 step 5
statement_not_recomputed§10.4 step 6

12. Conformance

live-conformance/offers/ holds the corpus, in one file, vectors.json, written from fixed seeds by wai_shoppable_events_vectors gen. It has:

Every reason of §11 has a case refused with it. Among them, each a control of one defect: an intent for an offer outside its window; an offer whose price was changed after it was signed; an intent for another offer; an order that cites an intent of another viewer key; a statement’s line with no order; an intent from a region the rule excludes; and an offer whose stock is written zero when it is unknown.

A conforming implementation gives every case the outcome the corpus states. wai_shoppable_events_vectors verify and the test tests/shoppable_events.rs check that the file regenerates byte for byte and that wai-rs gives every case its outcome. live-conformance/shoppable_events_verify.py is the second implementation, written from this text and sharing no code with wai-rs; it also mutates copies of the corpus and drops each of its own checks in turn, and requires each to be caught.

The room service wai-room publishes an offer and its description on the clock’s events track for a caller whose grant holds the direct power (POST /offer). The browser client’s web/offers/ presents an offer as §6.1 requires, signs an intent with the platform’s Ed25519 and hands it off as §8.6 maps it; its run against a fixture merchant on a loopback address requires that no request leaves the page before the viewer acts.

13. What this extension ensures, and what it does not

What a conforming set of records ensures, each by a check above: that an offer was made by a key the room’s authority admitted and has not changed since; that a sink that follows §6.1 showed the price, the seller, the item and that it was an offer before the viewer acted; that the price in the intent and the total in the order are the price of the offer; that the offer was open, and eligible for the region the sink aggregated, when the viewer acted; that an order answers one intent of one viewer key; and that every line of a statement follows from a verified order under the tariff.

What it does not ensure, and no record of it states:

14. Security and privacy considerations