OBSERVERTC / Version history

Version history

What changed in each schema release, field by field

The schema version lives in sources/version.txt and is stamped into every generated artifact. The published packages — the sample-schemas types, @observertc/samples-protobuf-codec and @observertc/samples-json-codec — are versioned in lockstep with it by the generator.

Current version: 3.7.0

Timeline

VersionDateHeadlineWire-format impact
3.7.02026-08Payloads may nest — free-form JSON instead of a map of primitivesNone — byte-identical to 3.6.0
3.6.02026-08scoreReasons becomes a map of contributions⚠️ scoreReasons becomes a protobuf map
3.5.02026-08Payloads become records, not pre-serialised strings⚠️ Payload representation changes
3.4.02026-08scoreReasons becomes a string[]; both delta codecs arrive⚠️ scoreReasons changes shape
3.3.02026-08key on ClientIssue; TypeScript generator rewrite⚠️ ClientIssue field numbers shift
3.2.02026-03ECN / RFC 8888 counters, PSNR, structured quality-limitation durationsAdditive
3.1.02025-06BigInt byte counters on outbound RTPEncoder/decoder only
3.0.02025-06Complete rewrite of the schema🚨 Breaking vs 2.x

What changed between 3.0.0 and 3.7.0

No field was ever removed and no field was renamed, so a decoder that understands 3.7.0 reads every 3.x sample structurally. What did change — twice — is the type of a small set of fields.

New records

RecordIntroducedWhere it is used
PsnrSum3.2.0OutboundRtpStats.psnrSum — per-plane PSNR (y, u, v)
QualityLimitationDurations3.2.0OutboundRtpStats.qualityLimitationDurations — was a loose map, now a named record with none, cpu, bandwidth, other
AnyValue3.7.0The Avro description of a nestable payload value — documentation of what may appear, not an encoding

New fields

RecordFieldVersionPurpose
ClientIssuekey3.3.0Ties an issue raise to its <type>-resolved companion
InboundRtpStatspacketsReceivedWithEct13.2.0ECN — packets marked ECT(1)
InboundRtpStatspacketsReceivedWithCe3.2.0ECN — packets marked CE
InboundRtpStatspacketsReportedAsLost3.2.0RFC 8888 — reported with a zero R bit
InboundRtpStatspacketsReportedAsLostButRecovered3.2.0RFC 8888 — later recovered
RemoteInboundRtpStatspacketsReceivedWithEct1, packetsReceivedWithCe, packetsReportedAsLost, packetsReportedAsLostButRecovered3.2.0The same, remote side
RemoteInboundRtpStatspacketsWithBleachedEct1Marking3.2.0ECT(1) marking stripped by a middlebox
OutboundRtpStatsencodingIndex3.2.0Index of this encoding in the encodings array
OutboundRtpStatspsnrSum, psnrMeasurements3.2.0Cumulative PSNR by plane, and how many measurements
OutboundRtpStatspacketsSentWithEct13.2.0ECN — packets sent marked ECT(1)
IceTransportStatsccfbMessagesSent, ccfbMessagesReceived3.2.0Congestion Control Feedback messages

Type changes

Field(s)VersionFrom → to
Outbound byte counters3.1.0round-trip as BigInt through the codecs
Several byte and packet counters3.2.0int → long, so long calls at high bitrate do not overflow
ClientSample.scoreReasons3.4.0string → string[]
scoreReasons on every record that has it3.6.0string[] → Record<string, number> — each reason mapped to how much it contributed
The four payload fields3.5.0pre-serialised string → record of primitives
The four payload fields3.7.0record of primitives → free-form JSON (Record<string, unknown>)

Documentation-only changes

IceCandidatePairStats.state gained documentation in 3.3.0 recording which RTCStatsIceCandidatePairState symbols are no longer in the W3C spec — new (never standardised) and cancelled (removed after w3c/webrtc-stats#66). Both symbols are retained, so nothing about the wire format changed.

Upgrading

  1. Read the release page for every version you are skipping.
  2. Upgrade both halves of a codec together — they are released in lockstep for a reason, and a mismatched pair can misread a sample without erroring.
  3. Check whether the versions you are crossing changed a type: 3.4.0, 3.5.0 and 3.6.0 each did, and producers and consumers should move together across those.
  4. If you store encoded samples, decode with a version that matches what wrote them, or re-encode.
  5. Update your own processing for any new field you want to consume; nothing existing was removed.
  6. Deploy incrementally and watch for decode errors before switching writers over.

Reading a mixed fleet

observer-js accepts all three payload generations on the same accept() call — every payload it reads goes through a parser that passes an object through untouched and parses a string, and a legacy string[] of score reasons is folded into the record shape with a magnitude of 0. So you can upgrade clients gradually without coordinating the server.

Sources

Version coverage varies by reference. Source revisions · Markdown source