The text W1-5 owes, on the code and corpora already landed on this branch: - jwp-receipts §2.3, §2.4.1, §3 step 3 and step 6, §6.1: receipt chains and the `group_id` guard are per (`signer_pubkey`, `track_alias`). The text disagreed with itself (per key in §2.4.1 and §6.1's first bullets, per track in the signing payload and §6.1's last); it now says per key and track throughout. A group that passes steps 1-3 can head its chain whether or not its objects arrived. §6.1 names the reference verifier's opt-in guard and freshness bound, and the new step-3 reasons `group_id_not_increasing` and `group_not_fresh` join §3.3. - §3 *Verified by inclusion*: an object receipt checked without the object's bytes is `inclusion-verified`, never acceptance or delivery. §4.1 makes a relay forward the receipts of objects it does not forward; §4.2 lets it sign a `delivery-gap` claim; §4.4 says how a subscriber accounts for an object it did not receive. - §2.4.4 rows and new §2.4.10 `stage` and §2.4.11 `delivery-gap`, their reasons in §3.1, their authorities in §7.5, their corpora in §5.9 (v6_per_track_chain) and §5.10. §2.4.9's vocabulary row names staged delivery's `urn:wai:staged-delivery:1`, which energy-binding §7.2 registers. - staged-delivery §10 splits into §10.1 attestation (naming the two evidences and the per-track chains) and §10.2 claims; its attestation references point to §10.1. http-delivery §10.5 says what an abandoned refinement is in receipt terms. SPEC's pointer to jwp-receipts lists the new classes. CHANGELOG entry. - The rule-5 vector is named for rule 5 (`stage_null_delivered_skips_rule5`). Co-Authored-By: Claude Opus 5.5 <[email protected]>
345 lines
19 KiB
Markdown
345 lines
19 KiB
Markdown
# WAI Extension: Energy Binding
|
||
|
||
> Status: Draft. Binds the **energy a reconstruction cost** to *that*
|
||
> reconstruction, as a companion to C2PA 2.4's
|
||
> `c2pa.environmental-sustainability` assertion — supplying the acquisition
|
||
> class, the coverage, and the outcome binding the standard assertion has no
|
||
> field for. Reference impl: the `energy_binding` module of `wai-rs`
|
||
> (feature `provenance`), emitted into a real signed manifest by `c2pa_emit`
|
||
> (feature `c2pa_emit`). Companion to
|
||
> [Reproducible-Reconstruction Binding](reconstruction-binding.md) and the
|
||
> acquisition protocol in [Energy Measurement](energy-measurement.md).
|
||
> Keywords **MUST**, **MUST NOT**, **SHOULD**, **MAY** are RFC 2119/8174.
|
||
|
||
## 1. Why a companion assertion
|
||
|
||
C2PA 2.4 defines `c2pa.environmental-sustainability`, carrying `energy_kwh`
|
||
(per ISO/IEC TR 20226:2025) alongside carbon and water figures. The dominant
|
||
signed-provenance container in media now has a place to put joules.
|
||
|
||
Three properties it does not have, and this extension supplies:
|
||
|
||
1. **It is self-declared.** `measurementMethod` is optional, and an
|
||
unattributed number cannot be compared with any other number. In practice
|
||
the optional field is the one that stays empty.
|
||
2. **It carries no acquisition class.** A hardware counter reading and a
|
||
modelled constant occupy the same field and are indistinguishable to a
|
||
reader, though they are different claims about the world.
|
||
3. **It is not bound to any reproducible output.** The assertion is about an
|
||
asset; a validator can check every signature in a manifest and still not
|
||
know what the figure was measured *over*.
|
||
|
||
The third is the load-bearing one. A signature proves who said a number, never
|
||
that the number is about anything checkable.
|
||
|
||
## 2. What the binding is
|
||
|
||
An energy binding is the tuple
|
||
|
||
```
|
||
{ reconstructionHash, alg, microjoules, acquisition, [evidence], coverage, workUnits, method }
|
||
```
|
||
|
||
where `[evidence]` is present exactly when `acquisition` is a refined hardware
|
||
class, `OnChipCounter` or `CalibratedInstrument`
|
||
([energy-measurement](energy-measurement.md) §2.3–§2.5); legacy `HwShunt`
|
||
carries none:
|
||
|
||
| field | present for | meaning |
|
||
|---|---|---|
|
||
| `uncertainty` | `OnChipCounter`, `CalibratedInstrument` | `{ relativePpm, windowUs }` — the declared standard (k = 1) relative uncertainty in ppm and the integration window in µs |
|
||
| `counterFiltering` | `OnChipCounter` | `On` / `Off` / `Unknown` |
|
||
| `calibrationRef` | `CalibratedInstrument` | identifier of the calibration certificate |
|
||
|
||
carried as a third-party C2PA assertion under the reverse-domain label
|
||
`science.transaction.wai.energy.reconstruction`, **alongside** — never instead
|
||
of — the standard `c2pa.environmental-sustainability` assertion.
|
||
|
||
`reconstructionHash` is the SHA-256 of the canonical decoded pixels, the same
|
||
digest [reconstruction-binding](reconstruction-binding.md) commits to. Both
|
||
assertions in one manifest therefore name the same decode by construction, and
|
||
a manifest whose two digests disagree is self-refuting.
|
||
|
||
## 3. What this makes checkable, and what it does not
|
||
|
||
Where the reconstruction is at `decode-equivalence` — reproducible byte for byte
|
||
on every conforming sink ([determinism-tiers](determinism-tiers.md) §2) — a
|
||
verifier holding the codestream can do the following with any conforming decoder,
|
||
and over `wai.image.jpegai`'s exact-decoder reconstruction only with that decoder
|
||
under the conditions in [determinism-tiers](determinism-tiers.md) §5
|
||
([reconstruction-binding](reconstruction-binding.md) §2):
|
||
|
||
- re-decode under the pinned descriptor,
|
||
- hash the pixels and confirm the energy figure is about **those** bytes,
|
||
- recompute `workUnits` from the descriptor rather than trusting it,
|
||
- and re-derive the rate.
|
||
|
||
What remains unverifiable is the measurement: the joules, and every field that
|
||
describes how they were obtained — `acquisition` and its evidence, `coverage`,
|
||
`method`, and the values of any report about the figure
|
||
([energy-measurement](energy-measurement.md) §6). No verifier can re-measure any
|
||
of them; each is its signer's statement. Some can be checked for consistency
|
||
without re-measuring anything: the declared uncertainty against the resolution
|
||
floor (§4), a calibration reference against the certificate it names
|
||
(energy-measurement §2.5), and a report's bracket against the figure and the
|
||
declared uncertainty it must give, and against the liveness rule
|
||
(energy-measurement §6.2). Consistency is not truth. That is the point of the
|
||
construction, and the honest limit of it. An energy claim about an output nobody
|
||
can reproduce is not checkable at all. An energy claim about an output anyone can
|
||
reproduce is checkable except for its measurement — and knowing precisely which
|
||
fields those are is the difference between a measurement and an assertion.
|
||
|
||
An implementation **MUST NOT** describe an energy binding as proving the energy
|
||
figure. It binds the figure to a reproducible outcome; it does not attest the
|
||
measurement.
|
||
|
||
## 4. Normative rules
|
||
|
||
- The binding **MUST** carry an acquisition class (`CalibratedInstrument` /
|
||
`OnChipCounter` / `ModelBased` / `Estimator`, per
|
||
[energy-measurement](energy-measurement.md) §2) and a coverage statement
|
||
(`Whole` / `Partial`, §5 there). A refined hardware class **MUST** carry its
|
||
evidence as above, and a new binding **MUST NOT** use the legacy `HwShunt`
|
||
label.
|
||
- A refined hardware class's declared uncertainty **MUST** cover the figure's
|
||
own rounding ([energy-measurement](energy-measurement.md) §2.3):
|
||
`microjoules` is a whole number of microjoules, so it is uncertain by at
|
||
least `0.5/√3` µJ (≈ 0.289 µJ), and `microjoules × uncertainty.relativePpm`
|
||
**MUST** be at least 288 676.
|
||
- A verifier reading a binding **MUST** accept `HwShunt` — it is what bindings
|
||
signed before the hardware class was refined say, and it carries no evidence
|
||
fields — and **MUST** reject a refined hardware class without its `uncertainty`, a
|
||
refined hardware class whose `microjoules × relativePpm` is below 288 676, a
|
||
`CalibratedInstrument` without its `calibrationRef`, and evidence fields beside
|
||
a class that carries none. The floor needs nothing but the body and is
|
||
computed in integers, so every verifier reaches the same verdict. A C2PA
|
||
validator checks the signature over the body; these rules are about what the
|
||
body says, and no validator applies them on the implementation's behalf.
|
||
- `method` **MUST** be present and name the published protocol the figure was
|
||
obtained under. This is deliberately stricter than the standard assertion's
|
||
optional `measurementMethod`.
|
||
- A figure of zero **MUST NOT** be recorded. A reconstruction that measured
|
||
0 µJ did not consume nothing; it failed to measure, and the honest artifact is
|
||
the absence of an energy assertion rather than a zero in one.
|
||
- `microjoules` is an integer and is authoritative. `energy_kwh` in the standard
|
||
assertion **MUST** be treated as a derived interop value: one microjoule is
|
||
2.8 × 10⁻¹³ kWh, so a whole image decode lands near 10⁻⁹ kWh, where the
|
||
trailing digits of the float are artifacts of the conversion rather than of
|
||
the measurement.
|
||
- An implementation **MUST NOT** populate `carbon_kgco2e` or `water_litres`
|
||
from an energy figure and a grid-average factor while presenting the result
|
||
as measured. Those are modelled quantities; emitting them beside a measured
|
||
joule figure, in the same assertion, with no per-field provenance, is how a
|
||
model comes to be read as a measurement.
|
||
- A `Partial` figure **MUST NOT** be presented as the reconstruction's total.
|
||
- A binding's identity is `BLAKE3("wai:energy-binding-id\x01" ‖ JCS(body))`,
|
||
where `body` is the companion assertion's body as a JSON value and JCS is
|
||
RFC 8785. A body holding an integer above 2⁵³ − 1 has no identity. A report
|
||
about the binding's figure ([jwp-receipts](jwp-receipts.md) §2.4.8–§2.4.9)
|
||
names it by this identity under the figure profile `wai:energy-binding-id`
|
||
([energy-measurement](energy-measurement.md) §6.1). No member is added to
|
||
the body, so a body written before this rule has the same bytes and an
|
||
identity. No Ed25519 key signs a binding's figure, so an `operating-point`
|
||
claim may name it and a `measurement` claim may not (jwp-receipts §2.4.8).
|
||
|
||
## 5. Relationship to the standard assertion
|
||
|
||
Interoperability is the reason both are emitted. A validator that understands
|
||
only C2PA 2.4 reads `energy_kwh` and a populated `measurementMethod`. A
|
||
validator that understands this
|
||
extension additionally learns how the figure was acquired, how much of the work
|
||
it covers, and which reconstruction it describes — and can check the last of
|
||
those itself.
|
||
|
||
## 6. Emitting a manifest
|
||
|
||
The assertion shapes above are emitted into an actual signed C2PA manifest by the
|
||
`c2pa_emit` module of `wai-rs`. Four decisions in that emitter are normative for
|
||
anyone writing their own, because each is a place where the obvious
|
||
implementation produces a manifest that validators reject, or that overstates
|
||
what a decoder knows.
|
||
|
||
### 6.1 The inception action is `c2pa.opened`, not `c2pa.created`
|
||
|
||
A manifest **MUST NOT** describe a reconstruction with `c2pa.created`.
|
||
|
||
C2PA 2.x requires every `c2pa.created` action to carry a `digitalSourceType`, and
|
||
that vocabulary describes how the *content* came to exist — captured, drawn by a
|
||
human, generated by a model. A decoder does not know that. It reconstructed
|
||
pixels from a codestream, and whatever was encoded could have been any of them.
|
||
Selecting `trainedAlgorithmicMedia` because a codec has a neural stage would
|
||
assert that a compressed photograph is AI-generated content, which is false, and
|
||
the vocabulary has no member meaning "decoded; origin not visible from here".
|
||
|
||
A decode is in any case not a creation. It is a conversion of an existing asset
|
||
into another representation, which C2PA already models: the codestream is a
|
||
`parentOf` ingredient, the manifest **opens** it, and `c2pa.converted` records
|
||
what was done. No action asserts a `digitalSourceType`, so no claim about the
|
||
original's origin is made anywhere — which is precisely the position a decoder is
|
||
in.
|
||
|
||
### 6.2 The codestream is carried as the parent ingredient
|
||
|
||
An emitted manifest **MUST** carry the codestream as a `parentOf` ingredient.
|
||
|
||
§3 turns on a verifier being able to re-decode. A manifest that carries the
|
||
reconstruction digest but does not name what produces it hands the verifier a
|
||
digest and no way to reach it. The digest says *which* decode the joules were
|
||
measured over; the parent ingredient says *what to decode to get there*. Both
|
||
halves, or the check §3 describes is not actually available.
|
||
|
||
### 6.3 The bindings are `created` assertions, not `gathered`
|
||
|
||
The bindings **MUST** be marked as attributed to the signer.
|
||
|
||
C2PA distinguishes assertions the signer stands behind from material gathered
|
||
elsewhere and carried along — the same distinction between a claim and hearsay
|
||
that the acquisition ladder draws inside the figure itself. These are first-party
|
||
statements: the signer measured the joules and hashed the pixels.
|
||
|
||
This is load-bearing rather than editorial. A validator looks for the inception
|
||
action among the created assertions, so an actions assertion left gathered yields
|
||
a manifest that signs without complaint and every validator then rejects as
|
||
`assertion.action.malformed` — a failure that surfaces only on the far side of a
|
||
signature.
|
||
|
||
### 6.4 What a signature does not settle
|
||
|
||
An emitted manifest signed by an untrusted certificate reaches
|
||
`ValidationState::Valid` — well-formed, signature intact, asset unmodified — and
|
||
not `ValidationState::Trusted`, which additionally requires a certificate
|
||
chaining to a known authority. An implementation **MUST NOT** present the first
|
||
as though it were the second.
|
||
|
||
The gap between them is the whole of *who says so*, and it is orthogonal to
|
||
everything §3 makes checkable: a perfectly reproducible reconstruction signed by
|
||
nobody in particular is still a claim from nobody in particular.
|
||
|
||
## 7. The operating point, and why a joule figure alone is not enough
|
||
|
||
### 7.1 Degradation-blindness
|
||
|
||
A receipt that records the joules a decode spent, and nothing about how much work
|
||
it was asked to do, is **degradation-blind**. A sink can always spend fewer joules
|
||
by decoding worse, and a figure that cannot distinguish *efficient* from
|
||
*degraded* rewards the wrong behaviour precisely where the number is meant to
|
||
carry weight.
|
||
|
||
This is not hypothetical. ISO/IEC 23001-11 defines a decoding operation reduction
|
||
request (DOR-Req) with which a receiving device asks the remote encoder for a less
|
||
complex bitstream, so that its own decoder does less, and notes that its metadata
|
||
can save more energy "at the expense of some QoE degradation". Its energy
|
||
quantities are expected or potential savings stated in advance, not energy that
|
||
was spent (determinism-tiers §2), and a WAI receipt without §7.2's fields records
|
||
only what was spent, with nothing about the operating point it was spent at.
|
||
`energy-measurement.md` §5's recomputable-work-unit rule blocks the substitution
|
||
at the byte-exact tier, but not at `entropy-consistency`, and not for the
|
||
conventional codecs WAI already carries.
|
||
|
||
The rules below make the operating point part of the claim, so the joule figure
|
||
is scored against something rather than merely reported.
|
||
|
||
### 7.2 Request, commitment, delivery
|
||
|
||
Where one party requests an operating point and another acts on it, all three
|
||
**MUST** be sealed, as `op_requested`, `op_committed` and `op_delivered`, in an
|
||
`operating-point` claim about the figure spent at that operating point
|
||
([jwp-receipts](jwp-receipts.md) §2.4.9). The claim names that figure by its
|
||
figure profile ([energy-measurement](energy-measurement.md) §6.1) and leaves the
|
||
receipt that seals it unchanged:
|
||
|
||
| field | meaning |
|
||
|---|---|
|
||
| `op_requested` | the operating point the requesting party asked for |
|
||
| `op_committed` | the operating point the acting party said it would use |
|
||
| `op_delivered` | the operating point actually used |
|
||
|
||
In the 23001-11 case in §7.1 the receiver requests and the sender acts: a
|
||
receiving device sends a DOR-Req, and the remote encoder answers with a DOR-Resp
|
||
saying how it decided to answer.
|
||
|
||
An operating point is a class-scoped descriptor — frame rate, spatial resolution,
|
||
a complexity target — and this extension does not define its vocabulary. The
|
||
claim names the vocabulary its parameters are drawn from by URI, and writes each
|
||
operating point as an object from parameter to unsigned integer, so whether two
|
||
operating points are equal is a comparison of canonical bytes. What this extension
|
||
fixes is that the three are **distinct fields**, because the interesting cases are
|
||
exactly the ones where they differ, and that whether the commitment was met is
|
||
derived from them, never stated.
|
||
|
||
- A claim **MUST NOT** carry `op_delivered` without `op_committed`
|
||
(`operating_point_delivered_alone`). A delivered figure with nothing to score it
|
||
against is the degradation-blind case §7.1 describes.
|
||
- `op_committed` **MUST** be recorded as what the acting party stated, even where
|
||
it differs from the request. A commitment that silently becomes the request is not
|
||
a commitment. No verifier can check what was stated; the claim is its signer's
|
||
account of it.
|
||
- Where `op_delivered` differs from `op_committed`, the claim records both and
|
||
states nothing about whether the commitment was met. A reader **MUST NOT** report
|
||
the commitment as met unless it derives that `op_delivered` equals
|
||
`op_committed` (jwp-receipts §2.4.9).
|
||
- An operating point **MUST NOT** be recorded against an unmetered figure or a
|
||
figure labelled with the legacy `HwShunt`. An `operating-point` claim restates a
|
||
figure with a refined or model class: a claim restating `HwShunt` is refused
|
||
(`energy_legacy_acquisition`), and one naming a figure that carries the unmetered
|
||
marker or no class is refused against it (`figure_disagrees`,
|
||
`figure_unclassified`).
|
||
- A receipt over a payload that an encoder altered to save energy, whether at a
|
||
receiver's request (such as a decoding-operation-reduction or attenuated-video
|
||
request) or on its own account, covers that payload, and **MUST NOT** be
|
||
presented as covering the payload it stands in for — see
|
||
[determinism-tiers](determinism-tiers.md) §2, *What a tier covers*.
|
||
|
||
This converts a recorded number into a number measured against a promise. Neither
|
||
the prescriptive standards nor the measurement standards provide that pair: one
|
||
says what should be spent, the other what was, and nobody scores the second
|
||
against the first.
|
||
|
||
Staged delivery registers the vocabulary `urn:wai:staged-delivery:1`, whose one
|
||
parameter is `wai-stage:layers`, the number of layers a prefix of a stage set holds
|
||
([staged-delivery](staged-delivery.md)). A `stage` claim (jwp-receipts §2.4.10) records
|
||
the stage requested, committed, delivered and presented as points of it. Its signer
|
||
observes what arrived rather than scoring an operation against a request, so it may
|
||
record a delivered stage with no commitment beside it.
|
||
|
||
### 7.3 The residual
|
||
|
||
When a declared complexity or operating-point descriptor and a measured energy
|
||
figure both cover the same reconstruction, the claim about that figure **MUST**
|
||
carry the prediction the descriptor gives: the `prediction` member of an
|
||
`operating-point` claim ([jwp-receipts](jwp-receipts.md) §2.4.9), holding the
|
||
energy the descriptor implies for that work, the model that computed it, and the
|
||
hash of the descriptor. The **residual** — the signed difference between the
|
||
energy measured and the energy the descriptor implies — is not written. Every
|
||
reader derives the same one, `joules_micro − predicted_joules_micro`, so it can be
|
||
neither stated inconsistently with the two figures nor dropped once the
|
||
prediction is sealed.
|
||
|
||
- `predicted_joules_micro` **MUST** be stated for the same work as the figure
|
||
(`energy-measurement.md` §5), or the residual compares two different things and
|
||
means nothing. A verifier checks that the claim names the figure; it cannot
|
||
check what work the prediction was made for.
|
||
- A residual **MUST NOT** be reported without the acquisition class of the
|
||
measured term. An `operating-point` claim restates the figure's class and is
|
||
refused where the figure has none (`figure_unclassified`). A residual against an
|
||
`Estimator` figure is a difference between two predictions.
|
||
- An implementation **MUST NOT** omit a prediction because the residual is large.
|
||
The large ones are the informative ones; omission converts a falsifiable claim
|
||
into an unfalsifiable one. No verifier can tell an omitted prediction from one
|
||
never made, so this rule binds producers.
|
||
|
||
The residual is what makes the predictive half checkable. A complexity descriptor
|
||
is a prediction about energy, and until something records what actually happened
|
||
against it, the prediction is never wrong. This closes that loop in the artifact
|
||
rather than in a report.
|
||
|
||
### 7.4 Companion capability
|
||
|
||
Green-metadata semantics are not yet registered as a capability. When they are,
|
||
they are registered the way [`wai.meta.hdr10plus`](../research/media-codec-registry.md)
|
||
and `wai.meta.dovi` are: the capability names the descriptor class — complexity
|
||
metrics, quality recovery, display adaptation — and the sink supplies the decoder.
|
||
**No carriage is adopted**: ISOBMFF and DASH carriage of green metadata belongs to
|
||
another layer, and WAI naming a descriptor does not make WAI responsible for moving
|
||
it. Until then, an `operating-point` claim names the vocabulary of its operating
|
||
points and the model behind its prediction by URI (jwp-receipts §2.4.9).
|