wai: bind a decode's joules to the decode (C2PA 2.4 companion assertion) #68

Merged
dcharlot merged 1 commit from wai/energy-binding into main 2026-09-03 15:10:02 -04:00
Owner

C2PA 2.4 added c2pa.environmental-sustainability, carrying energy_kwh per ISO/IEC TR 20226:2025. The dominant signed-provenance container in media now has a slot for joules — and nothing behind it.

Verified against the spec text during the 2026-08-29 sweep: the figure is self-declared, measurementMethod is optional, and it is not bound to any reproducible output. A validator can check every signature in a manifest and still not know where the number came from or what it was measured over.

This fills the slot rather than replacing it — the standard assertion is emitted for interop, with a companion carrying the three things it cannot express.

The load-bearing one: the binding

reconstructionHash is the SHA-256 of the canonical decoded pixels — the same digest reconstruction_binding commits to — so two assertions in one manifest name the same decode by construction, and a manifest whose digests disagree is self-refuting.

Because a WAI reconstruction is byte-exact on every architecture, a verifier can re-decode, confirm the figure is about those bytes, recompute workUnits from the descriptor rather than trusting it, and re-derive the rate — leaving exactly one unverifiable term: the joules.

That is the point, and also the honest limit. The extension states plainly that a binding does not prove the figure:

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 one number — and knowing precisely which number is the difference between a measurement and an assertion.

The other two

  • Acquisition class (HwShunt / ModelBased / Estimator) — a counter reading and a modelled constant are different claims occupying the same field in the standard assertion.
  • Coverage (Whole / Partial) — a partial reading is not wrong; presenting it as a total is.

Deliberate refusals

  • A zero measurement is rejected, not recorded. A decode that measured 0 µJ did not consume nothing — it failed to measure, and the honest artifact is the absence of an energy assertion.
  • method is required, where the standard leaves measurementMethod optional — which is exactly the field that stays empty in practice.
  • carbon_kgco2e and water_litres are not emitted. Deriving them from an energy figure and a grid-average factor, then placing them beside a measured joule figure in one assertion with no per-field provenance, is precisely how a model comes to be read as a measurement.

Units

Integer microjoules are authoritative; energy_kwh is derived. One µJ is 2.8e-13 kWh, so an image decode lands near 1e-9 kWh, where the float's trailing digits are conversion artifacts.

The first draft of that unit test asserted the magnitude from memory and was wrong by two orders. It now asserts against computed arithmetic, and the comment says why — the hazard is the unit itself.

Verification

76 wai-rs tests pass under --features provenance (6 new) · no clippy warnings in the new file · extension linked from SPEC.md beside its sibling.

