WAI Extension: Sink Energy
Mirrored from the canonical text at commit 260cbd09 ().
Status: Draft. Registers the record
wai.sink.energy: what decoding cost at the sink, per stream and per segment, with the acquisition class of energy-measurement and its uncertainty, and how one viewing session’s energy splits into its origin, its relay, the sink’s decode and its display. Three signed records: a sink’s decode table (§4), a measured figure per registered capability, decode path and sink energy profile, produced by a pinned procedure and committed with its raw readings; a segment receipt (§5), the decode energy of one segment of one stream, measured, taken from the sink’s own table, estimated, or stated as unmetered; and a session receipt (§6), the per-hop split over the four hop roles a stream log uses (origin, relay, sink decode, display), each hop a figure with its own class, the unmetered marker where the hop took place unmeasured, or absent where the sink states nothing about it. The counters, scopes, idle baselines and windows are energy-measurement §9’s sink profiles. No new transport, no change to the wire format or to SPEC §4’s algorithm. The reference implementation is thesink_energymodule ofwai-rs(featuresink_energy), whose readers are insink_meterand whose table procedure is the binarywai_sink_table; the corpus islive-conformance/sink-energy/, with the measured tables of the reference sinks under itstables/, and its second implementation islive-conformance/sink_energy_verify.py. Keywords MUST, MUST NOT, SHOULD, MAY are RFC 2119/8174.Texts read for this revision: RFC 8032 (Ed25519); RFC 8785 (JSON Canonicalization Scheme); RFC 8259; FIPS 180-4 (SHA-256); JCGM 100:2008 (GUM) §4.3.7 and F.2.2.1; Commission Delegated Regulation (EU) 2019/2013, for the on-mode power a display’s energy label states; arXiv 2402.09001, 2603.18305 and 2511.00707, for §4.6’s outside measurements of decode energy. A section number in this text is this extension’s unless it names another document.
1. Scope
In scope: the three records, their signatures and identities (§3); the decode table, the procedure that produces it, how each row’s status and figure follow from its rounds, and outside measurements of decode energy (§4); the segment receipt and its four methods (§5); the session receipt and its per-hop split (§6); verification (§7); the reasons (§8); and what a conforming implementation publishes (§9).
Out of scope: a transport; how a counter works, which energy-measurement §9 states per platform; the energy of networks, which no sink can read and this extension never attributes to a byte; and what a party does with a figure.
Relationship to other documents:
- energy-measurement. Every figure carries a class of its §2, encoded as its §2.7 writes the JSON members. A measured figure follows its §3, §3.1, §4 and §6.2. The profiles are its §9. A record here carries its report’s values inline, under the figure’s own signature (its §6).
- browser-sink §6. A page’s
energyentry for"<capability> <route>"MAY be a row of its sink’s decode table whose status isfigure:joules_microthe row’s,decodes1,window_us,class,relative_ppmandfilteringthe row’s class and evidence,coverageandfabricsthe row’s. - jwp-receipts §2.1. A segment receipt names what it decoded by that section’s content hash.
- live-metering §3. A relay’s hop entry is read from a hop’s
per-hop session record,
WMH1. - stream-termination §2, §3.5 and §4.6. The hop roles, the hop entries’ figure, marker and absence, and each role’s figure for one segment of a stream log. This extension’s hops are that table (§6.2), not a second one, and a segment receipt here is the sink-decode hop’s figure for its segment.
- staged-measurement §8. A decode figure never includes a display (its paragraph 8.4). The display is a hop of its own (§6.3), always an estimate.
Relationship to the core spec: wai.sink.energy is a record (SPEC §5).
It names no payload format and is not dispatchable.
2. Terms
- Sink: the party that decodes and presents. Reference sink: a machine the measured tables of the corpus were taken on.
- Path:
software(the decode runs on general-purpose processors) orhardware(a fixed-function decoder of the platform does it). - Profile: a sink energy profile of energy-measurement §9:
macos-task,macos-gpu,linux-rapl,linux-nvml, and the estimatorsestimator-cpu-time/1andestimator-display/1. - Unit: what a figure is per:
image(one picture ofwidth×height),frame(one video frame ofwidth×height),content-ms(one millisecond of audio content) orbyte(one byte of decoded text). A row or a segment states how many units its figure covers (units). - Class, evidence, unmetered marker, bracket, liveness, ratio,
verdict: as in energy-measurement §2–§4 and §6.2. The class is written as
jwp-receipts §2.4.5 writes it:
joules_micro,energy_provenance,energy_uncertainty({ "relative_ppm", "window_us" }),energy_counter_filteringandenergy_calibration_ref, eachnullwhere the class carries none; unmetered isjoules_micro0 with all four othersnull. - Bracket summary:
{ "n", "mean_uw", "sd_uw", "live_intervals", "longest_live_run" }, energy-measurement §6.2’s integers for one bracket. - Hexadecimal is lowercase. Every number in a record is an integer from 0 to 2⁵³ − 1.
⌊x⌋is the floor; every division below is exact integer arithmetic.
3. The records, their signatures and identities
Each record is one JSON object (RFC 8259). Its body is the object without its sig
member. A record is signed with Ed25519 (RFC 8032) by the key its signer member names,
64 hexadecimal digits, over
domain ‖ JCS(body)
where JCS is RFC 8785’s canonical form, and sig is the 64-byte signature as 128
hexadecimal digits. A signature is verified as WAI verifies every signature (SPEC §7,
Signatures). The record’s identity is
SHA-256(id-domain ‖ JCS(body) ‖ signature bytes)
| record | first member | domain | id-domain |
|---|---|---|---|
| decode table (§4) | "wai_sink_table": 1 | wai:sink-table\x01 | wai:sink-table-id\x01 |
| segment receipt (§5) | "wai_sink_segment": 1 | wai:sink-segment\x01 | wai:sink-segment-id\x01 |
| session receipt (§6) | "wai_sink_session": 1 | wai:sink-session\x01 | wai:sink-session-id\x01 |
The first member names the record and its version; every member the tables below list is
required, and a record with a member missing, a member they do not list, or a value of
another type is refused (record_malformed). Terms are compared as exact strings.
4. The decode table
A decode table is a sink’s measured decode energy: for each registered capability and decode path, on each profile the sink can read, one figure per completion of a stated work item, from repeated bracketed measurements, with every round’s values in the record and every raw reading in a file it binds.
4.1 The record
| member | type | meaning |
|---|---|---|
wai_sink_table | integer | 1 |
signer | 32 B hex | the key that signs the table |
procedure | term | wai-sink-table/1 (§4.4) |
procedure_sha256 | 32 B hex | SHA-256 of the procedure text committed with the table |
readings_sha256 | 32 B hex | SHA-256 of the readings file committed with the table (§4.3) |
machine | object | { "os": string, "arch": string, "processor": string, "cores": integer, "memory_gib": integer } |
build | object | the decoders’ build, as jwp-receipts §2.4.8 writes one: { "profile": "release" or "debug", "toolchain": string or null, "target": string or null } |
measured | string | the date the rounds were taken, YYYY-MM-DD |
rounds | integer | rounds per row: odd, at least 7 |
sample_us | integer | the sampling interval every bracket was read at, in microseconds |
counters | array | the sink’s survey: one { "profile": term, "status": term, "detail": string } per counter it looked for, in ascending order of (profile, detail); status is one of energy-measurement §9.1’s five, or implausible (§9.3 there) |
rows | array | the rows (§4.2), in strictly ascending order of (capability, path, decoder, profile), strings by their bytes and a null profile first |
4.2 A row
| member | type | meaning |
|---|---|---|
capability | string | a capability the registry lists (registry/capabilities.json) |
path | term | software or hardware |
decoder | string | the decoder: builtin:<crate>@<version> as jwp-receipts §2.4.12 writes one, or platform:<interface> for a decoder of the platform, <interface> 1 to 64 of a–z, 0–9 and - |
profile | term or null | the profile read: macos-task, macos-gpu, linux-rapl or linux-nvml (energy-measurement §9.2, §9.3), whose minimum windows are 1 s, 1 s, 1 s and 5 s; null only in a row absent for no_decoder |
fabrics | array | the fabrics the profile’s reading covers (energy-measurement §9.1), exactly: ["cpu"] for macos-task, linux-rapl and estimator-cpu-time/1, ["gpu"] for macos-gpu and linux-nvml; empty in an absent row |
coverage | term or null | Partial (§4.5); null in an absent row |
unit | term | image, frame, content-ms or byte |
units | integer | units per completion, at least 1 |
width, height | integer or null | the decoded picture’s size for image and frame; null for the other units |
workers | integer | the burst’s workers N, a power of two; 0 in an absent row |
status | term | figure, relative, unmetered or absent (§4.5) |
reason | term or null | why a row is unmetered or absent (§4.5, §8); null otherwise |
rounds | array | each round’s { "idle": summary, "busy": summary, "window_us": integer, "completions": integer }, in the order taken; empty in an absent row |
median | integer or null | the index in rounds of the median round (§4.5); null unless figure or relative |
range | array or null | [least, greatest] of the rounds’ figures; null unless figure or relative |
joules_micro, energy_provenance, energy_uncertainty, energy_counter_filtering, energy_calibration_ref | — | the figure per completion and its class (§2); unmetered unless figure or relative |
4.3 From readings to a round
The readings file is the JCS form (RFC 8785) of a JSON object, and readings_sha256 is
the SHA-256 of those bytes. The object is { "wai_sink_readings": 1, "rows": [ … ] }, one entry
per row of the table in the table’s order, each { "capability", "path", "decoder", "profile", "rounds": [ { "idle": [[d, t], …], "busy": [[d, t], …], "completions": integer } … ] }.
Each [d, t] is one interval of the bracket: d the counter’s advance over it, in
nanojoules, across its wrap; t its length, in microseconds, on a monotonic clock. The
busy bracket’s first read is taken as the window opens and its last as it closes, so the
window is the sum of its intervals’ t, and completions is the count of completions
between those two reads.
A round’s values follow from its intervals, with n the intervals and pᵢ = ⌊(2000·dᵢ + tᵢ) / (2·tᵢ)⌋ each interval’s power in microwatts:
mean_uw = ⌊(2·Σp + n) / (2n)⌋, the mean rounded to the nearest, a half up;sd_uwthe least integerswiths² · n · (n − 1) ≥ n·Σp² − (Σp)², the sample standard deviation rounded up (0 whenn< 2);live_intervalsthe intervals withd > 0, andlongest_live_runthe longest run of consecutive ones: an energy counter advances in an interval when its second read exceeds its first (energy-measurement §3.1);window_usthe sum of the busy intervals’t, andcompletionsas read.
A verifier holding the readings file recomputes every round and requires the table’s
values (table_readings_mismatch), and requires its SHA-256 to be readings_sha256.
4.4 The procedure wai-sink-table/1
- Survey. For each profile the platform has, read the counter at the sampling
interval for at least 2 s with nothing of the sink’s running, and record its status
under energy-measurement §9.1. A profile whose counter is not
livegives no rows butabsentones. A profile whose own sampling interval is not the table’s (linux-nvml’s is 1 s) is surveyed, and gives no rows in this table. - Work items. For each capability with a decoder in the sink, a fixed input, written by the procedure from fixed seeds or taken from a committed vector, and decoded once with its output checked against its reference before any round: a decode whose output is wrong is not timed and not metered. A completion is one decode of the whole item. The procedure text states each item, its digest, its units and its size.
- Rounds. For each round, and in each round for each row in an order rotated by one
each round: settle for 2 s, so that a processor package’s draw falls back from the burst
before it (read 200 ms after a burst, the reference Linux sink’s package still drew several
times its resting draw); read the idle bracket of the row’s
counter over the decode workers parked at their start barrier; release
Nworkers together as the window opens, and read the busy bracket while each decodes the item over and over, counting each completion; close the window; stop and join the workers. The window opens from rest (energy-measurement §2.3).Nis the sink’s worker count rounded down to a power of two (jwp-receipts §2.4.12,threads). - Record. Write every interval to the readings file, every round’s values and each row’s status, figure and class to the table (§4.5), and sign the table.
The procedure text committed with a table states the steps above as the tool ran them: its command line, the sampling interval and bracket lengths, the rounds, the work items with their digests, the workers, and the decoders’ build.
4.5 A row’s status and figure
Absent. A row is absent when there is nothing to measure: no_decoder (the sink has
no decoder for the capability on the path; profile null), no_work_item (the sink
decodes it, and the procedure defines no work item for it), no_hardware_path (the
platform’s decoder for it is not a hardware one on this sink, or its output differed from
the software path’s), or no_counter (the row’s profile is not live in the survey). An absent row has no rounds, workers 0, and the
unmetered marker; it states what was not measured, never a figure of zero.
A round’s figure. Every other row has exactly rounds rounds. Round r is the
bracket { idle, busy, window_us, completions, in_flight: workers, opening: from-rest }
of energy-measurement §6.2. It gives a figure F_r, with least declaration p_r, when
it passes, in order:
too_few_samples: each bracket has at least 2 intervals;rail_not_live: each bracket’s rail is live:sd_uwabove 0,live_intervalsat least 5,longest_live_runat least 2,5 · live_intervals ≥ 4 · n, and the bracket spans at least 300 ms (the idle bracketn · sample_us, the busy bracketwindow_us);window_short:window_usis at least the profile’s minimum window;empty_burst:completionsis at least 1;no_marginal: the busy mean is above the idle mean;below_attribution_floor:M ≥ sd_idle(energy-measurement §4);figure_zero:F_r, §6.2’s figure, is at least 1;not_declarable:F_ris at most 2⁵³ − 1 and §6.2’s leastp_rat most 2³² − 1.
The row. If any round gives no figure, the row is unmetered, and its reason is
the reason of the first round in rounds that gives none, the first rule it breaks.
Otherwise the rounds are ordered by (F_r, r) and the median is the middle one.
The row’s verdict is the median round’s (energy-measurement §6.2): clean gives figure,
usable for relative comparison gives relative, and noisy or unusable gives
unmetered with reason noisy. A figure or relative row seals:
joules_micro=F_median, andrange=[min F_r, max F_r];energy_provenanceOnChipCounterwith the profile’s filtering state, orCalibratedInstrumentwith its calibration reference where the profile reads an instrument;energy_uncertainty.window_us= the median round’swindow_us, andrelative_ppm ≥ max(p_median, q), whereqis the least integer with12 · q² · F_median² ≥ 10¹² · (max F_r − min F_r)²: the rounds’ spread read as a rectangular distribution over its range (GUM §4.3.7), relative to the figure.
A row whose max(p_median, q) exceeds 2³² − 1 cannot declare it (energy-measurement
§2.7): it is unmetered, with reason not_declarable, and like every unmetered row it
keeps its rounds and has median and range null.
A relative row’s figure is for comparing two rows of the same table measured round by
round together; a reader MUST NOT present it as an absolute figure
(energy-measurement §4; staged-measurement §8, its paragraph 8.3). An unmetered row keeps its rounds,
so the measurement that failed stays auditable.
Coverage. A figure is Whole only where its profile’s scope covers every fabric
the decode runs on. No profile of this revision does: none reads a media engine
(energy-measurement §9.2), and none reads memory. So every row that is not absent, and
every segment that is not unmetered, is Partial in this revision, and a Whole one is
refused (table_row_figure, segment_method_class); Whole is kept for a profile a
later revision registers.
4.6 Outside measurements of decode energy (informative)
The three points below are outside measurements of decode energy. Their figures are not figures of a WAI decoder or of a reference sink: no record of this extension states them, and no rule of it reads them. Each is cited by its arXiv identifier and the table it is read from, with what was measured and against what.
- A platform’s hardware decoder, against software decoders of the same format. arXiv 2402.09001, Table IV. Over AVC, VP9, HEVC and AV1, each in a random-access and a low-delay configuration, a hardware decoder spent from 0.89% to 8.86% of the energy of a software decoder of the same format, and from 4.44% to 8.86% of the energy of the optimised software decoders with vector instructions enabled. The figures are dynamic energy: the energy measured at idle is subtracted. The software decoders ran single-threaded on a desktop processor, read by the processor’s own energy counter; the hardware decoders ran on a single-board computer’s video unit, read at its power input by an external meter. The point sets two decode paths side by side as two machines built them.
- Fewer frames, chosen by the encoder. arXiv 2603.18305, Table V, its row for all sequences. Decoding energy was 17.60% lower, encoding energy 17.46% lower and the bitrate 3.38% lower, each a Bjøntegaard delta at equal matched PSNR (mPSNR, computed against the source), against encoding each sequence at its source’s 120 frames per second. A trained classifier picks, per sequence and per rate-factor setting (the table covers four), the frame rate the encoder codes at, the source’s or a lower one; where it is lower, the decoder decodes fewer frames than the source has. One software encoder and one software decoder of HEVC were measured, by the processor’s own energy counter, idle subtracted.
- Lower resolution or coarser quantisation, chosen by the encoder. arXiv 2511.00707, Table 4, its row ρ = 0.05. Decoding energy was 53.54% lower and encoding energy 51.22% lower, for an average loss of 1.68 VMAF points and 3.67 dB PSNR, against the configuration chosen with no loss allowed (ρ = 0). Per sequence, the encoder takes, of the resolutions and quantisers whose predicted VMAF is within 5% of the best predicted, the one of least predicted energy. One software encoder and one software decoder of VVC were measured on UHD sources at 60 frames per second, by a software tool reading the processor’s energy counter and the graphics processor’s; the paper does not state whether the idle energy is subtracted, nor whether Table 4’s savings are the measured or the predicted energy of the configurations chosen.
How they bear on this extension:
- Points 2 and 3 are choices of what to encode, and each costs frames or picture quality. Point 1 is a choice of decoder for the same stream. None of the three changes what a decoder computes from a given stream.
- SPEC §4’s Energy order already ranks first a classical decode that the sink’s platform answers supported, smooth and power-efficient. Point 1 measures what a hardware decode spent beside a software one; the platform’s answer stays a hint that orders, never an energy figure or evidence of hardware (SPEC §4).
- A
hardwarerow of this revision reads no media engine (§4.5, Coverage): its figure is the processors’ part of a fixed-function decode. It and point 1 measure different things, and are not set against each other as like for like.
5. The segment receipt
A segment receipt is a sink’s statement of what one segment of one stream cost it to decode: the sink-decode hop’s figure for that segment (role 3 of §6.2). A sink signs one per segment it decoded, in order, each naming the one before it. Where the sink also keeps a stream log, the log’s segment record states the same figure and class for role 3 (stream-termination §4.6), and the receipt here is where its bracket, table row or estimator inputs are.
5.1 The record
| member | type | meaning |
|---|---|---|
wai_sink_segment | integer | 1 |
signer | 32 B hex | the sink’s key |
session | 32 B hex | fresh random bytes per viewing session, derived from nothing that identifies a person, a device or an account |
stream | 32 B hex | the stream: for a track of a room, live-metering §2’s track digest; otherwise SHA-256 of the stream’s address as the sink fetched it, without query or fragment |
segment | integer | the segment’s index in this session’s reading of the stream, from 0 |
prev | 32 B hex or null | the identity (§3) of this session’s receipt for segment segment − 1 of the stream; null exactly when segment is 0 |
object | 32 B hex | the content hash (jwp-receipts §2.1) of the segment as decoded |
capability, path, decoder, unit, units, width, height | — | as a table row has them (§4.2), for this segment: units is the units the segment decoded |
media_us | integer | the segment’s content duration, in microseconds, at least 1 |
method | term | measured, table, estimator or unmetered (§5.2) |
profile | term or null | the profile the figure comes from; null exactly when unmetered |
fabrics, coverage | — | as a table row has them; empty and null exactly when unmetered |
joules_micro, energy_provenance, energy_uncertainty, energy_counter_filtering, energy_calibration_ref | — | the segment’s decode energy and its class (§2) |
bracket | object or null | measured: { "idle": summary, "busy": summary, "window_us": integer, "sample_us": integer }; otherwise null |
table | object or null | table: { "id": 32 B hex, "row": integer, "per_completion_uj": integer, "units_per_completion": integer }; otherwise null |
estimator | object or null | estimator: { "method": "estimator-cpu-time/1", "cpu_us": integer, "active_mw": integer, "basis": string, "relative_ppm": integer }; otherwise null |
reason | term or null | unmetered: why (§8, unmetered reasons); otherwise null |
5.2 The four methods
measured. The sink read its counter over the segment’s own decode, under a profile
of energy-measurement §9.2 or §9.3, and seals OnChipCounter (or
CalibratedInstrument). The busy window is bounded: it opens as the segment’s first
unit is submitted to the decoder and closes as its last is output, and no unit of another
segment is decoded inside it. The work the window paid for is then exactly the segment:
one completion, with no part-done unit at either edge, which removes the completion-count
term by construction (energy-measurement §2.3). The idle bracket is the last the sink read
under the profile with its decode parked. With M, T, n_i, n_b, s_i, s_b as in
energy-measurement §6.2, these are its §6.2 forms with Wn = Wd = 1 and N = 0:
F = ⌊(2·M·T + 10⁶) / (2·10⁶)⌋ the segment's figure, µJ
p = the least integer with p² · K ≥ P, where
K = 12 · n_i · n_b · F²
P = 12 · T² · (s_i² · n_b + s_b² · n_i) + 10¹² · n_i · n_b
The receipt is accepted when both brackets are live (as rule 2 of §4.5 reads them, the
idle span n · sample_us), window_us is at least the profile’s minimum window, M ≥ s_i, joules_micro is F and at least 1, energy_uncertainty.window_us is T, and
relative_ppm ≥ p. Below a ratio of 10 the figure is sealed and verifiable but not
publishable (energy-measurement §4): a reader derives the verdict from bracket.
table. The sink took its figure from a row of a decode table:
joules_micro = ⌊(2 · per_completion_uj · units + units_per_completion) / (2 · units_per_completion)⌋
at least 1. Its class is ModelBased when the table’s signer is the receipt’s signer
— a model calibrated on the host it describes (energy-measurement §2) — and Estimator
when it is another key’s table, a figure measured on another machine. Neither carries
evidence. A verifier holding the table requires: its identity is table.id; row
table.row exists, has status figure, and the receipt’s capability, path,
decoder, profile, unit, width, height, fabrics and coverage; per_completion_uj is the
row’s joules_micro and units_per_completion its units.
estimator. Energy-measurement §9.4’s estimator-cpu-time/1: profile names it,
joules_micro is its E over cpu_us and active_mw and at least 1, basis is
non-empty, relative_ppm is at least 1, and the class is Estimator. A reader presents
the figure with the stated uncertainty estimator.relative_ppm.
unmetered. No figure: the unmetered marker, profile, fabrics, coverage,
bracket, table and estimator empty, and reason one of §8’s unmetered reasons.
HwShunt is never a segment’s class (energy-measurement §2.6, hwshunt_forbidden).
5.3 The chain
A verifier holding a session’s receipts for one stream requires (segment_chain_broken):
the same session, stream and signer throughout; segment 0 to k with none missing or
repeated; and each prev the identity of the receipt before it. A stream whose sink
stopped decoding has a last segment; it has no receipt after it.
6. The session receipt and the per-hop split
6.1 The record
| member | type | meaning |
|---|---|---|
wai_sink_session | integer | 1 |
signer, session | 32 B hex | as in the segment receipts it closes |
streams | array | per stream, { "stream": 32 B hex, "segments": integer, "last": 32 B hex }: its segment receipts’ count and the identity of its last; at least one, in strictly ascending order of stream |
media_us | integer | the sum of the segments’ media_us |
presented_us | integer | the time the session’s content was on screen, in microseconds |
display | object or null | the display’s declaration (§6.3), or null |
hops | array | the session’s hop entries (§6.2), in strictly ascending order of role, each role at most once |
6.2 The hops, and a hop entry
The hops are the four roles of a delivery’s energy, and they are one table with a stream log’s hop entries (stream-termination §2, Hop, and §3.5): 1 origin, 2 relay, 3 sink decode, 4 display. A hop is in one of three states, by the same rule:
- a figure: an entry with a figure and its own class;
- the unmetered marker: an entry with the marker (energy-measurement §2), for a hop the sink knows took place and has no figure for; it says the hop happened and was not measured, and it is never a figure of zero joules;
- absent: no entry, for a hop the sink states nothing about. Absence is not the marker: it says nothing, and in particular not that the hop cost nothing.
| member | type | meaning |
|---|---|---|
role | integer | 1 origin, 2 relay, 3 sink decode, 4 display |
joules_micro, energy_provenance, energy_uncertainty, energy_counter_filtering, energy_calibration_ref | — | the hop’s figure and its own class (§2), or the marker |
unmetered | term or null | for the marker, why the hop has no figure: not_reported, hop_unmetered, hop_shared, unmetered or display_undeclared; null for a figure |
coverage | term or null | null for the marker; for a figure, Whole for the display (the estimate is of the whole panel’s draw), Partial for the origin and the relay (a hop’s record does not state what its figure covers), and the sink decode’s by §6.3 |
source | object or null | for the origin and the relay with a figure: the hop record the entry restates (§6.4); otherwise null |
Which hops have an entry:
- the sink decode always has one: a session’s sink decoded its segments;
- the display has one exactly when
presented_usis above 0, and none when nothing was presented; - the origin always has one: the session’s segments came from one;
- the relay has one where the sink knows its segments came through one, and none otherwise.
A figure entry’s figure is at least 1 and its class coheres with it (energy-measurement
§2); a marker entry has coverage and source null and a reason its role may have:
not_reported (the sink holds no figure for the hop), hop_unmetered (the hop’s own
record carries the marker) or hop_shared (§6.4) for the origin and the relay;
unmetered for the sink decode; display_undeclared or unmetered for the display
(split_part_invalid). Each hop carries its own class: the receipt states no total over
its hops, and a reader MUST NOT add hops of different classes into one figure, nor add
the display to any decode figure (energy-measurement §9.5).
6.3 The figure of each hop
Sink decode (3). Read from the session’s segment receipts (§5), each of which is the
sink-decode hop’s figure for its segment, the figure a stream log’s segment receipt states
for role 3 (stream-termination §4.6). With the metered segments those whose method is
not unmetered:
- every segment unmetered: the entry is the marker,
unmetered; - otherwise
joules_microis the sum of the metered segments’ figures, and the class is the weakest among theirs in energy-measurement §2’s order. Where every metered segment isCalibratedInstrumentwith one calibration reference, the entry is that class with that reference. Where every metered segment is a hardware class otherwise, the entry isOnChipCounter, its filteringOnif any segment’s isOn,Offif every metered segment isOnChipCounterwithOff, andUnknownotherwise. A hardware entry declareswindow_usthe longest of the segments’ windows andrelative_ppmat least⌈Σ(Fᵢ · pᵢ) / ΣFᵢ⌉over the metered segments’ figures and declarations: linear in the parts, so it assumes their errors fully correlated, as jwp-receipts §2.4.1 composes a group’s; coverageisWholeonly when no segment is unmetered and every metered segment isWhole.
Display (4). Energy-measurement §9.5’s estimator-display/1 over the session’s
presented_us, with the declaration
{ "method": "estimator-display/1", "panel_class": "backlit" or "emissive", "on_mode_mw": integer,
"reference_nits": integer, "nits": integer, "basis": string, "relative_ppm": integer }
on_mode_mw, reference_nits and relative_ppm at least 1, nits at least 0 and
basis non-empty. The entry is a figure with class Estimator when the declaration is
present and its E is at least 1; the marker display_undeclared when display is
null, and the marker unmetered when E is 0. A reader presents the figure with the
stated uncertainty display.relative_ppm, as an estimate, apart.
Origin (1) and relay (2). A hop’s own signed figure, where this session was the
hop’s sole user (§6.4), or the marker: not_reported when the sink holds no such figure,
hop_unmetered when the hop’s record carries the marker, hop_shared when the session
shared the hop with other sessions.
6.4 A hop’s figure, where the session was its sole user
source = { "kind": "live-metering-hop" or "other", "record": 32 B hex, "hop_joules": integer,
"hop_class": class label, "share_num": integer, "share_den": integer }
record is the hop’s record’s digest: for live-metering-hop the digest of
live-metering §3.5, and for other the SHA-256 of the record’s bytes as published.
hop_joules and hop_class are its figure and class label, hop_joules at least 1.
share_num is the busy time this session accrued on the hop and share_den the busy
time every user of the hop accrued over the hop’s span, 1 ≤ share_num ≤ share_den.
A hop’s figure is about every session it carried, and it is not divided among them by the time each kept it busy: busy time does not apportion energy (energy-measurement §9.1). The entry restates the hop’s figure only where this session was the hop’s sole user, at least 99% of its busy time:
100 · share_num ≥ 99 · share_den
joules_micro = ⌊(2 · hop_joules · share_num + share_den) / (2 · share_den)⌋
and then joules_micro is at least 1, its attribution is rail (energy-measurement
§9.1), and its class and evidence are the hop’s: energy_provenance is hop_class. A
hop this session shared below that gives no figure: its entry is the marker, with reason
hop_shared. A HwShunt hop gives
hwshunt_forbidden.
For live-metering-hop the record is a per-hop session record WMH1 (live-metering
§3.1), and share_den is the sum of its nine subscriber_us: share_num is the sink’s
statement of its own subscriber-microseconds on that hop, which the record does not hold
(live-metering §3 keeps no per-viewer entry). A verifier holding the record’s bytes
requires: they read as a per-hop record of the room their own room field names, its
signature verified (live-metering §3.6); their digest is record; its
joules is hop_joules, its class label is hop_class and its evidence the entry’s;
and share_den is its subscriber-microseconds’ sum (split_hop_mismatch). For other,
the arithmetic and the class rule are checked from the receipt alone.
6.5 Against the segment receipts
A verifier holding the session’s segment receipts requires (session_streams_mismatch)
that each verifies (§7), names this session and signer, and that per stream they form
a chain (§5.3) whose count is segments and whose last receipt’s identity is last, with
no stream left out; and that media_us is the sum of their media_us. It then requires
the sink-decode entry to be what §6.3 gives (split_decode_mismatch).
7. Verification
A verifier reads a record and checks it in the order below; the first rule broken is the refusal.
-
Shape (
record_malformed). The record is a JSON object with no duplicate member; it has the member §3 names it by, with the value 1 (JCS fixes no order of members, so “first” in §3 is the order a producer writes, not a rule); it has exactly the members its tables list, each of the type they give. Beyond the types:- a hex member has the stated length in lowercase hexadecimal digits; a term is one of the terms its table lists; an integer is from 0 to 2⁵³ − 1;
- a bracket summary has exactly its five integer members, and a round exactly its four members;
widthandheightare integers of at least 1 forimageandframe, andnullfor the other units;unitsis at least 1;decoderfollows its grammar (§4.2);- every member of a
fabricsarray is one ofcpu,gpu,mediaandnpu(energy-measurement §9.1), none twice; measuredis a dateYYYY-MM-DD;- a
source(§6.4) and adisplay(§6.3) have exactly their members, of their types; - each of
hopshas exactly the members of §6.2, itsrolefrom 1 to 4, the entries in strictly ascending order ofrole; - a
capabilityis oneregistry/capabilities.jsonlists; - a table’s
countersare in ascending order of (profile,detail), and itsmachineandbuildhave exactly their members; - a segment’s
media_usis at least 1, and itsprevisnullexactly when itssegmentis 0; - a session’s
streamsare at least one, in strictly ascending order ofstream; hop_classis a string.
-
Signature (
signature_invalid):sigover the body undersigner(§3). -
Classes. For each class the record carries, in this order — a table’s rows in order; the segment’s; a session’s hop entries in the order of their role, each entry’s class and then its
source’shop_class— three rules in turn: aHwShuntlabel is refused (hwshunt_forbidden); then a class beside ajoules_microof 0, or a non-zerojoules_microbeside no class, is refused (unmetered_as_zero); then a label energy-measurement §2 does not name, a class whose evidence members are not exactly those its §2.7 requires (as jwp-receipts §2.4.5 writes them,nullwhere a class carries none), anenergy_uncertaintywith a member of 0 or arelative_ppmabove 2³² − 1, a hardware declaration below §2.3’s resolution floor, or ahop_classthat is not a label of a class, is refused (energy_class_invalid). -
A decode table.
roundsis odd and at least 7, and each row notabsenthas exactly that many rounds (table_rounds_invalid); the rows are in strictly ascending order (table_row_order); then each row in order, by §4.5:- its status and reason (
table_row_status): an absent row has a reason of the four absent ones, no rounds,workers0,profilenullexactly forno_decoder, emptyfabrics,coverage,medianandrangenull; an absent orunmeteredrow carries the unmetered marker; any other row hasworkersa power of two and the status and reason its rounds give; and a row’s profile has an entry with statusliveincountersexactly when the row is not absent forno_counter(nullprofiles aside); - its figure (
table_row_figure):fabricsits profile’s, and forfigureandrelativerowscoveragePartial,median(an index intorounds, from 0) andrangethe ones its rounds give,joules_microthe median’s figure, the classOnChipCounterwith filteringUnknown(energy-measurement §9.2, §9.3) orCalibratedInstrument, andwindow_usthe median round’s; forunmeteredrowscoveragePartialandmedianandrangenull; - its declaration (
table_row_declaration):relative_ppm ≥ max(p_median, q);
and then, once every row has passed, holding the readings file: its SHA-256 is
readings_sha256, it has one entry per row naming the row’scapability,path,decoderandprofile, each row’s rounds as many as the table’s, and every round’s values are the ones §4.3 gives (table_readings_mismatch). - its status and reason (
-
A segment receipt.
- Its method (
segment_method_class):measuredhasbracketand notable,estimatororreason, a profile of §9.2 or §9.3 of energy-measurement, and a hardware class;tablehastableand no other, a profile of those, andModelBasedorEstimator;estimatorhasestimatorand no other, the profileestimator-cpu-time/1, andEstimator;unmeteredhas none of the three,profile,coveragenull, emptyfabrics, the unmetered marker and areasonof §8’s unmetered reasons. Every metered segment hasfabricsits profile’s andcoveragePartial, andreasonnull. measured(§5.2): both brackets live (segment_rail_not_live);window_usat least the profile’s minimum window (segment_window_short);M ≥ s_i(segment_below_floor);joules_microF, at least 1, and the declaredwindow_usT(segment_figure_mismatch);relative_ppm ≥ p(segment_declaration_below).table(§5.2): a verifier holds the table whose identity istable.idwhen one is given to it; a held table that does not pass steps 1–4 is refused here (segment_table_mismatch), andtable.rowis an index into itsrows, from 0. The figure is the formula’s and at least 1, the classModelBasedexactly when the verifier holds the table and itssigneris the receipt’s, and otherwiseEstimatorwhere it holds the table; and, holding the table, everything §5.2 requires of the row (segment_table_mismatch). A verifier that does not hold the table checks the formula only.estimator(§5.2): the figure isEover its inputs and at least 1,basisnon-empty andrelative_ppmat least 1 (segment_estimator_mismatch).- Holding the stream’s receipts: §5.3 (
segment_chain_broken), after each receipt has passed every step above.
- Its method (
-
A session receipt, in order:
- the hop entries as §6.2 reads them (
split_part_invalid): the sink decode and the origin have an entry, the display has one exactly whenpresented_usis above 0; then, in the order of their role, a figure entry hasunmeterednull, a figure of at least 1, thecoverage§6.2 gives it (for the sink decode, either of the two here, its value checked against §6.3 below) and asourceexactly when its role is the origin’s or the relay’s; a marker entry hascoverageandsourcenulland a reason its role may have; - the display declaration, where there is one, within its ranges (§6.3), and the
display entry, where there is one, against §6.3 (
split_display_mismatch); - the origin’s then the relay’s entry, where they have a figure, against §6.4: the
sole-user rule, the figure, the class and evidence, and, holding the record, the record
(
split_hop_mismatch); - holding the segment receipts (a verifier given none holds none), §6.5: each receipt
verifies (steps 1–5) and the streams agree (
session_streams_mismatch), then the sink-decode entry is what §6.3 gives (split_decode_mismatch).
- the hop entries as §6.2 reads them (
An accepted record establishes that its signer stated it. The arithmetic between its values is checked, and so are its readings, its table and its hop records where the verifier holds them. The values themselves — the counter readings, the completions, the declared inputs of an estimate — are the signer’s statements (energy-measurement §5): a record that invents every value consistently passes every check.
8. Reasons
| reason | refused when |
|---|---|
record_malformed | a record does not read in its shape (§3, §7 step 1) |
signature_invalid | its signature does not verify (§3) |
hwshunt_forbidden | a class, or a hop’s class, is HwShunt (energy-measurement §2.6) |
energy_class_invalid | a class label is unknown, or its evidence is missing, extra or below the resolution floor |
unmetered_as_zero | a class sits beside a zero figure, or a non-zero figure beside no class |
table_rounds_invalid | rounds is even or below 7, or a row has another count of rounds |
table_row_order | the rows are not in strictly ascending order |
table_row_status | a row’s status or reason is not the one its rounds give (§4.5) |
table_row_figure | a row’s median, range, figure, class or window is not the one its rounds give |
table_row_declaration | a row declares less than max(p_median, q) |
table_readings_mismatch | the readings file’s digest, or a round recomputed from it, differs |
segment_method_class | a segment’s members or class do not fit its method |
segment_rail_not_live | a measured segment’s bracket is not live |
segment_window_short | a measured segment’s window is below its profile’s minimum |
segment_below_floor | a measured segment’s marginal is below the idle deviation |
segment_figure_mismatch | a measured segment’s figure is not its bracket’s F, or its window not T |
segment_declaration_below | a measured segment declares less than its bracket’s p |
segment_table_mismatch | a table segment’s figure, class or row disagrees with its table |
segment_estimator_mismatch | an estimated segment’s figure or inputs are not the method’s |
segment_chain_broken | a stream’s receipts do not form its chain |
split_part_invalid | a hop entry is missing where §6.2 requires one, or is neither a figure nor a marker with its reason |
split_display_mismatch | the display entry is not the estimator’s over the declaration |
split_hop_mismatch | an origin or relay entry’s busy time, class or record disagrees |
split_decode_mismatch | the sink-decode entry is not what the segment receipts give |
session_streams_mismatch | the streams, counts, last receipts or media_us disagree with the segment receipts |
The row reasons a table row states are §4.5’s: no_decoder, no_work_item,
no_hardware_path, no_counter, the round rules too_few_samples, rail_not_live, window_short,
empty_burst, no_marginal, below_attribution_floor, figure_zero and
not_declarable, and noisy. The unmetered reasons a segment states are
no_counter, rail_not_live, window_short, no_marginal,
below_attribution_floor, figure_zero, not_declarable and not_reported.
9. Conformance
- A producer of a decode table publishes the table, its readings file and its procedure text together, and a table whose readings are not published MUST NOT be presented as measured.
- A sink that states a segment’s decode energy signs a segment receipt for every segment it decoded on the stream, unmetered ones included, so a gap shows in the chain.
- The corpus holds decode tables, segment chains and session receipts, accepted and refused, each refusal under its reason; every reason of §8 refuses at least one vector. It also holds the measured tables of the reference sinks, each with its readings and its procedure, which both implementations verify.
- Continuous integration runs the integer checks on every change, and a hardware job on the runners whose label names a reference sink’s platform. Where the job’s counter is not readable on its runner, the job reports the measurement as skipped, with the counter’s status, and never as passed.
10. Security and privacy considerations
- A decode table names its machine (operating system, architecture, processor, cores and memory) and publishes its power levels and its build in cleartext: it is about a reference machine, and its signer chooses to publish that. A sink that measures its own table and keeps it is not required to publish it.
- A segment receipt names no person, device or account: its
sessionis random per viewing session. Itsstreamdigest andobjecthash name what was decoded, and its timings and energy show how the sink decoded it; a sink discloses them to whoever it gives the receipt. - Energy readings are a side channel (energy-measurement §2.4). The profiles read counters at 200 ms or slower, the records carry bracket statistics rather than single instants, and a reader MUST NOT take a sink’s receipts as permission to read its counters.