<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Observer on ObserveRTC</title><link>/docs/observer-js/</link><description>Recent content in Observer on ObserveRTC</description><generator>Hugo</generator><language>en</language><copyright>Copyright (c) 2020-2024 Thulite</copyright><lastBuildDate>Sun, 13 Sep 2026 10:00:00 +0200</lastBuildDate><atom:link href="/docs/observer-js/index.xml" rel="self" type="application/rss+xml"/><item><title>What changed in 1.0</title><link>/docs/observer-js/what-changed-in-1.0/</link><pubDate>Sun, 13 Sep 2026 10:00:00 +0200</pubDate><guid>/docs/observer-js/what-changed-in-1.0/</guid><description>&lt;p>&lt;code>1.0.0&lt;/code> is the first stable release: the API described across these pages follows semver from here,
so a breaking change means a 2.0.0. Getting there meant removing several things rather than
deprecating them, because each of them was a mechanism that had earned its way out.&lt;/p></description></item><item><title>Ingestion &amp; lifecycle</title><link>/docs/observer-js/ingestion-lifecycle/</link><pubDate>Mon, 15 Jan 2024 10:00:00 +0200</pubDate><guid>/docs/observer-js/ingestion-lifecycle/</guid><description>&lt;h2 id="the-data-flow">The data flow&lt;/h2>



&lt;div class="expressive-code">
 &lt;figure class="frame not-content">
 &lt;figcaption class="header">
 &lt;span class="title">&lt;/span>
 &lt;/figcaption>
 &lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">client getStats() ──► ClientSample ──► observer.accept(sample, ctx?)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ┌────────────────────────────────┘
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ▼
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> accept middlewares (global, in order)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ▼
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> get-or-create ObservedCall ──► get-or-create ObservedClient ──► client.accept(sample, ctx)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> per peerConnections[] in the sample
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ▼
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> get-or-create ObservedPeerConnection
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> .accept(pcSample, ctx) updates all sub-stats,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> derives deltas/bitrates/RTT, correlates remote RTP
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> metrics roll up: PeerConnection → Client → Call → Observer
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> events emitted on the Observer bus&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
 &lt;/figure>
&lt;/div>
&lt;h2 id="observeracceptsample-context">&lt;code>observer.accept(sample, context?)&lt;/code>&lt;/h2>
&lt;p>The single entry point. It:&lt;/p></description></item><item><title>The event bus</title><link>/docs/observer-js/the-event-bus/</link><pubDate>Mon, 15 Jan 2024 10:00:00 +0200</pubDate><guid>/docs/observer-js/the-event-bus/</guid><description>&lt;p>This is the primary API. &lt;strong>Subscribe on the &lt;code>Observer&lt;/code> instance&lt;/strong> — it is the single emitter for the
entire hierarchy. The &lt;code>ObservedCall&lt;/code> / &lt;code>ObservedClient&lt;/code> / &lt;code>ObservedPeerConnection&lt;/code> objects are
&lt;code>EventEmitter&lt;/code>s too, but those local events are reserved for internal lifecycle wiring; application
code uses the observer bus.&lt;/p></description></item><item><title>Entities &amp; API reference</title><link>/docs/observer-js/entities-api-reference/</link><pubDate>Mon, 15 Jan 2024 10:00:00 +0200</pubDate><guid>/docs/observer-js/entities-api-reference/</guid><description>&lt;h2 id="the-hierarchy">The hierarchy&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Class&lt;/th>
 &lt;th>Created by&lt;/th>
 &lt;th>Keyed on its parent as&lt;/th>
 &lt;th>Holds&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;code>Observer&lt;/code>&lt;/td>
 &lt;td>&lt;code>new Observer(config?)&lt;/code>&lt;/td>
 &lt;td>— (root)&lt;/td>
 &lt;td>&lt;code>observedCalls&lt;/code>, global counters, the event bus&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>ObservedCall&lt;/code>&lt;/td>
 &lt;td>&lt;code>observer.createObservedCall(settings)&lt;/code> / lazily by &lt;code>accept&lt;/code>&lt;/td>
 &lt;td>&lt;code>observedCalls&lt;/code>&lt;/td>
 &lt;td>&lt;code>observedClients&lt;/code>, call-wide metrics, &lt;code>detectors&lt;/code>, &lt;code>scoreCalculator&lt;/code>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>ObservedClient&lt;/code>&lt;/td>
 &lt;td>&lt;code>call.createObservedClient(settings)&lt;/code> / lazily&lt;/td>
 &lt;td>&lt;code>observedClients&lt;/code>&lt;/td>
 &lt;td>&lt;code>observedPeerConnections&lt;/code>, per-client metrics&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>ObservedPeerConnection&lt;/code>&lt;/td>
 &lt;td>lazily, from &lt;code>sample.peerConnections[]&lt;/code>&lt;/td>
 &lt;td>&lt;code>observedPeerConnections&lt;/code>&lt;/td>
 &lt;td>the 15 sub-stat maps, transport/RTT/bitrate metrics&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Sub-stats&lt;/td>
 &lt;td>lazily, from the &lt;code>PeerConnectionSample&lt;/code>&lt;/td>
 &lt;td>maps on the PC&lt;/td>
 &lt;td>individual WebRTC stat objects&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>&lt;code>ObservedPeerConnection&lt;/code> holds fifteen &lt;code>public readonly&lt;/code> sub-stat maps:&lt;/p></description></item><item><title>Detectors</title><link>/docs/observer-js/detectors/</link><pubDate>Mon, 15 Jan 2024 10:00:00 +0200</pubDate><guid>/docs/observer-js/detectors/</guid><description>&lt;p>&lt;code>observer-js&lt;/code> ships &lt;strong>ten detectors&lt;/strong>, each an opt-in extension you register explicitly. None are
