Skip to main content

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 a wai.world.model action 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 receipt wai.world.model names (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, step generate, 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 the world_gen module of wai-rs (feature world_gen); the reference adapter, which runs an open-weights generator and writes what this extension seals, is tools/wai_world_model_adapter.py; the corpus is live-conformance/world-gen/, and its second implementation is live-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

membertypemeaning
kindstring"wai-generation"
capabilitystring"wai.world.model"
generatorstringa name for people; a verifier decides nothing by it
pinobjectthe 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)
codeobjectthe 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)
runtimestringfor people: the framework, the device and the number formats the run used
session32 B hexthe session hash of the trace (§3.2)
seedintegerthe header’s seed
width, height, fpsintegerthe header’s frame geometry, each at least 1
framesintegerthe number of frames, at least 1 and at most 4096
conditioningobjectthe 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)
samples32 B hexthe digest of the frames’ samples: SHA-256 over each frame’s RGB8 buffer (synthesis §2), frame after frame
receipt32 B hexthe frame receipt’s receipt hash (§3.3)
origin32 B hexthe link hash of the frames’ origin claim (§3.4)
rerunsarrayat 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_atdate-timewhen the signer signed it
signer_pubkey, sig32 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:

fieldsizemeaning
magic4WWT1 (ASCII)
header52the 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
opsrestan 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:

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:

membervalue
subject{"type": "samples", "samples"}: the record’s samples
media, class, stepvideo, synthesized, generate
operationwai.world.model
recordthe frame receipt’s receipt hash
modelsone: the pin’s sha256, type trained
oversightprompt_guided where a person chose the actions; fully_autonomous where the operator’s script did
inputsone 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
sourcesynthesis §5’s generated source: with a conditioning picture that is not synthesized, compositeWithTrainedAlgorithmicMedia
evidencenull

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).

  1. The record. It reads (§2, §2.1) and its signature verifies: record_malformed, bad_key, bad_signature. Its pin’s form is wai.prior.set and its sha256 is 64 lowercase hexadecimal digits (record_malformed); its tier is none (generator_tier).
  2. 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); its media, step and operation are §3.4’s (claim_mismatch); its models are the one pin, type trained (claim_model_mismatch); its subject is the record’s samples (claim_subject_mismatch); its record is the record’s receipt (claim_record_mismatch); its inputs are the conditioning object alone, by the record’s conditioning.object (claim_input_mismatch).
  3. 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).
  4. 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).
  5. The trace. It reads (§3.2): trace_malformed. Its model identifier is the pin’s sha256 (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 to frames − 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’s picture is the record’s conditioning.samples (conditioning_mismatch). The session hash recomputed is the record’s session (session_mismatch).
  6. 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’s receipt (receipt_mismatch); it is signed by the record’s key (signer_mismatch); its media, capability, parent and steps are §3.3’s for frames frames, each step’s op and work its index’s and no step labelled (receipt_invalid); its group_id and its first step’s input are the session hash’s (receipt_session_mismatch).
  7. The conditioning object, where held: the record names one (conditioning_mismatch); it is a WAI1 envelope of wai.image.png whose payload decodes to width × height RGB8 samples (conditioning_malformed); its content hash is the record’s conditioning.object and the digest of its samples the record’s conditioning.samples (conditioning_mismatch).
  8. The frames, where held: there are frames of them (frame_malformed, at the frames); each, in order, is width × height RGB8 (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’s samples (samples_mismatch, at the frames).

4.3 What an accepted generation states

An accepted generation states one JSON object of exactly these members:

membervalue
generationthe record’s link hash
pin, codethe pin’s sha256 and the code listing’s SHA-256
sessionthe session hash recomputed
frames, width, height, fpsthe record’s
samplesthe record’s samples
claimthe 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)
energythe 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
rerunsfor 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:

reasonraised 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:

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