Some checks failed
joule-moq-room room service / joule-moq-room · does this change touch it (pull_request) Successful in 6s
joule-moq-web browser client / joule-moq-web · does this change touch it (pull_request) Successful in 5s
wai-web reach builds / wai-web reach · does this change touch it (pull_request) Successful in 5s
wai-web reach builds / wai-web reach builds · the package of record, every output as recorded (pull_request) Successful in 2m35s
wai-web reach builds / wai-web reach builds · this tree's wai-web, every rung on its engine line (pull_request) Successful in 4m48s
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 37s
WAI conformance / does this change touch it (pull_request) Successful in 51s
WAI energy receipts / does this change touch it (pull_request) Successful in 1m1s
WAI HTTP delivery / WAI HTTP · does this change touch it (pull_request) Successful in 1m8s
wai-pack / wai-pack · does this change touch it (pull_request) Successful in 51s
wai-quantum studio and desktop / does this change touch it (pull_request) Successful in 54s
wai-web wasm32 / does this change touch it (pull_request) Successful in 44s
repository checks / lockfiles, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 4m49s
joule-moq-room room service / joule-moq-room, every target (pull_request) Successful in 2m50s
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (pull_request) Successful in 9m20s
WAI conformance / quantum core and corpus under wasm32-wasip2 (wasmtime) (pull_request) Successful in 2m16s
WAI conformance / cross-arch byte-equality · linux (pull_request) Successful in 28s
WAI conformance / cargo-class corpora · linux (pull_request) Failing after 15m26s
WAI energy receipts / energy receipts · linux (pull_request) Successful in 10m4s
wai-pack / wai-pack · linux (pull_request) Successful in 1m34s
wai-quantum studio and desktop / studio · browser (pull_request) Successful in 2m40s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Successful in 3m47s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 4m21s
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Successful in 17s
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Successful in 11s
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Successful in 31s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 1m58s
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Failing after 15m52s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Successful in 13m17s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 26m26s
WAI conformance / cargo-class corpora · macos (pull_request) Has been cancelled
WAI conformance / cross-arch byte-equality · macos (pull_request) Has been cancelled
WAI conformance / codec wrappers · fuzz smoke, text decode limits, C entry points, provenance, the semantic path (pull_request) Has been cancelled
WAI energy receipts / energy receipts · macos (pull_request) Has been cancelled
WAI HTTP delivery / HTTP corpus · falsify patches 0/3 (pull_request) Has been cancelled
WAI HTTP delivery / HTTP corpus · falsify patches 1/3 (pull_request) Has been cancelled
WAI HTTP delivery / HTTP corpus · falsify patches 2/3 (pull_request) Has been cancelled
WAI HTTP delivery / HTTP corpus · every patch and mutation in exactly one shard (pull_request) Has been cancelled
WAI HTTP delivery / HTTP corpus · verified both ways, and the differential (pull_request) Has been cancelled
WAI HTTP delivery / cache rules · controls and labelled caches (pull_request) Has been cancelled
WAI HTTP delivery / HTTP corpus · falsify mutations 2/3 (pull_request) Has been cancelled
wai-pack / wai-pack · linux-arm64 (pull_request) Has been cancelled
wai-pack / wai-pack · macos (pull_request) Has been cancelled
wai-pack / wai-pack · independent verifier (Python) (pull_request) Has been cancelled
wai-pack / wai-pack · the floor through a media prober (pull_request) Has been cancelled
wai-pack / wai-pack · dependency licences (pull_request) Has been cancelled
wai-quantum studio and desktop / desktop app · macos (pull_request) Has been cancelled
standards workspaces build + test / macOS energy backends (pull_request) Has been cancelled
WAI HTTP delivery / HTTP corpus · falsify mutations 1/3 (pull_request) Has been cancelled
WAI HTTP delivery / HTTP corpus · falsify mutations 0/3 (pull_request) Has been cancelled
C2 (#381) reads C2PA 2.4; actions moved from §18.14 to §18.15 there, and PNG embedding stays §A.3.2. A new *-conformance directory must state a SHA-256 or be listed with its reason in int_kernels_vectors.rs, which only the cargo-class corpora job runs. Co-Authored-By: Claude Opus 5.5 <[email protected]>
719 lines
47 KiB
Markdown
719 lines
47 KiB
Markdown
# WAI Extension: Deriving Edge
|
||
|
||
> Status: Draft. Registers the record `wai.edge.derivation`: an edge that derives new objects
|
||
> from objects a publisher delivered, publishes each one under receipts of its own, and signs
|
||
> a **derivation record** that chains it to its source. An edge may evaluate a `wai.derive.*`
|
||
> operation of [derived-tracks](derived-tracks.md) over decoded samples, rebuild an object at a
|
||
> lower rung for a sink that lacks the capability it was coded in (decode a
|
||
> `wai.video.int_motion` clip and write a frame of it as a `wai.image.png` still), or
|
||
> re-package an object without re-encoding it (explode a `WAI2` component into its `WAI1`
|
||
> envelope). The record names each input by its content hash and the delivery receipt that
|
||
> carries it, and by the record that derived it where another edge did; the derivation and its
|
||
> parameters; the output by its content hash and receipt; and the energy the derivation took,
|
||
> with its acquisition class. Its **check** says how a verifier holds the edge to it:
|
||
> `replicate`, when every step is pinned, so any verifier holding the inputs re-executes the
|
||
> derivation and compares the output byte for byte; or `attest`, when a step is not (a lossy
|
||
> classical encode, a resampling), so the output rests on the edge's signature. A sink verifies
|
||
> its object back to its source, hop by hop, and its policy may refuse attested hops. Each
|
||
> record carries forward, by digest, every provenance item its inputs had — the claims about
|
||
> them, the C2PA Manifest Stores bound to or embedded in them, and what their own records carry —
|
||
> in a system or a personal compartment, and an AI-origin label that no hop may lower; a hop
|
||
> that omits an item or lowers a label is refused. Relays
|
||
> still forward every envelope and receipt unaltered ([jwp-receipts](jwp-receipts.md) §4): an
|
||
> edge alters nothing it received; what it derives is a new object of its own. No new
|
||
> transport. The reference implementation is the `derive_edge` module of `wai-rs` (feature
|
||
> `derive_edge`) and its reference edge, `wai_edge`; the corpus is
|
||
> [`edge-conformance/`](../edge-conformance/), and its second implementation is
|
||
> `edge-conformance/edge_verify.py`.
|
||
> Keywords **MUST**, **MUST NOT**, **SHOULD**, **MAY** are RFC 2119/8174.
|
||
>
|
||
> Texts read for this revision: the WAI texts this one cites; RFC 8032 and RFC 8785; California
|
||
> Assembly Bill 853 (Statutes of 2025, Chapter 674), as chaptered, for Business and Professions
|
||
> Code §22757.1 (n), (o) and (p) and §22757.3.1; and the C2PA Technical Specification, version
|
||
> 2.4, §A.3.2 (a manifest store in PNG) and §18.15 (actions). The operations' names are
|
||
> ISO/IEC 23001-16:2021's, as [derived-tracks](derived-tracks.md) registers them; this extension
|
||
> defines its own parameters and arithmetic for them (§4.2), and does not read the parts of that
|
||
> standard derived-tracks did not read.
|
||
|
||
## 1. Scope
|
||
|
||
In scope: what an edge may derive and what it must not do (§3); the derivations this revision
|
||
registers, how each reads its inputs, what it computes and how it writes its output, and which
|
||
check each takes (§4); the derivation record, its signature and its identity (§5); how an edge
|
||
publishes what it derives, carries its inputs' provenance forward and keeps their AI-origin
|
||
label (§6); verification of a derived object back to its source (§7); who may sign a record
|
||
(§8).
|
||
|
||
Out of scope: a transport, and how a record reaches a sink, which is the carrier's, as it is for
|
||
receipts (jwp-receipts §1); how a sink asks an edge for a derivation, and which derivations an
|
||
edge offers; how an edge measures its energy, which [energy-measurement](energy-measurement.md)
|
||
governs; presentation, which stays with the sink ([derived-tracks](derived-tracks.md) §1); and
|
||
any derivation this revision does not register.
|
||
|
||
Relationship to the core spec: `wai.edge.derivation` is a **record** ([SPEC](../SPEC.md) §5): a
|
||
signed record a WAI tool emits and verifies. It names no payload format and is not
|
||
dispatchable. A derived object is an ordinary envelope of its own capability, dispatched by
|
||
SPEC §4 like any other.
|
||
|
||
Relationship to [jwp-receipts](jwp-receipts.md): this extension changes no receipt. A relay
|
||
forwards what it receives as §4 there requires, byte for byte, and §4.3's rules stand: a relay
|
||
re-signs no receipt and rewrites no envelope. An edge that derives is a relay for what it
|
||
forwards and a **publisher** for what it derives: each output is a new object with its own
|
||
content hash, in a group the edge signs with its own key (§6). The derivation record is the
|
||
separate, parallel statement §4.3 there asks of a relay that attests, and §7.5 there registers
|
||
its authority (§8).
|
||
|
||
Relationship to [derived-tracks](derived-tracks.md): an operation of §4.2 is that extension's
|
||
operation of the same capability string, executed under conditions this profile fixes, so that
|
||
derived-tracks §3 gives the output's tier from its inputs' (§4.5). The record carries the
|
||
content derived-tracks §4 lists for a receipt over a derived sample, in a layout of its own:
|
||
the executed operation, its parameters, the frame each input was read at, the inputs by
|
||
content hash, and, where the check is `replicate`, the output.
|
||
|
||
Relationship to SPEC §7.1: an output of one input carries `derived_from`, naming that input as
|
||
its motif (§4.3). A sink presents it as what it is, a derived object; it does not reconstruct
|
||
its motif.
|
||
|
||
## 2. Terms
|
||
|
||
- **Edge**: a party that derives objects from objects it received and publishes them; named by
|
||
its Ed25519 key (RFC 8032).
|
||
- **Derivation**: one of §4's, evaluated once: a set of inputs, its parameters, one output.
|
||
- **Input**, **output**: the objects a derivation reads and the object it writes, each a whole
|
||
`WAI1` or `WAI2` envelope (SPEC §2, §7.1).
|
||
- **Record**: the derivation record of §5. Its **hash** is its identity (§5.2).
|
||
- **Source object**: an input whose delivery receipt is signed by a key the verifier takes as a
|
||
source: a publisher it subscribed to (§7.1).
|
||
- **Hop**: a record. The **chain** of a derived object is its record, and, for each of that
|
||
record's inputs that an edge derived, the chain of that input, down to source objects.
|
||
- **Samples**: an image as the canonical `RGB8` buffer of
|
||
[reconstruction-binding](reconstruction-binding.md) §2: row-major, three bytes a pixel, no row
|
||
padding. A pixel is addressed (x, y), x to the right and y down from the top-left. The
|
||
**samples digest** is SHA-256 (FIPS 180-4) over the buffer.
|
||
- **Content hash**: jwp-receipts §2.1. A group receipt's **link hash**: jwp-receipts §3 step 3.
|
||
|
||
The record is JSON under the encodings of jwp-receipts §2.4.3: lowercase hexadecimal byte
|
||
strings, unsigned integers of at most 2^53 − 1 in plain decimal, no other number, no boolean,
|
||
terms and date-times as defined there, no member named twice. JCS is the canonical form of
|
||
that section (RFC 8785).
|
||
|
||
## 3. What an edge may derive
|
||
|
||
### 3.1 Three kinds
|
||
|
||
1. **Evaluate an operation.** Read each input as samples (§4.1), evaluate one operation of §4.2
|
||
over them, and write the result in an image capability (§4.3).
|
||
2. **Rebuild at a lower rung.** Read an object coded in a capability a sink lacks, and write it
|
||
in a capability of SPEC §5's mandatory floor or recommended set: a frame of a
|
||
`wai.video.int_motion` clip, as a `wai.image.png` still or a `wai.image.jpeg` picture. A
|
||
rebuild is an evaluation of `wai.derive.identity` over one frame (§4.2): the read and the
|
||
write change the capability, and the operation changes no sample. It serves a sink at which
|
||
the object is inert (SPEC §4 step 6).
|
||
3. **Re-package.** Change the container without decoding: `wai-edge:explode` writes one
|
||
component of a `WAI2` envelope as its exploded `WAI1` envelope (SPEC §7.1, *Exploding a
|
||
component*; §4.4). The output's payload is the component's bytes.
|
||
|
||
### 3.2 What an edge does not do
|
||
|
||
An edge **MUST NOT**:
|
||
|
||
- alter an envelope or a receipt it received, or forward one under its own signature
|
||
(jwp-receipts §4.3);
|
||
- publish an output under any key but its own, or in a group of another key's track (§6);
|
||
- present an output as the object it was derived from: an output has its own content hash and
|
||
its own receipts, and the source's receipts never cover it;
|
||
- sign a record over a derivation this revision does not register, or over an object it did not
|
||
derive;
|
||
- sign `replicate` for a derivation whose check is `attest` (§4.5);
|
||
- leave out of its record a provenance item an input had, or label its output lower than an
|
||
input (§6.1, §6.2).
|
||
|
||
### 3.3 What an edge derives from
|
||
|
||
An edge derives only from objects it has verified: each input's delivery receipts verify
|
||
(jwp-receipts §3 steps 1–5), its envelope hashes to its content hash, and, for an input another
|
||
edge derived, its chain verifies by §7. It names in its record, for each input, the record that
|
||
derived it, so that a sink can follow the chain (§7.3).
|
||
|
||
## 4. The derivations
|
||
|
||
| derivation | inputs | parameters (§4.2) | output capability | check (§4.5) |
|
||
|---|---|---|---|---|
|
||
| `wai.derive.identity` | 1 | none | `wai.image.png`, `wai.image.jpeg` | `replicate` for `wai.image.png` with no encoder; else `attest` |
|
||
| `wai.derive.crop` | 1 | `x`, `y`, `w`, `h` | `wai.image.png`, `wai.image.jpeg` | as identity |
|
||
| `wai.derive.roi_selection` | 1 | `x`, `y`, `w`, `h` | `wai.image.png`, `wai.image.jpeg` | as identity |
|
||
| `wai.derive.rotation` | 1 | `quarter_turns` | `wai.image.png`, `wai.image.jpeg` | as identity |
|
||
| `wai.derive.mirror` | 1 | `flip` | `wai.image.png`, `wai.image.jpeg` | as identity |
|
||
| `wai.derive.grid_composition` | `columns` × `rows` | `columns`, `rows` | `wai.image.png`, `wai.image.jpeg` | as identity |
|
||
| `wai.derive.overlay_composition` | 1 to 16 | `width`, `height`, `fill`, `at` | `wai.image.png`, `wai.image.jpeg` | as identity |
|
||
| `wai.derive.scaling` | 1 | `width`, `height` | `wai.image.png`, `wai.image.jpeg` | `attest` |
|
||
| `wai-edge:explode` | 1 | `component` | the component's capability | `replicate` |
|
||
|
||
A derivation is named by the string in its first column. The `wai.derive.*` strings are
|
||
derived-tracks' registered operations; `wai-edge:explode` is a term of this extension and not a
|
||
capability string. The other operations derived-tracks registers are not derivations of this
|
||
revision: `wai.derive.srgb_fill` reads no input, so an output of it chains to no source, and
|
||
`wai.derive.dissolve` has a time structure of its own, which an input read at one frame does
|
||
not give it.
|
||
|
||
### 4.1 Reading an input
|
||
|
||
An evaluation reads each input at one frame, the input's `frame` (§5.1), as samples:
|
||
|
||
- `wai.image.png`: frame 0, read by the still reader of [packaging](packaging.md) §5 (*A piece's ladder, and stills*) as 8-bit
|
||
RGB; the envelope **MUST** hold no chunk but `IHDR`, `IDAT`, `IEND` and `caBX`. An ancillary
|
||
chunk can state a colour representation the samples do not carry (`cHRM`, `gAMA`, `iCCP`,
|
||
`sRGB` and others), and this profile's representation is the samples alone. A `caBX` chunk
|
||
holds a C2PA Manifest Store (C2PA 2.4 §A.3.2): it describes the content and changes none of
|
||
its samples, and the record carries it forward (§6.1).
|
||
- `wai.video.int_motion`: the `WIV1` payload with keyframe 0 (`integer-payloads.md` §3),
|
||
decoded in full by its pinned decode; frame `frame` of the clip, which **MUST** be one of its
|
||
frames. A clip with keyframe 1 decodes through a parameter set, and is not read here.
|
||
|
||
An input of either capability holds `decode-equivalence` (determinism-tiers §2): every
|
||
conforming reader reads the same samples. An input that does not read as its record states it
|
||
— another capability, a frame it does not have, a payload its reader refuses, a chunk this
|
||
section excludes — is refused (`input_unreadable`).
|
||
|
||
### 4.2 The operations
|
||
|
||
Each operation computes its output samples from its inputs' samples and its parameters by the
|
||
rule below, and no other arithmetic. *W* × *H* is an input's width and height; out(i, j) is the
|
||
output pixel at (i, j) and in(x, y) an input's, all three bytes of it. An output's width and
|
||
height are each at least 1, and its samples at most 2^26 bytes; a parameter the inputs cannot
|
||
satisfy (a region outside an input, an output past that bound) is refused (`params_invalid`)
|
||
when the inputs are evaluated (§7.2 step 5).
|
||
|
||
- `wai.derive.identity`, no parameter (`{}`): out = in.
|
||
- `wai.derive.crop` and `wai.derive.roi_selection`, `{ "x", "y", "w", "h" }`: `w` and `h` at
|
||
least 1, `x + w ≤ W`, `y + h ≤ H`; the output is `w` × `h` and out(i, j) = in(x + i, y + j).
|
||
This profile emits an ROI at its own size, so the two operations compute the same samples;
|
||
each is named by its own string.
|
||
- `wai.derive.rotation`, `{ "quarter_turns" }`, 1, 2 or 3 quarter turns clockwise. For 1 and 3
|
||
the output is *H* × *W*, for 2 *W* × *H*; out(i, j) is in(j, H − 1 − i) for 1,
|
||
in(W − 1 − i, H − 1 − j) for 2 and in(W − 1 − j, i) for 3.
|
||
- `wai.derive.mirror`, `{ "flip" }`, `left-right` or `top-bottom`: out(i, j) is in(W − 1 − i, j)
|
||
or in(i, H − 1 − j).
|
||
- `wai.derive.grid_composition`, `{ "columns", "rows" }`, each at least 1, over exactly
|
||
`columns` × `rows` inputs of one size *W* × *H*: input *k* fills the cell at column
|
||
*k* mod `columns` and row ⌊*k* / `columns`⌋ of a `columns`·*W* × `rows`·*H* output. Inputs of
|
||
different sizes are refused (`params_invalid`).
|
||
- `wai.derive.overlay_composition`, `{ "width", "height", "fill", "at" }`: a canvas of
|
||
`width` × `height`, each at least 1, every pixel `fill` (six lowercase hexadecimal digits, the
|
||
red, green and blue bytes); then each input *k*, in order, written over it at its own size
|
||
with its top-left at `at[k]` (`{ "x", "y" }`, one entry per input), opaquely: out(x + i, y + j)
|
||
= in_k(i, j) for every pixel of the input that falls on the canvas. Pixels off the canvas are
|
||
dropped.
|
||
- `wai.derive.scaling`, `{ "width", "height" }`, each at least 1: the input resampled to
|
||
`width` × `height`. This revision pins no resampling arithmetic, so the samples are the
|
||
edge's (§4.5). The reference edge takes the nearest sample,
|
||
in(⌊i · W / `width`⌋, ⌊j · H / `height`⌋); a verifier does not hold an edge to it.
|
||
|
||
Under these rules every operation but scaling is **exact** for the instance it executes, in the
|
||
sense of derived-tracks §2: its inputs and output are 8-bit RGB with no stated colour
|
||
information, and the canvas fill is a constant the record names (same-representation); it
|
||
copies samples and computes none (copies-only); each input is one frame, and the parameters
|
||
have one value (time-invariant); a region or a cell lies on the sample grid, a rotation is a
|
||
multiple of 90° over one resolution, a reflection maps the grid onto itself, and a composed
|
||
input is placed at its own size at integer offsets, opaquely. Scaling computes new sample
|
||
values, and derived-tracks registers it untiered.
|
||
|
||
### 4.3 Writing the output
|
||
|
||
The output of an evaluation is a `WAI1` envelope whose manifest is, with no other bytes and no
|
||
whitespace, members in this order:
|
||
|
||
```
|
||
{"wai":"1.0","media":"image","intent":"replicate","model_requirement":{"capability":C},
|
||
"conditioning":{"kind":K},"derived_from":{"motif":M,"motif_capability":MC,"renderer":R,
|
||
"settings":S},"target":{"frames":1,"h":HEIGHT,"w":WIDTH}}
|
||
```
|
||
|
||
(shown here on three lines). `C` is the output capability and `K` its last dot-separated
|
||
segment (`png`, `jpeg`); `HEIGHT` and `WIDTH` are the output's. `derived_from` (SPEC §7.1) is
|
||
written exactly when the derivation has one input: `M` and `MC` are that input's content hash
|
||
and capability, `R` is the derivation followed by `@1` when the record names no encoder and the
|
||
encoder's `id` otherwise, and `S` is `BLAKE3("wai:render-settings\x01" ‖ JCS(settings))` with
|
||
`settings` the object `{"derivation", "encoder", "frame", "params"}` of the record's members of
|
||
those names and the input's `frame`. An output of several inputs carries no `derived_from`: it
|
||
presents several motifs, and its record names them all. Strings are written as JSON strings,
|
||
integers in decimal; nothing in them needs an escape.
|
||
|
||
The payload:
|
||
|
||
- `wai.image.png`, the record naming no encoder: the samples as packaging §5's still writer
|
||
writes them, every byte a pure function of the samples;
|
||
- otherwise the output of the encoder the record names, run with the settings it states: a PNG
|
||
of the samples for `wai.image.png`, a baseline JPEG of them (ITU-T T.81, SPEC §5) for
|
||
`wai.image.jpeg`. This revision pins no JPEG writer, so a `wai.image.jpeg` output always names
|
||
one.
|
||
|
||
### 4.4 Re-packaging
|
||
|
||
`wai-edge:explode`, `{ "component" }`, reads one input, a `WAI2` envelope, and writes the
|
||
exploded envelope of its entry number `component` (from 0), exactly as SPEC §7.1 defines it:
|
||
the entry's `envelope_manifest` where it carries one, and otherwise the manifest that section
|
||
assembles from the `WAI2` manifest's members, over the entry's rendition bytes. Nothing is
|
||
decoded and no sample is read. The output's capability is the entry's, and the input's
|
||
`capability` in the record names it too. An entry that cannot be exploded, or a `component`
|
||
past the last entry, is refused (`input_unreadable`). A component and its exploded envelope are
|
||
the same object for every rule that names an object by content hash (SPEC §7.1), so an exploded
|
||
output is the object a sink would have taken from the `WAI2`, now carried on its own, under the
|
||
edge's receipts.
|
||
|
||
### 4.5 The check
|
||
|
||
A derivation's check is `replicate` exactly when every step of it is pinned: the reading of its
|
||
inputs (§4.1 pins every reading this revision has), its operation, which is exact (§4.2), and
|
||
its writer, which is packaging §5's still writer for a `wai.image.png` output whose record
|
||
names no encoder, or SPEC §7.1's explode. Otherwise its check is `attest`:
|
||
|
||
- **replicate**: a verifier holding every input re-executes the derivation and requires the
|
||
output envelope, byte for byte (§7.2 step 5). Over inputs at `decode-equivalence` the output's
|
||
samples hold `decode-equivalence` too (derived-tracks §3 rule 1), and so does the output: PNG
|
||
is lossless.
|
||
- **attest**: the step that is not pinned rests on the edge's signature. A verifier still checks
|
||
every input, and every step before the first that is not pinned: the inputs' samples, and,
|
||
where the operation is exact, the samples the edge gave its encoder. For any
|
||
`wai.image.png` output, attested or not, it checks that the output's samples are the ones
|
||
the record states (§7.2 step 3). It never checks the attested step itself.
|
||
|
||
A record's `check` **MUST** be its derivation's (`check_mismatch`): one derivation has one
|
||
check, and a sink reads from the record what it is relying on.
|
||
|
||
## 5. The derivation record
|
||
|
||
### 5.1 Members
|
||
|
||
A record is a JSON object with exactly these members, under §2's encodings:
|
||
|
||
| member | type | meaning |
|
||
|---|---|---|
|
||
| `kind` | string | `"wai-derivation"`. |
|
||
| `derivation` | term | A derivation of §4. |
|
||
| `params` | object | Its parameters, exactly the members §4.2 or §4.4 gives it, each an integer, a string, or for `at` an array of `{ "x", "y" }`. |
|
||
| `encoder` | object or `null` | `null` where the output is written by a writer this revision pins (§4.3, §4.4); otherwise `{ "id": term, "settings": object }`, the encoder the edge ran and the settings it ran it with, its statement. |
|
||
| `check` | string | `replicate` or `attest` (§4.5). |
|
||
| `inputs` | array | 1 to 16 inputs, in the order the derivation reads them (below). |
|
||
| `output` | object | The output (below). |
|
||
| `energy` | object or `null` | `null` when the derivation was unmetered. Otherwise `{ "joules_micro", "energy_provenance", "energy_uncertainty", "energy_counter_filtering", "energy_calibration_ref" }`, each as jwp-receipts §2.4.5 writes it: the energy the edge spent on this derivation, reading, operating and writing, with its acquisition class. |
|
||
| `provenance` | array | At most 1024 provenance items carried forward (§6.1, below), in ascending order of `input`, then `kind` as a string, then `digest`, none twice. |
|
||
| `synthesis` | string or `null` | The output's AI-origin label (§6.2): `assist`, `generate`, or `null` for none. |
|
||
| `issued_at` | date-time | When the edge signed the record. Self-asserted. |
|
||
| `signer_pubkey` | 32 B hex | The edge's key. |
|
||
| `signer_id` | string | The edge's identity string, as for a group receipt (jwp-receipts §2.4.1, §7.1). |
|
||
| `sig` | 64 B hex | §5.2. |
|
||
|
||
An input:
|
||
|
||
| member | type | meaning |
|
||
|---|---|---|
|
||
| `content_hash` | 32 B hex | The input's content hash. |
|
||
| `capability` | term | The capability the derivation read it as (§4.1), or, for `wai-edge:explode`, the capability of the entry exploded. |
|
||
| `receipt` | 32 B hex | The link hash of the group receipt that carries it. |
|
||
| `object_id` | integer | Its `object_id` in that group. |
|
||
| `frame` | integer or `null` | The frame it was read at (§4.1); `null` for `wai-edge:explode`. |
|
||
| `samples` | 32 B hex or `null` | The samples digest of what was read; `null` for `wai-edge:explode`. |
|
||
| `derivation` | 32 B hex or `null` | The hash of the record that derived it, or `null` for an object the edge received from its publisher. |
|
||
| `synthesis` | string or `null` | The input's AI-origin label (§6.2), as `synthesis` above. |
|
||
|
||
A provenance item:
|
||
|
||
| member | type | meaning |
|
||
|---|---|---|
|
||
| `input` | integer | The position, among the inputs, of the input it came with. |
|
||
| `kind` | string | `claim`: a claim about that input (jwp-receipts §2.4.3); `c2pa`: a C2PA Manifest Store bound to it. |
|
||
| `digest` | 32 B hex | A claim's link hash (jwp-receipts §2.4.3); a manifest store's SHA-256 over its bytes. |
|
||
| `compartment` | string | `system` or `personal` (§6.1). |
|
||
|
||
The output:
|
||
|
||
| member | type | meaning |
|
||
|---|---|---|
|
||
| `content_hash` | 32 B hex | The output's content hash. |
|
||
| `capability` | term | Its capability. |
|
||
| `receipt` | 32 B hex | The link hash of the edge's group receipt that carries it (§6). |
|
||
| `object_id` | integer | Its `object_id` in that group. |
|
||
| `width`, `height` | integer or `null` | Its size; `null` for `wai-edge:explode`. |
|
||
| `samples` | 32 B hex or `null` | The samples digest of the samples written: those the operation produced, which an attested encoder was given; `null` for `wai-edge:explode`. |
|
||
|
||
### 5.2 Signature and identity
|
||
|
||
```
|
||
sig = Ed25519(signer_key, "wai:derivation\x01" || JCS(record without "sig"))
|
||
record hash = BLAKE3("wai:derivation-link\x01" || JCS(record))
|
||
```
|
||
|
||
The signature is verified under jwp-receipts §3.1 step 3's strict rule. A record **SHOULD** be
|
||
sent in canonical form; relays forward it byte for byte.
|
||
|
||
### 5.3 Reading and rules
|
||
|
||
A reader reads a record and refuses at the first of these that holds:
|
||
|
||
1. It is not one JSON text of the shape §5.1 gives, with every member of the right type and
|
||
encoding, no member missing and none added, at any level, `kind` `"wai-derivation"`, and its
|
||
provenance items in their order, none twice, each naming an input the record has
|
||
(`record_malformed`).
|
||
2. Its `derivation` is not one of §4 (`derivation_unknown`).
|
||
3. Its `params` do not hold exactly the derivation's members, each in its range (§4.2, §4.4):
|
||
`w`, `h`, `width`, `height`, `columns` and `rows` at least 1; `quarter_turns` 1, 2 or 3;
|
||
`flip` one of its two values; `fill` six lowercase hexadecimal digits; `at` one entry per
|
||
input, each `{ "x", "y" }`; a size the parameters fix (a region's, a canvas's, a scaling's)
|
||
within 2^26 sample bytes. Or its `encoder` is `null` where §4.3
|
||
pins no writer (a `wai.image.jpeg` output), or not `null` for `wai-edge:explode`. Or its
|
||
output capability is not one the derivation writes (§4), or, for `wai-edge:explode`, not the
|
||
input's (`params_invalid`).
|
||
4. Its inputs are not the derivation's: another number of them (`columns` × `rows` for a grid),
|
||
an input of a capability §4.1 does not read, a `frame`, input `samples`, output `width`,
|
||
`height` or `samples` that is `null` for an evaluation or not `null` for an explode, or a
|
||
`frame` other than 0 for a `wai.image.png` input (`inputs_invalid`).
|
||
5. Its `check` is not its derivation's (§4.5) (`check_mismatch`).
|
||
6. Its `energy` breaks [energy-measurement](energy-measurement.md) §2: a figure of 0 beside a
|
||
class, the legacy `HwShunt`, a class that does not read, a hardware class without its
|
||
evidence or below the resolution floor (§2.3 there) (`energy_invalid`).
|
||
7. Its `synthesis` is lower than an input's (§6.2) (`synthesis_dropped`).
|
||
|
||
## 6. Publishing the output
|
||
|
||
An edge publishes each output on a track of its own, under its own key, as a publisher does
|
||
(jwp-receipts §2): a group receipt it signs and an object receipt for the output, whose
|
||
`content_hash` is the output's. The record's `output.receipt` and `output.object_id` name them.
|
||
|
||
- The group receipt's `signer_pubkey` is the record's (`output_receipt_mismatch`).
|
||
- The object receipt's `joules_micro` is the record's figure, 0 where `energy` is `null`. Where
|
||
the object receipt carries a class label (jwp-receipts §2.4.2), the label is the record's
|
||
class and evidence, or the unmetered marker where `energy` is `null`
|
||
(`energy_disagrees`). An edge **SHOULD** label its object receipts, and sign its groups over
|
||
the labelled payload.
|
||
- The output is a new object: its receipts bind it to the edge's key and nothing else. What
|
||
binds it to its source is the record.
|
||
|
||
An edge serving a derived object **MUST** offer its subscriber the record of each hop of the
|
||
object's chain, the delivery receipts each record names and the provenance items each carries
|
||
(§6.1), by the same carriage as the object's receipts. Without them a sink can verify the
|
||
object's delivery from the edge and nothing about where it came from. It forwards the source's
|
||
receipts unaltered (jwp-receipts §4.1).
|
||
|
||
### 6.1 Provenance carried forward
|
||
|
||
A derivation makes a new object, and nothing the source carried is in it unless the edge puts
|
||
it there: a still the pinned writer writes holds no chunk but its picture, and an explode writes
|
||
a new envelope. So a record carries its inputs' provenance forward, by reference.
|
||
|
||
The **provenance items** of an input are, of what the edge received:
|
||
|
||
- each claim (jwp-receipts §2.4.3) whose `subject_type` is `object` and whose `subject` is the
|
||
input's content hash, named by its link hash;
|
||
- each C2PA Manifest Store bound to the input, carried beside it or embedded in it (for
|
||
`wai.image.png`, the data of a `caBX` chunk, C2PA 2.4 §A.3.2), named by its SHA-256;
|
||
- for an input another edge derived, every item that edge's record carries, whatever input of
|
||
it the item came with.
|
||
|
||
A record **MUST** list every provenance item of every input, each with the input it came with
|
||
and its compartment, and an item that a record below it carries keeps that record's
|
||
compartment. Listing an item copies nothing of it: the record names it by digest, the edge
|
||
offers the item with the chain (§6), and the item's own signature, where it has one, is what
|
||
holds it to what it says. An item the record does not list, of any compartment, has been
|
||
stripped (`provenance_stripped`).
|
||
|
||
The **compartment** is the edge's reading of an item, in the split California Business and
|
||
Professions Code §22757.1 makes: **personal** where the item holds personal information, or
|
||
unique device, system or service information reasonably capable of being associated with a
|
||
particular user (§22757.1(n)), and **system** otherwise: information about the type of device,
|
||
system or service that made the content, or about its authenticity (§22757.1(p)). Information
|
||
inside a digital signature is not personal there (§22757.1(n)(2)), so a signature is system
|
||
provenance. A store embedded in an input the edge reads as system provenance unless it knows
|
||
otherwise.
|
||
|
||
This revision carries every item, personal ones included: an edge strips none, and a sink holds
|
||
the chain's items from the edge that served it. Redacting a personal item under a stated rule,
|
||
with a salted commitment that accounts for the removal so that it is never silent, is a later
|
||
revision's; until it is registered, a personal item is carried as a system one is. A C2PA
|
||
manifest an edge emits for its output names each manifest store of its inputs as an ingredient,
|
||
`parentOf` for a derivation of one input (C2PA 2.4 §18.15: `c2pa.transcoded` for a rebuild,
|
||
`c2pa.repackaged` for an explode); the reference edge emits none.
|
||
|
||
### 6.2 The AI-origin label
|
||
|
||
A record states, for each input and for its output, an **AI-origin label**: `null` where no
|
||
label is known, or, in rising order, `assist` (AI altered the content and generated none of it)
|
||
or `generate` (AI generated the content, or part of it).
|
||
A label may rise along a chain and never fall:
|
||
|
||
- an input another edge derived is labelled at least as that edge's record labels its output
|
||
(`synthesis_dropped`); a source object is labelled as its provenance states, read by the edge;
|
||
- the output is labelled at least as every input (§5.3 rule 7, `synthesis_dropped`), and at
|
||
least as the derivation's own step warrants: an edge whose step synthesizes content (a
|
||
learned upscaler behind an attested scaling, a generated frame) labels its output at least
|
||
`assist`, or `generate` where it generated content.
|
||
|
||
Every derivation this revision pins copies samples or containers and synthesizes nothing, so it
|
||
raises no label; an attested step is labelled by its edge, and a verifier checks that no label
|
||
falls, not what an attested step did. The two labels are the two highest classes of an
|
||
origin claim's order of steps, capture, convert, assist and generate, in which every derivation
|
||
this revision pins is a convert step; a later revision may carry such a claim as a provenance
|
||
item, name the lower classes, and map them onto the digital source types an export writes.
|
||
|
||
## 7. Verification
|
||
|
||
### 7.1 What a verifier holds
|
||
|
||
- **records**: record texts, the subject's among them, and the records of its chain that it
|
||
holds;
|
||
- **receipts**: group and object receipts (jwp-receipts §2.4.1, §2.4.2);
|
||
- **objects**: envelopes it holds, each by the content hash it was given under;
|
||
- **sources**: the publisher keys it subscribed to;
|
||
- **heads**: for each origin it holds one for, the head revision of that origin's key log
|
||
(jwp-receipts §7.2);
|
||
- **provenance**: the claims it holds (jwp-receipts §2.4.3), and the C2PA Manifest Stores it
|
||
holds bound to an object, by the object's content hash;
|
||
- **policy**, three members:
|
||
|
||
| member | values | default | meaning |
|
||
|---|---|---|---|
|
||
| `attested` | `accept`, `refuse` | `accept` | `refuse`: a chain with an `attest` hop is refused (`attested_refused`). |
|
||
| `recompute` | `when-held`, `required` | `when-held` | `required`: a `replicate` hop whose inputs are not all held is refused (`inputs_not_held`); `when-held`: it is accepted as `not-recomputed`. |
|
||
| `authority` | `when-given`, `required` | `when-given` | `required`: a hop that is not confirmed under §8 is refused (`edge_not_authorised`). |
|
||
|
||
A record that reads as a JSON text is held under its hash (§5.2), whether or not it then passes
|
||
§5.3, so that a hop that is held and broken is refused for what it breaks.
|
||
|
||
### 7.2 One record
|
||
|
||
The verifier verifies the subject's record by these steps, and an input's record by the same
|
||
steps when step 4 reaches it. A record verified once is not verified again. At the first step
|
||
that fails, the verification is refused with that step's reason, at that record.
|
||
|
||
1. **Reading** (§5.3): `record_malformed`, `derivation_unknown`, `params_invalid`,
|
||
`inputs_invalid`, `check_mismatch`, `energy_invalid`, `synthesis_dropped`.
|
||
2. **Signature** (§5.2): a key that fails RFC 8032 §5.1.3's decoding or is of small order is
|
||
`bad_key`; a signature that fails, `bad_signature`.
|
||
3. **Output.** The group receipt whose link hash is `output.receipt`, and the object receipt of
|
||
that group at `output.object_id`, are held (`output_receipt_not_held`); the group verifies
|
||
(jwp-receipts §3 steps 1–2), the object receipt is included at its position (§3 step 5
|
||
there), and its `content_hash` is the output's (`output_receipt_invalid`); §6's rules hold
|
||
(`output_receipt_mismatch`, `energy_disagrees`). Where the output envelope is held: it hashes
|
||
to `output.content_hash` (`output_hash_mismatch`); its capability is `output.capability`,
|
||
and, for an evaluation, its manifest is the one §4.3 writes from the record
|
||
(`output_manifest_mismatch`); and, for a `wai.image.png` output, the samples it reads as
|
||
(packaging §5's still reader) are of the record's `width` and `height` and have its `samples`
|
||
digest (`samples_mismatch`): PNG is lossless, so this holds whoever wrote it.
|
||
4. **Each input**, in order:
|
||
1. its group receipt (by `receipt`) and its object receipt (at `object_id`) are held
|
||
(`input_receipt_not_held`); the group verifies, the object receipt is included at its
|
||
position, and its `content_hash` is the input's (`input_receipt_invalid`);
|
||
2. where `derivation` is `null`, the group receipt is signed by a source key; otherwise an
|
||
edge's output is passed off as a source object, and a hop is missing (`chain_skips_hop`);
|
||
3. where `derivation` is not `null`: the input's chain is not longer than 8 hops, the
|
||
subject's record being the first (`chain_too_long`); the record of that hash is held
|
||
(`hop_not_held`) and reads (§5.3, refused at that record); its output's `content_hash`,
|
||
`capability`, `receipt` and `object_id` are the input's (`hop_mismatch`); and it is
|
||
verified by these steps, whose refusal, at that record, is this verification's;
|
||
4. where the input envelope is held: it hashes to `content_hash` (`input_hash_mismatch`); it
|
||
reads as §4.1 or §4.4 has it (`input_unreadable`); and the samples it reads at `frame`
|
||
have the digest `samples` (`samples_mismatch`);
|
||
5. its provenance (§6.1): each claim the verifier holds about it, each manifest store the
|
||
verifier holds bound to it, and, where the input envelope is held, each store embedded in
|
||
it, is an item of the record for this input, of that kind and digest; and, where an edge
|
||
derived it, every item of that edge's record is one too, with its compartment
|
||
(`provenance_stripped`). Where an edge derived it, its `synthesis` is no lower than that
|
||
edge's record's (§6.2, `synthesis_dropped`).
|
||
5. **Recomputation**, where every input is held. For a `replicate` record: evaluate the
|
||
derivation (§4.2–§4.4) over the inputs (`params_invalid` for a parameter they cannot
|
||
satisfy); the output's samples digest, size and envelope **MUST** be the record's, byte for
|
||
byte (`replicate_mismatch`). For an `attest` record: where the operation is exact, evaluate
|
||
it, and the output's `samples`, `width` and `height` **MUST** be the record's
|
||
(`samples_mismatch`); where it is not, nothing of it is recomputed. Where an input is not
|
||
held, a `replicate` record is `not-recomputed`, and refused under the policy
|
||
`recompute: required` (`inputs_not_held`).
|
||
6. **Authority** (§8): `key_log_not_held`, `edge_not_authorised`, `edge_revoked`.
|
||
7. **Policy**: an `attest` record under `attested: refuse` is `attested_refused`.
|
||
|
||
A refusal names its place: `["record", r]` for steps 1 and 2, `["record", r, "output"]` for
|
||
step 3, `["record", r, "input", i]` for step 4, `["record", r, "recompute"]` for step 5,
|
||
`["record", r, "authority", origin]` for step 6, an origin of §8 or, where the hop has none,
|
||
`null`, and `["record", r, "policy"]` for step 7, with *r* the record's position among the
|
||
records given and *i* the input's among its inputs.
|
||
|
||
### 7.3 The chain
|
||
|
||
Step 4 follows each derived input to the record that derived it, and that record's inputs to
|
||
theirs, until every input is a source object. Each hop is verified on its own, against its own
|
||
receipts and inputs. A hop that names a record the verifier does not hold is refused, not
|
||
skipped: a sink that cannot follow a chain to its source knows only that the edge delivered the
|
||
object (§6). A chain cannot loop, since a record names its inputs' records by hash and no hash
|
||
can name a record that names it; the bound of 8 hops is a bound on work.
|
||
|
||
What a verified chain establishes: the subject's object was derived, hop by hop, from the
|
||
source objects the chain reaches, by the derivations its records name with the parameters they
|
||
state, each signed by the edge that made it; each `replicate` hop whose inputs were held was
|
||
re-executed and matched byte for byte; each `attest` hop rests, at its attested step, on its
|
||
edge's signature; and each source object is one its publisher signed for delivery. It does not
|
||
establish that the publisher meant the object to be derived from; that is §8's.
|
||
|
||
### 7.4 Outcome
|
||
|
||
A verifier states `refuse` with a reason and its place, or `accept` with:
|
||
|
||
- `subject`: the subject's record hash;
|
||
- `hops`: each record verified, in the order first reached, each with its `record` hash, its
|
||
`signer` key, its `derivation`, its `check`, `checked` — `recomputed` (a `replicate` record
|
||
re-executed), `not-recomputed` (a `replicate` record with an input not held) or `attested` —
|
||
its `energy`, `null` or `{ "joules_micro", "class" }`, its `authority`: `confirmed`,
|
||
`unconfirmed` or `none` (§8), and its `synthesis` (§6.2);
|
||
- `sources`: each source object reached, in the order first reached, with its `object` content
|
||
hash and the `signer` key and `signer_id` of its group receipt;
|
||
- `energy`: for each acquisition class among the hops, in energy-measurement §2's order,
|
||
strongest first, the sum of their figures of that class; and `unmetered`, the number of hops
|
||
whose `energy` is `null`. Figures of different classes are never summed together, and an
|
||
unmetered hop is never counted as zero. The source objects' figures are their own receipts';
|
||
this total covers the derivations alone;
|
||
- `provenance`: the subject's record's items (§6.1), each its `input`, `kind`, `digest` and
|
||
`compartment`: every item of the chain, carried to the object the sink received.
|
||
|
||
## 8. Authority
|
||
|
||
A derivation is made under the authority of the origin whose content it derives from. A hop's
|
||
**origins** are the origins its chain reaches: for each source object, the origin its group
|
||
receipt's `signer_id` names where it is origin-bound (jwp-receipts §7.1), taken in ascending
|
||
order, each once. The authority is `edge/derive` (jwp-receipts §7.5), and a hop's edge needs it
|
||
from each of its hop's origins, the hops below it included: an origin lists, in its key log,
|
||
the edges it trusts with its content, as it lists its relays and stores.
|
||
|
||
For each origin of a hop, where the verifier holds a head for it: the head is a revision the
|
||
verifier holds for that origin (jwp-receipts §7.2 rules 1–3) (`key_log_not_held`); it has an
|
||
entry whose `key` is the record's `signer_pubkey` and whose `may_sign` holds `edge/derive`
|
||
(`edge_not_authorised`); and that entry's `status` is not `revoked` (`edge_revoked`).
|
||
|
||
A hop is `confirmed` when it has origins and the verifier holds a head for each that passes;
|
||
`unconfirmed` when it has origins and lacks a head for one; and `none` when it has no origin,
|
||
its sources naming no origin. Under the policy `authority: required`, a hop that is not
|
||
`confirmed` is refused (`edge_not_authorised`, at the first origin that lacks a head, or at
|
||
`null`).
|
||
|
||
`edge/derive` is an authority of its own: not `delivery`, which an edge needs from its own
|
||
origin to sign its group receipts, and not `live/relay`, since a relay forwards what it
|
||
received and an edge publishes something new. Retiring an entry does not withdraw it here;
|
||
revoking it does. A metered record's figure needs, beside it, the energy authority of its class
|
||
(`energy/<class>`, jwp-receipts §7.5), which that section's confirmation (§7.4 there) checks
|
||
under an anchored origin; this section checks `edge/derive` alone.
|
||
|
||
## 9. Reasons
|
||
|
||
The codes this extension adds, each with where it is raised:
|
||
|
||
| reason | raised at |
|
||
|---|---|
|
||
| `record_malformed` | §5.3 rule 1, §7.2 step 1 |
|
||
| `derivation_unknown` | §5.3 rule 2, §7.2 step 1 |
|
||
| `params_invalid` | §4.2, §5.3 rule 3, §7.2 steps 1 and 5 |
|
||
| `inputs_invalid` | §5.3 rule 4, §7.2 step 1 |
|
||
| `check_mismatch` | §4.5, §5.3 rule 5, §7.2 step 1 |
|
||
| `energy_invalid` | §5.3 rule 6, §7.2 step 1 |
|
||
| `bad_key` | §7.2 step 2 |
|
||
| `bad_signature` | §7.2 step 2 |
|
||
| `output_receipt_not_held` | §7.2 step 3 |
|
||
| `output_receipt_invalid` | §7.2 step 3 |
|
||
| `output_receipt_mismatch` | §6, §7.2 step 3 |
|
||
| `energy_disagrees` | §6, §7.2 step 3 |
|
||
| `output_hash_mismatch` | §7.2 step 3 |
|
||
| `output_manifest_mismatch` | §4.3, §7.2 step 3 |
|
||
| `input_receipt_not_held` | §7.2 step 4 |
|
||
| `input_receipt_invalid` | §7.2 step 4 |
|
||
| `chain_skips_hop` | §7.2 step 4 |
|
||
| `chain_too_long` | §7.2 step 4 |
|
||
| `hop_not_held` | §7.2 step 4, §7.3 |
|
||
| `hop_mismatch` | §7.2 step 4 |
|
||
| `input_hash_mismatch` | §7.2 step 4 |
|
||
| `input_unreadable` | §4.1, §4.4, §7.2 step 4 |
|
||
| `samples_mismatch` | §4.5, §7.2 steps 3, 4 and 5 |
|
||
| `provenance_stripped` | §6.1, §7.2 step 4 |
|
||
| `synthesis_dropped` | §5.3 rule 7, §6.2, §7.2 steps 1 and 4 |
|
||
| `replicate_mismatch` | §4.5, §7.2 step 5 |
|
||
| `inputs_not_held` | §7.1, §7.2 step 5 |
|
||
| `key_log_not_held` | §8, §7.2 step 6 |
|
||
| `edge_not_authorised` | §7.1, §8, §7.2 step 6 |
|
||
| `edge_revoked` | §8, §7.2 step 6 |
|
||
| `attested_refused` | §7.1, §7.2 step 7 |
|
||
|
||
## 10. Conformance
|
||
|
||
[`edge-conformance/`](../edge-conformance/) holds the corpus, in one file, `vectors.json`,
|
||
written from fixed seeds by `wai_edge_vectors gen`. Each vector is what a verifier holds (§7.1)
|
||
and the outcome §7.4 gives it. It has:
|
||
|
||
- **Sources**: one publisher, whose `signer_id` is origin-bound, signing an integer clip
|
||
(`wai.video.int_motion`), three PNG stills and a `WAI2` envelope of two renditions; and the key
|
||
log of its origin, listing two edges with `edge/derive`, one edge with `delivery` alone, and
|
||
one revoked.
|
||
- **Replicate hops, recomputed**: a frame of the clip rebuilt as a still; each operation of §4.2
|
||
but scaling over a still; a grid and an overlay over several stills; a component exploded from
|
||
the `WAI2`; and a chain of two hops by two edges, a crop of a rebuilt still, each hop
|
||
recomputed from its inputs.
|
||
- **Attested hops**: a frame rebuilt as a JPEG picture, whose samples are checked and whose
|
||
encode is not; a scaling written as a pinned still; and a replicate crop over an attested
|
||
scaling.
|
||
- **Provenance and labels**: a still whose PNG embeds a manifest store, with a claim about it
|
||
and a store beside it in the personal compartment, every item carried through two hops; a
|
||
still whose provenance says it was generated, its label carried through two hops; and a
|
||
scaling its edge labels `assist`. The corpus's manifest stores are byte strings standing for
|
||
one: a verifier names a store by its SHA-256 and parses none.
|
||
- **Policies**: a chain verified without its inputs' bytes; one under `authority: required`
|
||
whose hops are confirmed; metered hops of two classes, totalled per class.
|
||
- **Refusals**: one vector or more for each reason of §9, each a control of one defect: among
|
||
them a JPEG output whose record names no encoder, a grid stated over more inputs than its
|
||
cells, a parameter its inputs cannot satisfy, an attested JPEG whose stated samples are not
|
||
its frame's, and a scaling whose stated samples are not its still's.
|
||
|
||
A conforming verifier gives every vector the outcome, the reason, the place and the facts the
|
||
corpus states. `wai_edge_vectors verify` and the test `tests/derive_edge.rs` check that the
|
||
file regenerates byte for byte, that `wai-rs` gives every vector its outcome, that every reason
|
||
of §9 appears, and that §9 lists exactly the reference's codes; the same test runs the
|
||
reference edge, `wai_edge`, twice over a source to make a chain of two hops, and verifies it.
|
||
`edge-conformance/edge_verify.py` is the second implementation, written from this text: it
|
||
shares no code with `wai-rs`; it reads delivery receipts with the JWP corpus's second
|
||
implementation (`jwp-conformance/jwp_verify.py`), decodes `WIV1` with the integer payload
|
||
corpus's (`int-payload-conformance/int_payload_verify.py`), explodes `WAI2` envelopes with the
|
||
staged-delivery corpus's (`staged-conformance/staged_verify.py`), reads key-log heads with the
|
||
live-rooms corpus's (`live-conformance/live_rooms_verify.py`), verifies every signature with
|
||
WAI's strict rule (`claim-conformance/_ed25519.py`), and writes stills and evaluates every
|
||
operation itself. It requires every mutated copy of the corpus to fail, and, with each of its
|
||
own checks dropped in turn, requires the corpus to catch the weakened verifier.
|
||
|
||
## 11. Security considerations
|
||
|
||
- **What a record proves.** An edge's signature binds it to the derivation it states. For a
|
||
`replicate` hop whose inputs a verifier holds, nothing rests on that signature but who made
|
||
it: the verifier made the output itself. For an `attest` hop, the attested step is the edge's
|
||
word; an edge that encodes something else and signs it is caught only by a party that compares
|
||
the output with what it should hold. A sink that cannot accept that refuses attested hops.
|
||
- **Chains.** A hop that names its input as a source object when an edge derived it, or that
|
||
names a record the sink cannot get, would hide a derivation; both are refused. A chain is
|
||
verified hop by hop, so a chain is no stronger than its weakest hop, and the facts say which
|
||
hops were re-executed, which were not, and which are attested.
|
||
- **Not the source.** A derived object is never the object it was derived from. The source's
|
||
receipts do not cover it, and its own receipts are the edge's. jwp-receipts §3.2's ancestor
|
||
rule is unchanged: a soft-binding recovery attests the ancestor alone. A verified chain is
|
||
the accounted transformation that recovery lacks, and it is the record that accounts for it.
|
||
- **Energy.** Each hop's figure is its edge's, with its class, and covers that derivation
|
||
alone. Totals are per class, an unmetered hop is not zero, and the source's figures stay its
|
||
own (§7.4).
|
||
- **Keys.** An edge's key signs its records and its delivery receipts. An origin that stops
|
||
trusting an edge revokes its entry, and every record under it is refused for that origin's
|
||
content from then on.
|
||
- **Work.** A chain has at most 8 hops and a record at most 16 inputs; an output's samples are
|
||
bounded. A verifier re-executes only what it holds, and only what §4 pins.
|
||
- **Provenance.** A verifier catches an item stripped from a record only where it holds the
|
||
item, or the record below that carried it: an edge that drops an item it received, before any
|
||
party downstream saw it, is caught by a party that holds what the edge received. Once a record
|
||
lists an item, every hop above it must list it too, so the sink of a chain holds every item's
|
||
digest from the subject's record alone. A compartment is the edge's reading, and the first
|
||
edge to read an item fixes it for the rest of the chain.
|
||
- **Labels.** A label can only rise along a chain, so a transcode cannot launder a generated
|
||
object into an unlabelled one. A source object's label, and the label an attested step
|
||
warrants, are the edge's statements; a verifier checks that no label falls.
|
||
- **Privacy.** A record names objects, keys and provenance items by digest, never a viewer or a
|
||
session, and copies nothing of an item. Personal provenance is carried by digest in this
|
||
revision (§6.1); an edge that must not pass a personal item on can only refuse to derive until
|
||
the redaction rule is registered.
|