C2PA 2.4 added `c2pa.environmental-sustainability`, carrying `energy_kwh` per ISO/IEC TR 20226:2025. **The dominant signed-provenance container in media now has a slot for joules — and nothing behind it.** Verified against the spec text during the 2026-08-29 sweep: the figure is **self-declared**, `measurementMethod` is **optional**, and it is **not bound to any reproducible output**. A validator can check every signature in a manifest and still not know where the number came from or what it was measured over. This fills the slot rather than replacing it — the standard assertion is emitted for interop, with a companion carrying the three things it cannot express. ## The load-bearing one: the binding `reconstructionHash` is the SHA-256 of the canonical decoded pixels — **the same digest `reconstruction_binding` commits to** — so two assertions in one manifest name the same decode by construction, and a manifest whose digests disagree is self-refuting. Because a WAI reconstruction is byte-exact on every architecture, a verifier can re-decode, confirm the figure is about *those* bytes, recompute `workUnits` from the descriptor rather than trusting it, and re-derive the rate — leaving **exactly one** unverifiable term: the joules. That is the point, and also the honest limit. The extension states plainly that a binding does **not** prove the figure: > 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 one number — and knowing precisely which number is the difference between a measurement and an assertion. ## The other two - **Acquisition class** (`HwShunt` / `ModelBased` / `Estimator`) — a counter reading and a modelled constant are different claims occupying the same field in the standard assertion. - **Coverage** (`Whole` / `Partial`) — a partial reading is not wrong; presenting it as a total is. ## Deliberate refusals - **A zero measurement is rejected, not recorded.** A decode that measured 0 µJ did not consume nothing — it failed to measure, and the honest artifact is the *absence* of an energy assertion. - **`method` is required**, where the standard leaves `measurementMethod` optional — which is exactly the field that stays empty in practice. - **`carbon_kgco2e` and `water_litres` are not emitted.** Deriving them from an energy figure and a grid-average factor, then placing them beside a measured joule figure in one assertion with no per-field provenance, is precisely how a model comes to be read as a measurement. ## Units Integer microjoules are authoritative; `energy_kwh` is derived. One µJ is 2.8e-13 kWh, so an image decode lands near **1e-9 kWh**, where the float's trailing digits are conversion artifacts. > The first draft of that unit test asserted the magnitude from memory and was **wrong by two orders**. It now asserts against computed arithmetic, and the comment says why — the hazard is the unit itself. ## Verification 76 wai-rs tests pass under `--features provenance` (6 new) · no clippy warnings in the new file · extension linked from `SPEC.md` beside its sibling.
wai: bind a decode's joules to the decode (C2PA 2.4 companion assertion)
Some checks failed
no-vanity-metrics / Scan committed Markdown for vanity metrics (pull_request) Successful in 2s
WAI conformance / cargo-class corpora · linux (pull_request) Successful in 29s
WAI conformance / cross-arch byte-equality · linux (pull_request) Successful in 14s
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Successful in 1m58s
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Successful in 46s
WAI conformance / cargo-class corpora · macos (pull_request) Successful in 3m44s
WAI conformance / cross-arch byte-equality · macos (pull_request) Successful in 35s
WAI conformance / cargo-class corpora · windows (pull_request) Has been cancelled
WAI conformance / cross-arch byte-equality · windows (pull_request) Has been cancelled
c291fec716
C2PA 2.4 added `c2pa.environmental-sustainability`, carrying `energy_kwh` per
ISO/IEC TR 20226:2025. The dominant signed-provenance container in media now has
a slot for joules — and nothing behind it. Verified against the spec text: the
figure is self-declared, `measurementMethod` is optional, and it is not bound to
any reproducible output. A validator can check every signature in a manifest and
still not know where the number came from or what it was measured over.

This fills the slot rather than replacing it: the standard assertion is emitted
for interoperability, with a companion carrying the three things it cannot
express.

The load-bearing one is the binding. `reconstructionHash` is the SHA-256 of the
canonical decoded pixels — the same digest `reconstruction_binding` commits to,
so two assertions in one manifest name the same decode by construction and a
manifest whose digests disagree is self-refuting.

Because a WAI reconstruction is byte-exact on every architecture, a verifier can
re-decode, confirm the figure is about *those* bytes, recompute `workUnits` from
the descriptor rather than trusting it, and re-derive the rate — leaving exactly
one unverifiable term, the joules. That is the point and also the honest limit:
the extension says plainly that a binding does NOT prove the figure. An energy
claim about an output nobody can reproduce is not checkable at all; one about an
output anyone can reproduce is checkable except for a single number, and knowing
which number that is, is the whole difference.

The other two: an acquisition class (`HwShunt`/`ModelBased`/`Estimator`) because
a counter reading and a modelled constant are different claims occupying the
same field in the standard assertion; and a coverage statement, because a
partial reading is not wrong but presenting it as a total is.

Deliberate refusals:

- A zero measurement is rejected, not recorded. A decode that measured 0 uJ did
  not consume nothing; it failed to measure, and the honest artifact is the
  absence of an energy assertion.
- `method` is required, where the standard leaves `measurementMethod` optional —
  which is exactly the field that stays empty in practice.
- `carbon_kgco2e` and `water_litres` are NOT emitted. Deriving them from an
  energy figure and a grid-average factor, then placing them beside a measured
  joule figure in one assertion with no per-field provenance, is precisely how a
  model comes to be read as a measurement.

Integer microjoules are authoritative and `energy_kwh` is derived: one
microjoule is 2.8e-13 kWh, so an image decode lands near 1e-9 kWh, where the
float's trailing digits are conversion artifacts. The first draft of the unit
test asserted that magnitude from memory and was wrong by two orders; it now
asserts against computed arithmetic, and the comment says why.

Verified: 76 wai-rs tests pass under `--features provenance` (6 new), no clippy
warnings in the new file, extension linked from SPEC.md beside its sibling.
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Transaction-Science/open-standards!68
No description provided.