WAI Extension: Model Generations (a generator’s run in WAI’s terms)
Mirrored from the canonical text at commit 0d3f0d7c ().
Status: Draft. Registers the record
wai.world.generation: a signed generation record (wai-generation) that states one run of a world or video generator, an open-weights model run on a party’s own machine, in the terms WAI already has for it. The generator’s weights are named by a pin (SPEC §3.1) and its source by a listing in the same form; its inputs are awai.world.modelaction trace (SPEC §5): the 52-byte model header that binds the model, the seed and the frame geometry, and one signed action op per frame (interactive-worlds §5.1), the first carrying the conditioning; the frames it made are vouched for by the provenance receiptwai.world.modelnames (provenance §3), each frame a step from the session hash; and the frames together are the subject of an origin claim of synthesis §3.1, stepgenerate, naming the pin as its model. The record binds the five to one another, and a verifier checks each binding, so a weight file that is not the pinned one, a trace that names another model or seed, and a frame edited after the generation are each refused with a reason of their own. A float generator is not reproducible across machines, and the record says only what was shown: its pin claims no tier, and each further run the generator made of the same inputs is stated with the frames it gave, so a reader sees whether one host reproduced it. No new transport and no new payload format. The reference implementation is theworld_genmodule ofwai-rs(featureworld_gen); the reference adapter, which runs an open-weights generator and writes what this extension seals, istools/wai_world_model_adapter.py; the corpus islive-conformance/world-gen/, and its second implementation islive-conformance/world_gen_verify.py. Keywords MUST, MUST NOT, SHOULD, MAY are RFC 2119/8174.Texts read for this revision: SPEC §2, §3.1, §5 and §7 Signatures; interactive-worlds §5.1 and §5.2; live-worlds §3.2 and §9.3; provenance; synthesis §2, §3, §5, §6 and §11; jwp-receipts §2.1 and §2.2; energy-measurement §2; determinism-tiers §2; RFC 8032, RFC 8785 and FIPS 180-4.
1. Scope
In scope: the generation record and its encodings (§2); what one generation is made of, and how the parts bind (§3); verification (§4) and its reasons (§5); the reference adapter (§6); conformance (§7).
Out of scope: how a generator works, which is its own; a transport; a payload that carries a
trace to a sink that renders it, which wai.world.model does not yet have (SPEC §5); a
determinism tier for a float generator, which has none (determinism-tiers §2).
Relationship to the core spec: wai.world.generation is a record (SPEC §5): a signed
record a WAI tool emits and verifies. It names no payload format and is not dispatchable. It
adds no member to a manifest and changes no record it binds.
Relationship to synthesis: a generation’s frames are synthesized content, and
their origin claim is synthesis §3.1’s, checked as synthesis §11 checks a claim held alone.
This extension adds what synthesis leaves to its record: the derivation the claim names is a
frame receipt whose first step is a session hash, and the record states the trace, the pin
and the source that turn that session into those frames. The claim’s model is the pin’s digest,
as synthesis §3.1 names a prior’s.
2. The generation record
A generation record is a JSON object (RFC 8259) under synthesis §2’s encodings: byte strings in
lowercase hexadecimal; integers unsigned, at most 2^53 − 1, in plain decimal; no other number;
no boolean; terms and date-times as jwp-receipts defines them. It is read as synthesis §2 reads
a record: strict JSON, no member named twice, at most 32 levels of nesting, exactly the members
§2.1 lists at every level, none missing and none added; one that does not is
record_malformed.
sig = Ed25519(signer_key, "wai:generation\x01" || JCS(record without "sig"))
link_hash = BLAKE3("wai:generation-link\x01" || JCS(record))
JCS is RFC 8785. The signature is verified strictly (SPEC §7 Signatures): a key that fails
step 1 or 2 is bad_key, and a signature that fails a later step bad_signature.
2.1 Members
| member | type | meaning |
|---|---|---|
kind | string | "wai-generation" |
capability | string | "wai.world.model" |
generator | string | a name for people; a verifier decides nothing by it |
pin | object | the generator’s parameter set, as SPEC §3.1 pins one: id (a string), sha256 (64 lowercase hexadecimal digits), determinism (a string), form ("wai.prior.set"), tier (a string); no other member (§3.1) |
code | object | the generator’s source: listing (32 B hex, the SHA-256 of a listing of its files in SPEC §3.1’s form) and revision (a string for people: the revision the files were taken at) |
runtime | string | for people: the framework, the device and the number formats the run used |
session | 32 B hex | the session hash of the trace (§3.2) |
seed | integer | the header’s seed |
width, height, fps | integer | the header’s frame geometry, each at least 1 |
frames | integer | the number of frames, at least 1 and at most 4096 |
conditioning | object | the conditioning object (§3.2): object, its content hash (32 B hex, jwp-receipts §2.1), and samples, the digest of its samples (32 B hex, synthesis §2) |
samples | 32 B hex | the digest of the frames’ samples: SHA-256 over each frame’s RGB8 buffer (synthesis §2), frame after frame |
receipt | 32 B hex | the frame receipt’s receipt hash (§3.3) |
origin | 32 B hex | the link hash of the frames’ origin claim (§3.4) |
reruns | array | at most 16, each {"host", "samples"}: a term naming a host, and the digest of the frames a further run of the same inputs gave there (§3.5) |
issued_at | date-time | when the signer signed it |
signer_pubkey, sig | 32 and 64 bytes: the signer’s Ed25519 key, and §2’s signature |
The signer is the party that ran the generator. A record is named by its link hash.
3. A generation
3.1 The parameter set and the source
The pin names every file of the generator’s weights that the run read, in the form
wai.prior.set: its sha256 is the SHA-256 of the weights listing, SPEC §3.1’s listing of
those files. Its tier is "none": a generator that computes in floating point gives frames
that depend on the device, the libraries and the order of their arithmetic, and claims no
reproducibility tier (determinism-tiers §2). A pin whose tier is another value is refused
(generator_tier). Its determinism says in words what the run was, for people.
The code listing is a listing in the same form of the files of the generator’s source the
run used: its own modules and configuration, as taken at code.revision. Neither listing names
the libraries the source runs on; the runtime says what they were, for people.
A holder of the files checks them as SPEC §3.1 checks a set: each file the listing names, below
the listing’s root, has the SHA-256 its line gives (part_mismatch), and the listing reads by
SPEC §3.1’s rules (set_malformed).
3.2 The trace
The trace is wai.world.model’s (SPEC §5), in one file:
| field | size | meaning |
|---|---|---|
magic | 4 | WWT1 (ASCII) |
header | 52 | the model header of live-worlds §3.2: the model’s 32-byte identifier, the seed (u64), the frame width, the frame height and the frame rate (u32 each), little-endian |
ops | rest | an op batch: one or more ops of interactive-worlds §5.1, each whole, concatenated |
In a generation the model identifier is the pin’s sha256, as 32 bytes. Each op is an
action (mutation kind 1, wai.world.model’s body: dx i32, dy i32, buttons u32,
payload_len u32 and the payload, little-endian) whose effect_tick is the index of the frame
it steers: there is exactly one op for each frame from 0 to frames − 1. The op of frame 0
carries the conditioning payload; every other op an empty payload. The ops are signed by the
party that chose the actions (interactive-worlds §5.1): a person at the controls, or the
generator’s operator for a path it scripted.
The conditioning payload is the JCS text (RFC 8785) of an object with exactly two members:
picture, the digest of the conditioning picture’s samples (32 B hex, synthesis §2), and
params, an object of the generator’s parameters (its denoising steps, its schedule’s shift,
its block length), each a string, an integer or an array of integers, under the generator’s own
names. A generator that starts from no picture has no conditioning object, and its picture is
the digest of zero bytes. The conditioning object is the picture as a WAI object: a WAI1
envelope (SPEC §2) of the capability wai.image.png whose payload decodes to width ×
height RGB8 samples, the picture the generator read.
The session hash is SPEC §5’s: BLAKE3 over the 24 bytes wai:world-model-session and 0x01
followed by the header, chained through each op in canonical order (interactive-worlds §5.2),
each link BLAKE3 over that domain, the value before it and the op’s whole bytes, its signature
included (live-worlds §9.3 states the same chain over a room’s admitted ops). It binds the
model, the seed, the geometry, every action, the conditioning picture and the parameters.
What the actions mean is the generator’s. The reference adapter’s generator reads, per frame,
four keys from buttons (bit 0 forward, 1 back, 2 left, 3 right) and a turn of its camera from
dx (right) and dy (up) in thousandths of its camera unit (§6).
3.3 The frames and the frame receipt
Each frame is an RGB8 picture of width × height, and a frame’s digest is the SHA-256 of
that buffer (synthesis §2). The frame receipt is the provenance receipt wai.world.model signs
for the frames it generates (provenance §3): media "video", capability
"wai.world.model", group_id the first 8 bytes of the session hash read as a u64,
little-endian, no parent, and one step a frame, in order: step i has op render.frame.i
(the index in decimal), input_hash the session hash for step 0 and the digest of frame i − 1
after it, output_hash the digest of frame i, and work i. Its asset_hash is the last
frame’s digest. Each step’s joules_micro is the energy that frame took where it was measured,
and otherwise 0, provenance §5’s unmetered marker, which a reader presents as unmetered and
never as zero joules (energy-measurement §2). Its steps carry no acquisition label in this
revision.
Provenance §3 states the receipt’s parts; its bytes are these, every integer big-endian:
- a step’s hash: BLAKE3 over
wai:prov-stepand 0x01, the length ofop(u32),op,input_hash,output_hash,work(u64) andjoules_micro(u64); root_hash: jwp-receipts §2.2’s Merkle root over the steps’ hashes, each leaf BLAKE3 overmoq-jwp:merkle-leafand 0x01 and the step’s hash, each node BLAKE3 overmoq-jwp:merkle-nodeand 0x01 and its two children, the last node of an odd level paired with itself;- the signed payload:
wai:asset-receiptand 0x01,asset_hash, the length (u32) and bytes ofmedia, and ofcapability,group_id(u64),root_hash, the number of steps (u64),total_joules_micro(u64, the sum of the steps’ figures), the byte 0 (no parent), then for each step the length ofop(u32),op,input_hash,output_hash,work(u64) andjoules_micro(u64), thensigner_pubkeyand the length (u32) and bytes ofsigner_id; sig: Ed25519 over that payload, verified strictly (SPEC §7 Signatures);- the receipt hash: BLAKE3 over
wai:asset-receipt-idand 0x01, the payload andsig.
Its JSON form is the object of exactly the members kind ("asset-provenance"),
asset_hash, capability, group_id, media, parent_receipt_hash (null),
receipt_hash, root_hash, sig, signer_id, signer_pubkey, steps (each of exactly
input_hash, joules_micro, op, output_hash and work) and total_joules_micro.
3.4 The origin claim
The frames’ origin claim is synthesis §3.1’s, signed by the record’s signer, with these values:
| member | value |
|---|---|
subject | {"type": "samples", "samples"}: the record’s samples |
media, class, step | video, synthesized, generate |
operation | wai.world.model |
record | the frame receipt’s receipt hash |
models | one: the pin’s sha256, type trained |
oversight | prompt_guided where a person chose the actions; fully_autonomous where the operator’s script did |
inputs | one where there is a conditioning object: its content hash, the link hash of its own origin claim or null, and that claim’s class and source as the signer states them; none otherwise |
source | synthesis §5’s generated source: with a conditioning picture that is not synthesized, compositeWithTrainedAlgorithmicMedia |
evidence | null |
Its C2PA statement is synthesis §6’s: a c2pa.ai-disclosure assertion naming the pin’s digest
as the model’s identifier.
3.5 What a generation claims about reproduction
A generation states its frames, by digest, and that the generator the pin and the listing name
made them from the trace; the signer vouches for that, and a verifier checks every binding of
it. It does not state that another machine would make the same frames, and a pin of a float
generator claims no tier (§3.1). Where the signer ran the same inputs again, on the same host or
another, it MAY list each further run in reruns with the digest of the frames it gave, and
MUST NOT list a run it did not make. A verifier states each as identical (its digest is
the record’s samples) or differs. So a record with reruns on one host shows whether that
host reproduced the generation, and says nothing of any other host.
4. Verification
4.1 Inputs
A verifier is given: the generation record; the trace; the frame receipt; the origin claim; the weights listing and the code listing; and, where it holds them, the conditioning object, the frames (in order, each a picture of 8-bit RGB), the files of the weights below the weights listing’s root, and the files of the source below the code listing’s root.
4.2 Steps
The verifier runs these steps in order and stops at the first refusal, which it reports with its reason and its place (the record, the claim, the weights, the code, the trace, an op by its index, the receipt, the conditioning object, the frames, or a frame by its index).
- The record. It reads (§2, §2.1) and its signature verifies:
record_malformed,bad_key,bad_signature. Its pin’sformiswai.prior.setand itssha256is 64 lowercase hexadecimal digits (record_malformed); itstierisnone(generator_tier). - The claim. It verifies as synthesis §11 verifies a case that holds it alone, with the
default policy; a refusal there is this verification’s, with synthesis’s reason. Then: its
link hash is the record’s
origin(origin_mismatch); it is signed by the record’s key (signer_mismatch); itsmedia,stepandoperationare §3.4’s (claim_mismatch); itsmodelsare the one pin, typetrained(claim_model_mismatch); its subject is the record’ssamples(claim_subject_mismatch); itsrecordis the record’sreceipt(claim_record_mismatch); its inputs are the conditioning object alone, by the record’sconditioning.object(claim_input_mismatch). - The weights. The weights listing’s SHA-256 is the pin’s
sha256(pin_mismatch); it reads as SPEC §3.1’s listing (set_malformed); and, where the verifier holds the files, each has the SHA-256 its line gives (part_mismatch). - The code. The code listing’s SHA-256 is
code.listing(code_mismatch); it reads (set_malformed); and each file held has the SHA-256 its line gives (part_mismatch). - The trace. It reads (§3.2):
trace_malformed. Its model identifier is the pin’ssha256(trace_model_mismatch); its seed is the record’s (seed_mismatch); its width, height and frame rate are the record’s (geometry_mismatch). Each op, in the trace’s order: its signature verifies (bad_op_signature); it is an action whose body reads (op_not_action). There is one op for each frame, ticks 0 toframes− 1 each once, the op of frame 0 with a conditioning payload that reads (§3.2) and every other op’s payload empty (trace_malformed). The payload’spictureis the record’sconditioning.samples(conditioning_mismatch). The session hash recomputed is the record’ssession(session_mismatch). - The receipt. It reads as §3.3’s JSON form and verifies as provenance §4 has it: the step
chain linked, the root recomputed, the total the sum of the steps, the signature
(
receipt_invalid). Its receipt hash, recomputed, is the record’sreceipt(receipt_mismatch); it is signed by the record’s key (signer_mismatch); itsmedia,capability, parent and steps are §3.3’s forframesframes, each step’sopandworkits index’s and no step labelled (receipt_invalid); itsgroup_idand its first step’s input are the session hash’s (receipt_session_mismatch). - The conditioning object, where held: the record names one (
conditioning_mismatch); it is aWAI1envelope ofwai.image.pngwhose payload decodes towidth×heightRGB8samples (conditioning_malformed); its content hash is the record’sconditioning.objectand the digest of its samples the record’sconditioning.samples(conditioning_mismatch). - The frames, where held: there are
framesof them (frame_malformed, at the frames); each, in order, iswidth×heightRGB8(frame_malformed) and its digest is the output of the receipt’s step of its index (frame_mismatch), at that frame; and the digest of all of them in order is the record’ssamples(samples_mismatch, at the frames).
4.3 What an accepted generation states
An accepted generation states one JSON object of exactly these members:
| member | value |
|---|---|
generation | the record’s link hash |
pin, code | the pin’s sha256 and the code listing’s SHA-256 |
session | the session hash recomputed |
frames, width, height, fps | the record’s |
samples | the record’s samples |
claim | the statement synthesis §11.3 gives the claim held alone: its link hash, class and source, its C2PA statement as synthesis §6 writes it, and assistive (null for a generation) |
energy | the receipt’s total as provenance §5 presents it over unlabelled steps: UNMETERED (no step measured) where every step carries the marker; n µJ (unlabelled; Whole) where none does; otherwise n µJ (unlabelled) over the measured steps only (Partial; unmetered: and the unmetered steps’ operations joined by , and ), n the total in decimal |
reruns | for each rerun in order, {"host", "result"}: its host, and identical or differs (§3.5) |
held | {"conditioning", "frames", "weights", "code"}, each checked where the verifier held it and checked it, not held otherwise |
An accepted generation where the frames were not held says nothing about any frame’s samples beyond what the signer states.
5. Reasons
The codes this extension adds, each with where it is raised:
| reason | raised at |
|---|---|
generator_tier | §3.1, §4.2 step 1 |
origin_mismatch | §4.2 step 2 |
signer_mismatch | §4.2 steps 2 and 6 |
claim_mismatch | §4.2 step 2 |
claim_model_mismatch | §4.2 step 2 |
claim_subject_mismatch | §4.2 step 2 |
claim_record_mismatch | §4.2 step 2 |
claim_input_mismatch | §4.2 step 2 |
pin_mismatch | §4.2 step 3 |
code_mismatch | §4.2 step 4 |
trace_malformed | §3.2, §4.2 step 5 |
trace_model_mismatch | §4.2 step 5 |
seed_mismatch | §4.2 step 5 |
geometry_mismatch | §4.2 step 5 |
conditioning_mismatch | §4.2 steps 5 and 7 |
session_mismatch | §4.2 step 5 |
receipt_invalid | §4.2 step 6 |
receipt_mismatch | §4.2 step 6 |
receipt_session_mismatch | §4.2 step 6 |
conditioning_malformed | §4.2 step 7 |
frame_malformed | §4.2 step 8 |
frame_mismatch | §4.2 step 8 |
samples_mismatch | §4.2 step 8 |
It also raises record_malformed, bad_key and bad_signature (synthesis §2’s, for this
record), every code synthesis §11 raises for a claim held alone, set_malformed and
part_mismatch (SPEC §3.1’s), and bad_op_signature and op_not_action (live-worlds §9.2’s).
6. The reference adapter (informative)
tools/wai_world_model_adapter.py runs an open-weights interactive world model on a build host’s
GPU: an image-to-world generator that makes frames three latent frames at a time from a picture
and one keyboard-and-mouse action a frame, at 640 × 352 and 25 frames a second. Its weights and
source are fetched, read and pinned as decisions D68 records, with the
licences they are under and the credit those ask for; neither is in this repository. The adapter
imports the generator’s own modules from the pinned source and runs them, replacing only the
attention kernels the source calls, an extension written for one vendor’s GPUs, with the
framework’s portable attention, the same arithmetic in another order, and compiling nothing. It
writes a run directory: the weights and code listings, the conditioning picture, the frames as
PNG, and the run’s inputs, timings and energy reading. wai_world_gen_vectors seal signs a run
directory into this extension’s records, and check verifies them. A generation’s dated
throughput, one host’s, is research/world-gen.md.
7. Conformance
live-conformance/world-gen/ holds the corpus. Under runs/,
two real generations as the reference adapter wrote them, from one picture: a, whose nine
frames are held as PNG with its conditioning object, input.wai, and whose record lists a rerun
on its host; and b, another seed and other actions, whose frames are named by digest alone (they
take as much room as a’s) and which shares a’s picture. Each run’s directory holds the run’s
facts (run.json) and its two listings. vectors.json is written from them with fixed keys by
wai_world_gen_vectors gen: each vector a case of §4.1 and its outcome, and, accepted, the object
§4.3 states; refused, its reason and its place. A vector holds its records inline and names the
files it holds by path below the corpus, with each change it makes to them (a byte of a frame
flipped or cut, a frame left out, a byte of the conditioning object flipped), so a planted fault
needs no file of its own. It has:
- Accepted: each run,
awith its frames and its conditioning object held andbwith its conditioning object;awith nothing held but the records. - Refused: each reason of §5 at least once, and each reason §5 says it reuses, each a control
of one defect, among them the four faults this extension is for: a weight file changed under
the same pin, its line in the weights listing changed and the pin kept (
pin_mismatch); the seed changed in the trace while the record and the claim keep the old one (seed_mismatch); a trace that names another model (trace_model_mismatch); and a frame edited after the generation, one byte of one frame changed (frame_mismatch). Files held that are not the pinned ones are refused (part_mismatch) by stand-ins the vector gives inline.
wai_world_gen_vectors verify and the test tests/world_gen.rs check that vectors.json
regenerates byte for byte from the runs, that wai-rs gives every vector its outcome, its reason
and its place, and that §5 lists exactly the reference’s codes; the test also seals a generation
over files it writes, accepts it with those files held, and refuses it once one byte of one file
changes under the same pin. live-conformance/world_gen_verify.py is the second implementation,
written from this text and sharing no code with wai-rs: it verifies the claims with
synthesis_verify.py, every signature with WAI’s strict rule (claim-conformance/_ed25519.py)
and every BLAKE3 with claim-conformance/_blake3.py, reads the trace, the receipt and the
listings, and decodes the PNG files and the envelope, itself; it gives every vector its outcome,
reason, place and statement, mutates copies of the corpus and requires every copy to be refused,
and drops each of its own checks in turn and requires the corpus to catch every weakened verifier.
The weights are not in the repository; a holder of them runs wai_world_gen_vectors check --weights DIR on a sealed run, and research/world-gen.md records such
a run on the real weights, and its refusal (part_mismatch) of a copy with one byte of one file
changed.
8. Security and privacy considerations
- A generation record carries its signer’s authority. What this extension adds is that its parts bind: the frames to the receipt, the receipt to the session, the session to the model, the seed, the actions and the picture, the model to the files pinned, and the claim to all of them. A signer that lies about what it ran signs a record a holder of the weights cannot hold to the frames: a float generator’s frames cannot be recomputed by anyone, and nothing here says they can.
- The pin names the weights’ bytes, not their behaviour. Two pins of one model in two number formats are two sets.
- A record’s reruns are its signer’s statement of runs it made. A verifier compares their digests with the record’s; it cannot tell whether a run was made.
- The trace’s ops name their actors’ keys, and their timing is the frames’. A person who steers a generation is identified by their key to whoever holds the trace, as an actor of interactive-worlds is.
- The conditioning object is the picture the generator read. A record that names it by content hash discloses that hash, and whoever holds the object can check it.