open-standards/wai/extensions/energy-binding.md
Transaction Science 27e470578a wai: jwp-receipts — chains per (key, track), inclusion-verified, the stage and delivery-gap classes
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]>
2026-10-06 06:01:26 -04:00

345 lines
19 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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