created automatically.&lt;/p>
&lt;div class="callout callout-tip d-flex flex-row mt-4 mb-4 pt-4 pe-4 pb-2 ps-3">
 
 &lt;div class="callout-content">
 &lt;div class="callout-title">
 &lt;p>The organising principle&lt;/p></description></item><item><title>Validators</title><link>/docs/observer-js/validators/</link><pubDate>Mon, 15 Jan 2024 10:00:00 +0200</pubDate><guid>/docs/observer-js/validators/</guid><description>&lt;p>Every detector answers &lt;em>&amp;ldquo;is something wrong right now?&amp;rdquo;&lt;/em> and runs on every tick, because the answer
legitimately changes. A &lt;strong>validator&lt;/strong> answers &lt;em>&amp;ldquo;is this deployment built correctly?&amp;rdquo;&lt;/em> — which only
changes when you deploy. So it is not configured on and left running: you &lt;strong>start&lt;/strong> one, it runs
until it can decide, reports once, and the observer drops it.&lt;/p></description></item><item><title>Call summaries</title><link>/docs/observer-js/call-summaries/</link><pubDate>Sun, 13 Sep 2026 10:00:00 +0200</pubDate><guid>/docs/observer-js/call-summaries/</guid><description>&lt;p>Detectors answer &amp;ldquo;is something wrong right now&amp;rdquo;, validators answer a structural question once, and
both read state the call throws away when it ends. A &lt;strong>call summary&lt;/strong> is the one thing that
outlives the call: who was in it, what was raised against it, how it scored.&lt;/p></description></item><item><title>SFU integration</title><link>/docs/observer-js/sfu-integration/</link><pubDate>Mon, 15 Jan 2024 10:00:00 +0200</pubDate><guid>/docs/observer-js/sfu-integration/</guid><description>&lt;h2 id="remote-track-resolution">Remote track resolution&lt;/h2>
&lt;p>In an SFU, one participant&amp;rsquo;s &lt;strong>outbound&lt;/strong> track is delivered to other participants as &lt;strong>inbound&lt;/strong>
tracks — one publisher, many subscribers. Correlation is &lt;strong>opt-in per observer&lt;/strong>: set
&lt;code>ObserverConfig.createRemoteTrackResolver&lt;/code>, a factory invoked when each call is created that returns
the call&amp;rsquo;s &lt;code>RemoteTrackResolver&lt;/code> (or &lt;code>undefined&lt;/code> for none).&lt;/p></description></item><item><title>Sinks &amp; injection</title><link>/docs/observer-js/sinks-injection/</link><pubDate>Mon, 15 Jan 2024 10:00:00 +0200</pubDate><guid>/docs/observer-js/sinks-injection/</guid><description>&lt;h2 id="sinks">Sinks&lt;/h2>
&lt;p>A &lt;strong>sink&lt;/strong> receives the samples a client accepts — for archival, streaming, or later offline replay.
Each &lt;code>ObservedClient&lt;/code> gets its &lt;strong>own&lt;/strong> sink, produced by the &lt;code>ObserverConfig.createClientSink&lt;/code>
factory when the client is created (return &lt;code>undefined&lt;/code> for no sink). The client pushes every accepted
sample to its sink and &lt;code>end()&lt;/code>s it on close.&lt;/p></description></item><item><title>Recipes</title><link>/docs/observer-js/recipes/</link><pubDate>Mon, 15 Jan 2024 10:00:00 +0200</pubDate><guid>/docs/observer-js/recipes/</guid><description>&lt;h2 id="a-complete-sfu-side-setup">A complete SFU-side setup&lt;/h2>



