WAI Extension: Energy-Aware Content Steering
Mirrored from the canonical text at commit f871af55 ().
Status: Draft. Registers the record
wai.live.steering: a steering server’s choice, for one client, of the pathway that delivers a room’s stream for the fewest joules within the operator’s quality and latency limits, decided from the per-hop session records (live-metering §3,WMH1) that each pathway’s hops signed, and signed so that anyone holding those records can recompute it. The operator signs a steering policy,WSP1: the pathways, their hops, their limits and the window the evidence is read over. For each request the steering server derives each pathway’s evidence, its joules per delivered byte (or per delivered second at the floor tier) with the acquisition class of energy-measurement §2, applies one integer decision rule, and signs the steering decision,WSD1, which names the evidence records by digest, the client’s report, the server’s own inputs and the output. A figure is compared only with figures of its class; a pathway whose energy is not known, because a hop is unmetered, its classes differ or its evidence is stale, is never ranked above one whose energy is, so that not measuring is never rewarded. The output is the steering manifest that HLS and DASH players already read, written byte for byte, carrying the decision’s URI in one key that players ignore. No new transport. The reference implementation is theenergy_steeringmodule ofwai-rs(featureenergy_steering); the corpus islive-conformance/steering/, and its second implementation islive-conformance/energy_steering_verify.py. Keywords MUST, MUST NOT, SHOULD, MAY are RFC 2119/8174.Texts read for this revision:
draft-pantos-hls-rfc8216bis-22(§4.4.6.6, theEXT-X-CONTENT-STEERINGtag, and §7, content steering in HLS) anddraft-pantos-content-steering-04(§2 to §7: pathway IDs, the steering manifest, pathway cloning, query parameters and client behaviour), Internet-Drafts not past Working Group Last Call; ETSI TS 103 998 V1.1.1 (2024-01), content steering for DASH (clauses 5 to 8); CTA-5004-B (the CMCD keysblandmtp, and the query and header modes); RFC 9651 §4.2; RFC 3986 §2; RFC 8259; RFC 8032; FIPS 180-4. A section number of one of those drafts in this text is that revision’s.
1. Scope
In scope: the steering policy, its pathways and limits, and who signs it (§3); what a steering server reads from a client’s steering request (§4); a pathway’s evidence and its figure (§5); the decision rule (§6); the steering decision record and who signs it (§7); the steering manifest, the HLS tag and the DASH element, written exactly, and how the record rides beside them (§8); verification (§9).
Out of scope: a transport, and a steering server’s network service, which this extension describes only through its inputs and its output; how a hop measures its energy, which energy-measurement governs; how an operator chooses its limits; and how a player applies a steering manifest, which the three texts above fix and this extension does not change.
Relationship to the core spec: wai.live.steering is a record (SPEC §5):
a signed record a WAI tool emits and verifies. It names no payload format and is not
dispatchable.
2. Terms
- Room, room clock, room key, hop, per-hop session record, unmetered marker: as in live-rooms §2 and live-metering §2. A record’s digest is live-metering §3.5’s.
- Pathway, pathway ID, pathway clone, base pathway, steering manifest,
steering server: as in
draft-pantos-content-steering-04§2. A pathway is an HLS pathway (rfc8216bis-22 §7.1) and a DASH service location (ETSI TS 103 998 clause 5.1). - Content description: the HLS multivariant playlist or the DASH MPD.
- Edge hop of a pathway: a hop whose sessions are clients of the stream; interior hop: a hop of the pathway that feeds other hops.
- Tier: a quality level, numbered from 0, the lowest. Tier i is the room’s layer class i (live-metering §3.3), and the policy states its bitrate.
- Class: an acquisition class of energy-measurement §2, by its tag (§2.7): 2
ModelBased, 3Estimator, 4OnChipCounter, 5CalibratedInstrument. - u128, u64, u32, u16, u8: little-endian unsigned integers of that many bits. A digest is SHA-256 (FIPS 180-4), 32 bytes. ppm: parts per million.
- Every quantity in this text is an integer and every operation is exact: a product is formed in full, never rounded or saturated, and a quotient is named with its rounding.
3. The steering policy
3.1 Layout
The operator signs one policy for a room’s steering, WSP1:
| field | size | meaning |
|---|---|---|
magic | 4 | WSP1 (ASCII) |
version | 1 | 1 in this revision |
room | 32 | the room id whose per-hop records are the evidence |
signer | 32 | the key that signs the policy (§3.5) |
period_us | 8 | u64: P, the period on which the pathways’ hops cut their records (§5.1) |
window_us | 8 | u64: W, the evidence window, a whole number of periods |
lag_us | 8 | u64: L, how long after a period ends its records are first required |
basis | 1 | 1: joules per delivered byte; 2: joules per delivered second at or above the floor tier (§5.3) |
floor | 1 | F: the floor tier |
latency_ceiling_ms | 4 | u32: the latency ceiling |
min_buffer_ms | 4 | u32: below this buffer a client is not moved (§6.4) |
hysteresis_ppm | 4 | u32: h, the margin by which a pathway must beat a client’s current one (§6.4) |
ttl_s | 4 | u32: the steering manifest’s TTL |
min_ttl_s | 4 | u32: the TTL when the decision holds a client or finds no pathway within the limits |
flags | 1 | bit 0: DASH queryBeforeStart; every other bit 0 |
initial | 1 | the index of the initial pathway of the content description |
tiers | 1 + 8 × n | a u8 count n, then each tier’s bitrate in bits per second (u64), tier 0 first |
server_uri | 2 + … | a u16 length, then the steering server’s URI, which the content description names |
reload_uri | 2 + … | a u16 length, then the URI the manifest names for the next request (§8.1) |
decision_uri | 2 + … | a u16 length, then the prefix of the URI at which a decision record is served (§8.4) |
pathways | 1 + … | a u8 count, then each pathway entry (§3.2), in the policy’s order |
sig | 64 | §3.4 |
3.2 Pathway entries
| field | size | meaning |
|---|---|---|
id | 1 + … | a u8 length, then the pathway ID |
base | 1 | 255: a pathway of the content description; otherwise the index of the pathway it clones |
host | 1 + … | a u8 length, then the clone’s HOST (§8.1); length 0 for none |
params | 1 + … | a u8 count, then each of the clone’s PARAMS: a u8 length and the name, a u8 length and the value |
top_tier | 1 | the highest tier the pathway carries |
latency_ms | 4 | u32: the latency the operator states for the pathway |
capacity | 4 | u32: the most sessions the server steers to it first at once; 2^32 − 1 for no limit |
min_share_ppm | 4 | u32: the share of sessions it is placed first for whatever its evidence (§6.3) |
hops | 1 + 33 × k | a u8 count k, then each hop: its key (32) and its place (u8: 1 edge, 2 interior) |
The pathways that are not clones are the content description’s: each is the PATHWAY-ID of
its HLS variant streams (rfc8216bis-22 §4.4.6.2) and the @serviceLocation of its DASH
BaseURL elements (ETSI TS 103 998 clause 5.2). A clone is a pathway of its own, with its
own hops: the same content reached through another host.
3.3 Reading and rules
A reader reads exactly the bytes it is given, in the order of §3.1, as live-events §3.3
reads a record, with WSP1 for WEV1 (truncated, malformed, unknown_version). It
refuses as clone_unknown a policy with a clone whose base is not the index of a pathway
before it: a clone of a pathway the manifest would not yet define, which
draft-pantos-content-steering-04 §5 has a client ignore. It refuses as policy_invalid a
policy that breaks any of these:
period_usis above 0, andwindow_usis a multiple of it above 0;basisis 1 or 2; 1 ≤min_ttl_s≤ttl_s;hysteresis_ppm≤ 1 000 000;flagsis 0 or 1;- 1 ≤ n ≤ 8; the bitrates are above 0 and strictly ascending;
floor< n; - each URI is 1 to 2048 bytes of RFC 3986’s characters (unreserved, reserved and
%, never#), and each%begins a percent-encoding of two hexadecimal digits; - 1 to 32 pathways; each
idis 1 to 64 bytes ofA–Z,a–z,0–9,.,-and_(content-steering-04 §2), and no two are equal; initialis the index of a pathway that is not a clone;- a pathway that is not a clone has no
hostand noparams; a clone’shostis at most 255 bytes ofA–Z,a–z,0–9,-and.; itsparamshave names of 1 to 64 bytes and values of 0 to 255 bytes, each of RFC 3986’s unreserved characters and percent-encodings, and names in strictly ascending bytewise order (content-steering-04 §5 appends them in UTF-8 order, which is bytewise for these characters); - each pathway’s
top_tier< n; each has at least one hop and at least one edge hop; each place is 1 or 2; no hop key appears twice in the policy, in one pathway or in two; - the sum of the
min_share_ppmis at most 1 000 000.
A hop belongs to one pathway. A hop that carries two pathways’ traffic signs one figure for both, and no record states how it divides; a share of it would be an allocation, not a reading. A hop common to every pathway, such as the origin that feeds them all, changes no comparison and is listed in none.
3.4 Digest and signature
The digest of a policy is SHA-256 over its bytes before sig. Its signature is
Ed25519 (RFC 8032) by signer over the 20 bytes wai:steering-policy (ASCII) and 0x01,
followed by the bytes the digest covers, verified strictly as SPEC §7
Signatures requires: a key that fails step 1 or 2 is bad_key, a signature that fails a
later step bad_signature.
3.5 Who signs: the steering authority
A policy and a decision are signed by the room authority, or by a steering key: a key
the room’s origin’s key log lists, not revoked, with the authority live/steer in its
may_sign (jwp-receipts §7.5). A decision restates figures with their classes, so its
signer’s entry, the room authority’s included, MUST also hold energy/ followed by the
label of each class among its figures (§7.1), as jwp-receipts §7.5 requires of every signer
that restates a figure with its class. A room that names no origin has no key log, and only
its authority signs. A signer that is neither is signer_not_authorised, and a steering key
whose entry is revoked is signer_revoked (the codes of live-metering §4.3).
Steering is the operator’s act, as metering is, and its key lives in the origin’s key log for the reason live-metering §4.3 gives: a decision moves every client of the room, and a grant’s powers are live-rooms’ closed set.
4. The client’s report
A steering request is an HTTP GET of the steering server’s URI, or of the last manifest’s
RELOAD-URI. The server reads three things from it, each absent when the request does
not carry it in the form below; a value that does not read is absent, never zero:
- the client’s current pathway, c: the index of a pathway of the policy;
- its throughput, T: bits per second;
- its buffer, B: milliseconds.
4.1 The query
The query is the request target’s bytes after its first ? and before any #. It is cut
at each & into parts; a part is cut at its first = into a name and a value (a part with
no = has an empty value), and both are percent-decoded (RFC 3986 §2.1; + is not a
space). A part with a % not followed by two hexadecimal digits is skipped. Of the parts
with a name the server reads, the first is read and later ones are ignored. A value is
unquoted by removing one " at each end when it both begins and ends with one.
An integer here is 1 to 20 ASCII digits whose value is at most 2^64 − 1.
4.2 HLS
rfc8216bis-22 §7.4 has the client add _HLS_pathway="<CURRENT-PATHWAY-ID>" and
_HLS_throughput=<THROUGHPUT>, the throughput an integer number of bits per second:
- c:
_HLS_pathway, unquoted, when it is theidof a pathway of the policy; - T:
_HLS_throughput, when it is an integer.
4.3 DASH
ETSI TS 103 998 clause 7 step 7 has the client add _DASH_pathway, the service locations it
used since its last request, comma-separated within one pair of double quotes, and
_DASH_throughput, a comma-separated list of integer bits per second, the n-th for the
n-th service location:
- c: of
_DASH_pathway, unquoted and cut at each,, the first item that is theidof a pathway of the policy, at position j of the list (from 0); - T: of
_DASH_throughput, cut at each,, the item at position j, or at 0 when no pathway is current, when it is an integer.
The clause’s own example writes _DASH_pathway without quotes, so both are read.
4.4 CMCD
A client that reports CMCD (CTA-5004-B) sends its keys as the query parameter CMCD, or in
the header shards; CTA-5004-B assigns bl (buffer length, milliseconds) and mtp
(measured throughput, kilobits per second) to the shard CMCD-Request. The server reads
the CMCD parameter’s value or, when the query has no such part, the CMCD-Request
header’s value, as an RFC 9651 Dictionary (§4.2.2 of that RFC, the whole grammar, Dates and
Display Strings included, and a Byte Sequence whose content is not base64 of RFC 4648 §4, its
padding supplied where it is missing, refused); a value that does not parse gives neither
key. A key read is:
- an Integer of 0 or above; or a non-empty Inner List of such Integers, each with any parameters (CTA-5004-B carries one per object type, its token a parameter): the least of them, the object type whose buffer runs out first, or the slowest measurement;
- anything else, absent.
Then B is bl, and when §4.2 or §4.3 gives no T, T is mtp × 1000.
5. A pathway’s evidence
5.1 The window
A pathway’s hops cut their per-hop records (live-metering §3.8) on the policy’s period: each record’s span runs from one multiple of P on the room clock to a later one. At a decision’s instant t on the room clock the window is [a, b), with
b = ⌊(t − L) / P⌋ × P (0 when t < L)
a = b − W (0 when b < W)
so that the window ends on the last period that has been over for at least L: L gives a
hop’s record of a period time to reach the steering server before the server needs it. A
record is in the window when a ≤ its start and its end ≤ b.
5.2 Coverage and staleness
A decision names, for each pathway, the records of its hops it read, grouped by hop in the
pathway’s hop order and each hop’s in ascending order of start (§7.1). A hop is
covered when the records named for it tile the window: there is at least one, the
first starts at a, each starts where the one before it ends, and the last ends at b. The
window is then empty for no hop: when a = b no hop is covered.
A pathway’s evidence is stale when one of its hops is not covered: a period the hop has not signed, or whose record has not reached the server within L; a gap; an overlap; a record that does not reach to b. Its energy over the window is then not known, and §6 ranks it so. Because the window is tiled exactly, every hop of every pathway is read over the same span, so the figures of two pathways describe the same stretch of room clock; and a server that leaves out a record makes a pathway stale. It cannot make one look cheaper.
5.3 The figure
Over the records named for a pathway, its status is the first of these that holds:
- stale (3): a hop is not covered (§5.2);
- unmetered (1): a record’s figure is the unmetered marker;
- mixed (2): the records’ classes are not all one;
- empty (4): D = 0;
- figure (0): otherwise.
Its joules J is the sum of the records’ joules, in microjoules, and its delivery
D is, over the records of its edge hops:
- with
basis1, the sum of thebytesof every track entry of kind 1 (a media track) and direction 2 (out): the media bytes the pathway delivered; - with
basis2, the sum ofsubscriber_us[c] for the layer classes c from F to 7: the microseconds of delivery at the floor tier or above.
A pathway of status figure has the figure J / D, in the records’ common class; a
figure is never written as a quotient, and two figures are compared by their products (§6.2).
On basis 2 the pathway’s every joule is charged to its delivery at the floor tier or above,
so the figure is never less than the joules per such second that any division of the hops’
joules among tiers would give.
5.4 What is not known is not zero
A pathway of any status other than figure has no figure, and its J and D are stated as 0 (§7.2). In particular:
- An unmetered hop is not a hop of zero joules. A sum over a pathway’s records that
leaves out an unmetered record would state less energy than the pathway spent, and the
less a pathway measured the cheaper it would look. A decision that gives a pathway with an
unmetered record a figure is refused
unmetered_as_zero. - Figures of two classes are not summed. A
ModelBasedfigure added to anOnChipCounterfigure is neither, and labelled as either it would present a model as a reading or a reading as a model (live-metering §4.5). A decision that gives a pathway of mixed classes a figure is refusedclasses_summed. - Stale evidence is not current evidence, and a pathway whose hops delivered nothing (empty) has no rate to state.
6. The decision rule
The rule is a function of the policy; the evidence (§5); the client’s report (§4); the
client’s session, 16 bytes the server minted at the client’s first request and carries
in the manifest’s RELOAD-URI (§8.1); and each pathway’s load, the number of sessions
the server is steering to it first, its own count. Its output is the order of the pathways,
a TTL and the reason the first was chosen.
6.1 Limits
A pathway p is within the limits for the request when all three hold:
- latency: its
latency_ms≤latency_ceiling_ms; - quality: its
top_tier≥ F: it carries the floor tier; - capacity: its
capacityis 2^32 − 1; or its load <capacity; or it is c and its load ≤capacity, since c’s load already counts the client.
6.2 The order
A pathway’s rank is set by its evidence: 0 for a figure of class 5
(CalibratedInstrument), 1 of class 4 (OnChipCounter), 2 of class 2 (ModelBased), 3 of
class 3 (Estimator), and 4 for no figure. Pathway p comes before q when, of these, the
first that tells them apart says so:
- p’s rank is below q’s;
- both have a figure, of one class, and Jp × Dq < Jq × Dp: p’s figure is the lower;
- p’s
idis below q’s, bytewise.
Every two pathways differ in id, so the order is total. Figures are compared only within a
class (step 2). Across classes the order is by what backs the figure, the order
energy-measurement §2 gives its classes, and never by the figures: no figure of one class
can be compared with one of another, so the rule cannot tell which of two such pathways
spends less, and it prefers the evidence it can rely on. A pathway with no figure comes after
every pathway with one (step 1), and among pathways with no figure only the id orders them
(step 3): being unknown cannot raise a pathway above any pathway whose energy is known, and
nothing about an unknown pathway’s energy orders it.
6.3 The draw and the minimum shares
The client’s draw is
d = u32(SHA-256("wai:steering-draw" ‖ 0x01 ‖ policy digest ‖ session)[0..4]) mod 1 000 000
the first four bytes read as a u32. It is fixed for the session, so it moves no client from
one decision to the next. The policy’s pathways, in its order, divide [0, 1 000 000) from 0
into bands: pathway i’s band is [Si, Si +
min_share_ppmi), with Si the sum of the shares before it. A
session whose draw falls in a pathway’s band is placed on that pathway first while it is
within the limits (§6.4 step 4), whatever its evidence.
The minimum shares are how evidence stays fresh: a pathway no client is steered to delivers
nothing, and its records soon state no delivery, or none at all. A pathway’s share of
sessions from its band is its min_share_ppm, the same whether its energy is known or not,
so a pathway gains no session by being unknown.
6.4 The first pathway
The client’s sustainable tier is, when T is known, the highest tier whose bitrate is at most T, or none when tier 0’s is above it. The client is at its floor when T is known and its sustainable tier is none or at most F.
Let w be the first pathway of the order (§6.2) that is within the limits. w beats a pathway x of its rank when both have a figure and
J_w × D_x × (1 000 000 + h) < J_x × D_w × 1 000 000
that is, w’s figure is below x’s by more than the margin h. The first pathway, and the reason it is first, is given by the first of these steps that applies:
- buffer (1): c is known and within the limits, B is known, and B <
min_buffer_ms: c. A switch costs a fresh start on the new pathway, which a short buffer may not cover. - floor (2): c is known and within the limits, and the client is at its floor: c. Its throughput was measured on c, and a client with no margin above the floor is not moved to a pathway on which it was never measured.
- none (6): no pathway is within the limits: c when it is known, otherwise the first pathway of the order. The client is not moved for a limit no pathway meets (content-steering-04 §7 step 5 has it stay).
- share (3): the draw falls in the band of a pathway e within the limits: e.
- hold (4): c is known and within the limits, c is not w, c has w’s rank, and either that rank is 4 or w does not beat c: c. This is the hysteresis: a client moves only for a figure lower than its own by more than the margin, so that two figures within the margin of each other do not swap its pathway from one decision to the next. Between two pathways with no figure nothing says that either is cheaper, and the client stays.
- rank (5): w.
Steps 1, 2 and 4 hold a client or place it whatever its pathway’s evidence, and so do not prefer a pathway for being unknown; step 5 holds a client only against a pathway of its own rank, so a client on a pathway with no figure moves as soon as a pathway with a figure is within the limits.
6.5 The priority and the TTL
The priority is the first pathway, then the pathways within the limits in the order (§6.2), then the others in the order, each once. Every pathway is in it, so a client that penalises its first pathway (content-steering-04 §7 step 3) falls back in the same order.
The TTL is min_ttl_s when the reason is buffer, floor or none, so that a client held
or left without a pathway within the limits asks again soon; and ttl_s otherwise.
7. The steering decision
7.1 Layout
For each request the steering server signs a decision, WSD1:
| field | size | meaning |
|---|---|---|
magic | 4 | WSD1 (ASCII) |
version | 1 | 1 in this revision |
room | 32 | the room id |
signer | 32 | the key that signs the decision (§3.5) |
policy | 32 | the policy’s digest |
t | 8 | u64: the decision’s instant on the room clock, in microseconds |
session | 16 | the client’s session (§6) |
draw | 4 | u32: d (§6.3) |
protocol | 1 | 1 HLS (§4.2), 2 DASH (§4.3): which parameters the report was read from |
report | 18 | present (u8: bit 0 c, bit 1 T, bit 2 B), pathway (u8: c), throughput (u64: T), buffer (u64: B); an absent value is 0 |
pathways | 1 + … | a u8 count, the policy’s, then for each pathway in the policy’s order: load (u32); limits (u8: bit 0 latency, bit 1 quality, bit 2 capacity, each set when it holds, §6.1); status (u8, §5.3); class (u8: the figure’s tag, 0 for no figure); joules (u128: J); delivery (u128: D); evidence, a u8 count and the digest of each record named (§5.2) |
ttl | 4 | u32: the TTL (§6.5) |
reason | 1 | the first pathway’s reason (§6.4): 1 buffer, 2 floor, 3 share, 4 hold, 5 rank, 6 none |
priority | 1 + m | a u8 count m, then the index of each pathway, the first first (§6.5) |
sig | 64 | §7.3 |
7.2 Reading and rules
A reader reads the bytes as §3.3 reads a policy, with WSD1 for WSP1, and refuses as
decision_invalid a decision that breaks any of these:
protocolis 1 or 2;draw< 1 000 000;ttl≥ 1;reasonis 1 to 6;presenthas no bit above 2; an absent value is 0; a presentpathwayis below the count ofpathways;- each pathway’s
limitshas no bit above 2 and itsstatusis 0 to 4; with status 0 itsclassis 2 to 5 and itsjoulesanddeliveryare above 0; with any other status itsclass,joulesanddeliveryare 0; - no digest is named twice in the decision;
prioritynames each pathway once: m is the count ofpathwaysand its indices are a permutation of 0 to m − 1.
7.3 Digest and signature
The digest of a decision is SHA-256 over its bytes before sig; its signature is
Ed25519 by signer over the 22 bytes wai:steering-decision (ASCII) and 0x01, followed by
the bytes the digest covers, verified strictly (SPEC §7 Signatures). Who may sign it is
§3.5’s rule.
8. The mappings
8.1 The steering manifest
The output of a decision is one steering manifest (draft-pantos-content-steering-04 §4,
the DASH Content Steering Manifest of ETSI TS 103 998 clause 6), served with the media type
§11.1 of that draft registers. It is the JSON text (RFC 8259) below, UTF-8, with no white
space outside its strings, its members in this order:
{"VERSION":1,"TTL":<ttl>,"RELOAD-URI":"<reload>","PATHWAY-PRIORITY":[<ids>],
"PATHWAY-CLONES":[<clones>],"science.transaction.wai-decision":"<decision>"}
(one line, cut here for legibility), where:
<ttl>is the decision’sttlin decimal digits, with no leading zero;<reload>is the policy’sreload_uri, then&when it holds a?and?otherwise, thenwai_session=and the session as 32 lowercase hexadecimal digits;<ids>is theidof each pathway of thepriority, in its order, each in double quotes, separated by,;"PATHWAY-CLONES":[<clones>],is written only when the policy has a clone.<clones>is, for each clone in the policy’s order, separated by,:{"BASE-ID":"<base id>","ID":"<id>","URI-REPLACEMENT":{<replacement>}}, where<replacement>is"HOST":"<host>"when the clone has a host, then (after a,when a host was written)"PARAMS":{<params>}when it has params,<params>being"<name>":"<value>"for each, in the policy’s order, separated by,; and empty when it has neither;<decision>is the policy’sdecision_uri, then the decision’s digest as 64 lowercase hexadecimal digits (§8.4).
§3.3’s character rules leave nothing in these strings that JSON escapes, so the text is
fixed byte for byte. PATHWAY-CLONES is written for every clone, whether or not it is
first, so that a client can fall back to any pathway of the priority.
HLS and DASH are the same bytes. ETSI TS 103 998 clause 6.2 defines the same keys as
content-steering-04 §4 and §5, and a DASH client ignores the URI-REPLACEMENT keys HLS
adds (PER-VARIANT-URIS, PER-RENDITION-URIS, rfc8216bis-22 §7.3; clause 7 step 13),
which a policy of this revision does not state. A DASH PATHWAY-PRIORITY names service
locations, which are the same IDs (§3.2). The two protocols differ in the request (§4) and
in the content description (§8.2, §8.3).
8.2 HLS
The multivariant playlist carries the policy as one tag (rfc8216bis-22 §4.4.6.6):
#EXT-X-CONTENT-STEERING:SERVER-URI="<server_uri>",PATHWAY-ID="<initial id>"
with the policy’s server_uri and the id of its initial pathway. The tag’s
PATHWAY-ID is the pathway a client applies until its first steering manifest, and
content-steering-04 §4 asks that the first manifest’s first pathway agree with it: a client
whose first request carries no report and whose draw is in no band gets the first pathway of
the order, which an operator SHOULD make the initial one when it writes the policy.
8.3 DASH
The MPD carries the policy as one element (ETSI TS 103 998 clause 5.2):
<ContentSteering defaultServiceLocation="<initial id>"
queryBeforeStart="<true|false>"><server_uri></ContentSteering>
(one line), queryBeforeStart true when the policy’s flag bit 0 is set and false
otherwise, and the URI with each & written &, the only character of §3.3’s URIs that
XML text escapes. With queryBeforeStart true a client asks before it starts (clause 7
step 5), and its first request carries no _DASH_ parameters, so its first decision has no
report.
8.4 Carrying the decision
The decision record is served at the URI the manifest’s science.transaction.wai-decision
member names: the policy’s decision_uri followed by the decision’s digest in hexadecimal.
A player ignores the member: content-steering-04 §4 lets a manifest hold other keys and has
a client ignore any key it does not recognise, and ETSI TS 103 998 clause 6.1 does the same.
The member is the decision’s digest, not its signature over the manifest: the manifest is a
function of the decision and the policy (§8.1), and its member names the decision, so there
is no loop between them. A party that holds the manifest fetches the record, verifies it
(§9), and requires that the manifest be the bytes §8.1 writes from it. A steering server
SHOULD keep each decision available for as long as the records it names are.
9. Verification
9.1 Inputs and steps
A verifier is given the room’s descriptor and the head of its authority’s key log, or none; the policy; the decision; the per-hop records; and, optionally, the manifest that was served with the decision. It runs these steps and stops at the first refusal, which it reports with the reason and where:
- The room, at
room: the descriptor reads and verifies, and the head is held for its origin and lists its authority (live-rooms §9.1 steps 1 and 2, with that section’s reasons). - The policy (§3), at
policy:truncated,malformed,unknown_version,clone_unknown,policy_invalid;wrong_roomwhen itsroomis not the room id;bad_key,bad_signature; its signer (§3.5),signer_not_authorised,signer_revoked. - The decision (§7), at
decision:truncated,malformed,unknown_version,decision_invalid;wrong_room;record_missingwhen itspolicyis not the given policy’s digest;decision_invalidwhen its count ofpathwaysis not the policy’s, or a presentpathwayis not a pathway of it;bad_key,bad_signature; its signer,signer_not_authorised,signer_revoked. - Each record it names, in order, at
evidencewith the pathway’s index and the record’s place in its list:- a record whose digest is the one named is given: otherwise
record_missing; - it reads, its room is the room id and its signature verifies (live-metering §3.6:
truncated,malformed,unknown_version,energy_invalid,record_invalid,wrong_room,bad_key,bad_signature); - its hop may sign it (live-metering §5.2:
hop_not_authorised,hop_revoked): a key the room’s key log does not list for its role and class does not sign evidence; - its
hopis a hop of the pathway it is named under: otherwiseevidence_not_on_pathway; - it is in the window of the decision’s
t(§5.1): otherwiseevidence_outside_window; - it follows the record named before it under the pathway in the order of §5.2, a later
hop of the pathway or the same hop and a later
start: otherwisedecision_invalid.
- a record whose digest is the one named is given: otherwise
- The evidence (§5), at
pathwayand its index: each pathway’s status, class, J and D recompute from its records. A pathway stated with a figure whose records hold the unmetered marker isunmetered_as_zero; one stated with a figure whose records are of more than one class isclasses_summed; any other difference isevidence_mismatch. - The output (§6), at
output: the draw, each pathway’slimits, the priority, the TTL and the reason recompute from the policy, the evidence, the report, the session and the loads. When they do not, the decision isunknown_preferredif its first pathway has no figure while the first of the recomputed priority has one, anddecision_mismatchotherwise. - The manifest, at
manifest, when one is given: it is the bytes §8.1 writes from the decision and the policy, otherwisemanifest_mismatch.
A decision that passes is accepted: the priority, the TTL and the reason are what the rule gives for the evidence the records state, the report and the loads it names.
9.2 What is recomputed and what is stated
A decision has inputs of three kinds, and a verifier treats them differently:
- The evidence is signed by the hops and verified: its figures cannot be other than the records give.
- The report is the client’s, as the server read it (§4). The client does not sign it, and the decision states it rather than the request, so that a request’s other parameters (tokens, the session) are not published with it.
- The session and the loads are the server’s own. The decision states them; a verifier recomputes the rule from them and the draw from the session, and an auditor that holds a server’s decisions checks over them that the loads and the bands’ shares are what the server claims.
9.3 Outcome
- refuse: the reason and where (§9.1).
- accept: the decision’s digest; the priority by pathway ID; the TTL and the reason; each pathway’s status, class, J, D and limits; the steering manifest; and the record’s URI.
10. Reasons
The codes this extension adds, each with where it is raised:
| reason | raised at |
|---|---|
policy_invalid | §3.3, §9.1 step 2 |
clone_unknown | §3.3, §9.1 step 2 |
decision_invalid | §7.2, §9.1 steps 3 and 4 |
evidence_not_on_pathway | §9.1 step 4 |
evidence_outside_window | §5.1, §9.1 step 4 |
evidence_mismatch | §5.3, §9.1 step 5 |
unmetered_as_zero | §5.4, §9.1 step 5 |
unknown_preferred | §6.2, §9.1 step 6 |
decision_mismatch | §6, §9.1 step 6 |
manifest_mismatch | §8.1, §9.1 step 7 |
A verifier of this extension also raises truncated, malformed and unknown_version for a
policy, a decision or a record that does not read; bad_key and bad_signature for their
signatures; wrong_room for one of another room; live-metering’s classes_summed,
signer_not_authorised, signer_revoked, record_missing, energy_invalid,
record_invalid, hop_not_authorised and hop_revoked; and live-rooms §9.1’s codes for
the room, at room.
11. Conformance
live-conformance/steering/ holds the corpus, in one file,
vectors.json, written from fixed seeds by wai_energy_steering_vectors gen. It sits beside
the metering corpus because its evidence is per-hop records of live-metering, read by that
corpus’s second implementation. It holds a room with its key log, policies, the per-hop
records of the pathways’ hops, and:
- requests: steering requests over HLS and DASH, with and without CMCD in the query and in the header, with the report §4 reads from each;
- vectors, each a policy, a decision, the records it names and the manifest served with it, with the outcome §9.3 gives them. Accepted: three pathways with figures of one class; mixed classes, ordered by class; a pathway with an unmetered hop, ranked after those with figures though its metered hops alone are the cheapest; stale evidence; a client at its floor, and one with a short buffer, held; hysteresis holding a client and, a period later, moving it; pathway clones; a session placed by its band; a pathway at its capacity and one past the latency ceiling; the per-second basis; DASH; and no pathway within the limits; each with its steering manifest, HLS tag and DASH element byte for byte. Two more show what has no figure: hops of two classes in one pathway, and, beside the session its band places, one outside the band, whose unmetered pathway comes last. Refused: each reason of §10, and the codes it reuses from the extensions it reads, at least once, each vector a control of one defect.
A conforming verifier gives every request its report and every vector the outcome, the
reason, the place and the facts the corpus states. wai_energy_steering_vectors verify and
the test tests/energy_steering.rs check that the file regenerates byte for byte, that
wai-rs gives every request and vector its outcome, that every reason of §10 refuses a
vector, and that §10 lists exactly the reference’s codes.
live-conformance/energy_steering_verify.py is the second implementation, written from
this text: it shares no code with wai-rs, verifies signatures with WAI’s strict rule written
from RFC 8032 (claim-conformance/_ed25519.py), reads the room with live_rooms_verify.py
and the per-hop records with live_metering_verify.py, parses RFC 9651 itself, recomputes
every report, figure, decision and manifest with Python’s integers, gives every request and
vector the same outcome, and requires every mutated copy of the corpus to fail.
The corpus’s energy figures are synthetic, chosen to exercise the rule. It computes which pathway the rule places first, and claims nothing about the energy any deployment spends or saves.
12. Security and privacy considerations
- The evidence is signed; the choice is checkable. A steering server cannot state a figure the records do not give, or a priority the rule does not, without its decision being refused. What it can do is leave records out, which makes a pathway stale (§5.2) and so ranks it lower, never higher; and state its own loads and sessions (§9.2), which only an audit over its decisions checks.
- Not measuring is never rewarded. No status other than figure gives a pathway a figure (§5.4), no pathway with no figure ranks above one with a figure (§6.2), and the rules that place a client whatever its pathway’s evidence (§6.4 steps 1, 2 and 4) give a pathway with no figure no more than they give one with a figure. An operator that wants a pathway preferred meters it.
- Measuring better is rewarded, measuring worse is not penalised below unknown. Across classes the order is by what backs the figure; a pathway of the weakest class still ranks above every pathway with no figure.
- A hop’s figure is its own statement (energy-measurement §5): the decision makes the choice recomputable from what the hops signed, not the figures true. A hop that understates its energy wins traffic, and the remedies are those of live-metering §11: its class says how the figure was obtained, the key log confines which classes a key may state, and an upstream hop’s record contradicts a downstream one’s.
- Flapping. The draw is fixed for a session, the window moves one period at a time, and the margin h keeps a client on its pathway against a figure within it (§6.4 step 5).
- Privacy. A decision holds no identifier of the client beyond its session, which the
server minted and which names no person; the report is the throughput and buffer the
client sends to every steering server; and the decision does not carry the request, so a
token in its query is not published with it (§9.2). A CMCD report is read for
blandmtpalone, and the decision keeps nothing else of it.