Transport candidates¶
Relay - relay-candidates
REL-01 - status: draft - audio, video, group
Parse the <relay> block of a call ack into relay endpoints (<te2> packed addresses), keying material, and indexed token tables, then select which endpoint to probe for latency versus connect media to.
<relay> element¶
<relay uuid=… self_pid=… peer_pid=…>
<key>…</key> ; relay key (base64 ASCII), 16 bytes decoded
<hbh_key>…</hbh_key> ; hop-by-hop key (base64 ASCII), 30 bytes decoded
<warp_mi_tag_len>…</warp_mi_tag_len> ; optional decimal, > 0
<token id="N">…</token> ; indexed relay tokens (0-based)
<auth_token id="N">…</auth_token> ; indexed auth tokens (0-based)
<te2 …>…</te2> ; one or more transport endpoints
</relay>
uuid is an opaque string. self_pid/peer_pid are decimal participant ids.
Node content MAY be raw bytes or UTF-8 text.
<key> and <hbh_key>¶
<key>: base64-decode (standard alphabet, no-padding fallback) to the raw relay
key (16 bytes observed); if content is not base64, use verbatim. The pre-decode
ASCII is the STUN message-integrity key and MUST be retained.
<hbh_key>: MUST decode to exactly 30 bytes (masterKey(16) || masterSalt(14))
for hop-by-hop SRTP (see srtp-hop-by-hop). Handle a single
base64 layer; if the first decode is not 30 bytes, retry a second base64 decode
(double-base64). Content not yielding 30 bytes is treated as absent.
<warp_mi_tag_len>: decimal; MUST be ignored unless it parses and is > 0.
Token tables¶
<token> and <auth_token> are two independent 0-based indexed tables. Place each
element's decoded content at the slot given by its id, growing the table with
empty entries as needed; when id is absent use the next free slot. <te2>
references these via token_id / auth_token_id.
<te2> endpoint¶
Content is the packed address:
6 bytes → IPv4 : a.b.c.d + port (u16 big-endian, bytes[4..6])
18 bytes → IPv6 : 8 × u16 groups (big-endian) + port (u16, bytes[16..18])
Any other content length MUST be skipped. Default relay port is 3478 (packed
0x0D 0x96).
Attributes:
relay_id— decimal, default 0.relay_name— string, default empty.token_id— decimal index into<token>table, default 0.auth_token_id— decimal index into<auth_token>table, default 0.is_fna—"1"marks a fallback (inbound-only) relay; absent/other = false.protocol— decimal, default 0; carried onto the parsed address.c2r_rtt— optional decimal client→relay RTT in milliseconds.
Multiple <te2> sharing the same relay_id and relay_name MUST be merged into
one endpoint accumulating all addresses (e.g. one IPv4 + one IPv6). The endpoint's
c2r_rtt is taken from the latest <te2> supplying one. A client MUST retain the
verbatim 6-byte IPv4:port content of the first IPv4 <te2>; this exact byte string
is echoed in the <te> of a <relaylatency> stanza (see
call-relaylatency).
Endpoint selection¶
An endpoint is an outbound (latency) candidate iff is_fna is false AND
auth_token_id is non-zero. FNA relays (is_fna=1, auth_token_id=0) are
inbound-only and MUST NOT be probed for outbound latency.
- relaylatency probing: use only outbound candidates, deduped by
relay_name, ordered by ascendingrelay_id. - media transport: select in order: (1) first outbound candidate; else (2)
first non-FNA endpoint; else (3) first endpoint. An offer whose endpoints all
have
auth_token_id=0has no latency candidate but MUST still connect media to a chosen endpoint rather than drop the call.
When merging a patch relay block (e.g. accept ack supplies a fresh hbh_key) over
an existing block, a client MUST prefer each present patch field, and MUST treat
empty token tables / empty endpoint lists in the patch as "unset" so base values
are retained.
Synthetic ICE credentials¶
For WebRTC stacks: ICE ufrag = base64 of the raw selected auth_token bytes; ICE
pwd = base64 of the decoded relay key. An empty token or key yields an empty string.
Requires: call-relaylatency, srtp-hop-by-hop, stun-relay
Implemented by
| Flavor | Status | Source | Notes |
|---|---|---|---|
whatsapp-rust |
working | history - blame - commits 674e851 |
— |
zapo-caller |
working | — | origin of the parser (src/relay/parse.ts) |
Annotation wacrg:REL-01 — a flavor marks its implementation site in source with this comment; a script clones the source, finds it, and attaches the commit blame/permalink.
Contributors
| Contributor | Role |
|---|---|
| wrote initial spec |
protocol history / diff - blame
Open questions
- Full semantics of the relay key vs hbh_key roles beyond STUN MI and hop-by-hop SRTP keying.
- Meaning of the protocol attribute values on a
References - RFC 5389 — STUN
Changelog¶
- 2026-06-21 — Initial spec entry.