&lt;div class="expressive-code">
 &lt;figure class="frame not-content">
 &lt;figcaption class="header">
 &lt;span class="title">&lt;/span>
 &lt;/figcaption>
 &lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">Observer&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">createDefaultMediasoupRemoteTrackResolverFactory&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">createJsonlFileSinkFactory&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;@observertc/observer-js&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">observer&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">Observer&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">closeClientIfIdleForMs&lt;/span>: &lt;span class="kt">60_000&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">closeCallIfEmptyForMs&lt;/span>: &lt;span class="kt">20_000&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Publisher ↔ subscriber links, which four detectors and two validators need.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">createRemoteTrackResolver&lt;/span>: &lt;span class="kt">createDefaultMediasoupRemoteTrackResolverFactory&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Archive every accepted sample, one file per client.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">createClientSink&lt;/span>: &lt;span class="kt">createJsonlFileSinkFactory&lt;/span>&lt;span class="p">({&lt;/span> &lt;span class="nx">directory&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;./stats&amp;#39;&lt;/span> &lt;span class="p">}),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// The one record that outlives the call.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">callSummary&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">include&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;clients&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;issues&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;turnServers&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;scores&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Bake the tenant into appData at birth, resolved once in a middleware.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">createCallAppData&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">({&lt;/span> &lt;span class="nx">callId&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">acceptCtx&lt;/span> &lt;span class="p">})&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">({&lt;/span> &lt;span class="nx">callId&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">tenant&lt;/span>: &lt;span class="kt">acceptCtx?.tenant&lt;/span> &lt;span class="p">}),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">createClientAppData&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">({&lt;/span> &lt;span class="nx">clientId&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">observedCall&lt;/span> &lt;span class="p">})&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">({&lt;/span> &lt;span class="nx">clientId&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">tenant&lt;/span>: &lt;span class="kt">observedCall.appData.tenant&lt;/span> &lt;span class="p">}),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Nothing is implicit — say what you want to watch.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">observer&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">addObserverDetector&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;observer-concurrent-issue-detector&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">issueTypes&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;congestion&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;ice-disconnected&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;ice-connection-failed&amp;#39;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">minAffectedCalls&lt;/span>: &lt;span class="kt">3&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">addObserverDetector&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;client-population-issue-detector&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">issueTypes&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;cpulimitation&amp;#39;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">groupBy&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;browser&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">addObserverDetector&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;turn-server-health-detector&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">addObserverDetector&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;turn-server-outage-detector&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">minClientsAtPeak&lt;/span>: &lt;span class="kt">10&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">observer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">addCallDetector&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;call-concurrent-issue-detector&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">issueTypes&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;congestion&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;video-flow-disrupted&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;invented-speech&amp;#39;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">observer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">addCallDetector&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;track-delivery-mismatch-detector&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">observer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">addCallDetector&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;publisher-fault-corroboration-detector&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">publisherIssueTypes&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;encoder-bottleneck&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;video-capture-bottleneck&amp;#39;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">receiverIssueTypes&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;video-flow-disrupted&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;dry-inbound-track&amp;#39;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Prove the resolver is actually wired — the failure mode is permanent silence.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">observer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">addValidator&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;remote-track-resolver&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">process&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">on&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;SIGINT&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="nx">observer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">close&lt;/span>&lt;span class="p">());&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
 &lt;/figure>
&lt;/div>
&lt;h2 id="http-ingestion">HTTP ingestion&lt;/h2>



&lt;div class="expressive-code">
 &lt;figure class="frame not-content">
 &lt;figcaption class="header">
 &lt;span class="title">&lt;/span>
 &lt;/figcaption>
 &lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="nx">express&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;express&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">app&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">express&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">app&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">use&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">express&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">json&lt;/span>&lt;span class="p">({&lt;/span> &lt;span class="nx">limit&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;2mb&amp;#39;&lt;/span> &lt;span class="p">}));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Carry the clientId outside the payload so routing never depends on decoding it.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">app&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">post&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;/api/samples/:clientId&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">req&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">res&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">observer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">accept&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">req&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">body&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">receivedAt&lt;/span>: &lt;span class="kt">Date.now&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="nx">tenant&lt;/span>: &lt;span class="kt">req.header&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;x-tenant&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">sendStatus&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">202&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
 &lt;/figure>
&lt;/div>
&lt;p>With a binary codec, one decoder per connection and in order — see
&lt;a href="/docs/samples-protobuf-codec/">the protobuf codec&lt;/a>:&lt;/p></description></item></channel></rss>