From f1ecc041215bc47157e1e4301a31f1ac44ef3b75 Mon Sep 17 00:00:00 2001 From: robertjamesprior <83608739+robertjamesprior@users.noreply.github.com> Date: Thu, 15 Oct 2026 00:08:36 +0000 Subject: [PATCH 1/6] Correct the media stage bound to connected --- browsers/live-view.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browsers/live-view.mdx b/browsers/live-view.mdx index d520bd98..e4946b02 100644 --- a/browsers/live-view.mdx +++ b/browsers/live-view.mdx @@ -109,7 +109,7 @@ Both terminal events carry the same `reason`, so branch on it instead of matchin | --- | --- | | `transport` | the socket never opened inside its bound, or closed before signaling | | `signaling` | the socket opened but no offer arrived inside its bound | -| `media` | a peer exists but ICE never reached `checking` inside its bound | +| `media` | a peer exists but ICE never reached `connected` inside its bound | | `peer` | peer construction or the remote offer threw | | `unsupported` | the browser has no `RTCPeerConnection` | | `server` | the server closed the connect | From 58849c976ad0295df1e68111679cf338440c6d02 Mon Sep 17 00:00:00 2001 From: robertjamesprior <83608739+robertjamesprior@users.noreply.github.com> Date: Fri, 16 Oct 2026 19:32:47 +0000 Subject: [PATCH 2/6] Move the example watchdog above the client's connect budget MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The snippet remounted 15s after mount. The client's connect bounds run in sequence and sum to 23s, so an embedder following the example tore the frame down before the client could report which stage stalled — including media, the stage most likely to have stalled. Also correct the note about KERNEL_CONNECTION_TIMEOUT never firing once negotiation begins. The media bound stays armed past `checking` until ICE reaches `connected`, so a connect that stalls in between does report one; what never emits it is a viewer whose frames stop after it connected. --- browsers/live-view.mdx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/browsers/live-view.mdx b/browsers/live-view.mdx index e4946b02..4ee29dc6 100644 --- a/browsers/live-view.mdx +++ b/browsers/live-view.mdx @@ -130,7 +130,7 @@ Both terminal events carry the same `reason`, so branch on it instead of matchin ### Detecting a viewer that never starts -Gate on `KERNEL_PLAYING`. It fires only once frames actually arrive, so it is the signal that distinguishes a working viewer from one that is still connecting or has silently failed. The terminal events below tell you when the client has stopped trying. Start a timer when you mount the iframe and remount if `KERNEL_PLAYING` has not arrived: +Gate on `KERNEL_PLAYING`. It fires only once frames actually arrive, so it is the signal that distinguishes a working viewer from one that is still connecting or has silently failed. The terminal events below tell you when the client has stopped trying. Start a timer when you mount the iframe and remount if `KERNEL_PLAYING` has not arrived. The timer has to outlast the client's connect bounds, which run in sequence — transport, then signaling, then media — and sum to 23s. Reload the frame sooner than that and you will tear it down before it can report which stage stalled: ```typescript Typescript/Javascript const iframe = document.querySelector('#kernel-live-view'); @@ -147,7 +147,7 @@ function arm() { iframe.src = 'about:blank'; iframe.src = src; arm(); - }, 15000); + }, 25000); } window.addEventListener('message', (event) => { @@ -161,7 +161,7 @@ window.addEventListener('message', (event) => { arm(); ``` -Do not gate on `KERNEL_CONNECTION_TIMEOUT` alone. The watchdog behind it is cleared once negotiation begins, so a connection that stalls after that point never emits it. Log it alongside `KERNEL_PLAYING` to capture the connection state at the moment things stalled. +Do not gate on `KERNEL_CONNECTION_TIMEOUT` alone. It only covers a connect that is still negotiating: the media bound clears once ICE reaches `connected`, and a viewer whose frames stop after that never emits one. Log it alongside `KERNEL_PLAYING` to capture the connection state at the moment things stalled. `KERNEL_CONNECTION_FAILED` and `KERNEL_CONNECTION_TIMEOUT` are both terminal, and a failed connect posts one of them, never both. Whichever arrives, nothing further happens until the iframe reloads. Surface a persistent message rather than remounting blindly: a `peer` or `unsupported` reason is a property of the browser environment, so a remount repeats it, while a `transport` reason is worth one retry. From 77bfce2d7f8b286d1d08e5f7d4131b3f488b3466 Mon Sep 17 00:00:00 2001 From: robertjamesprior <83608739+robertjamesprior@users.noreply.github.com> Date: Mon, 19 Oct 2026 21:42:24 +0000 Subject: [PATCH 3/6] Set the example watchdog above the connect budget and deslop the guidance The example remounts at 35s, above the client's connect bounds, which run in sequence (transport, signaling, media) and now sum to 28s after kernel-images#472 settled the media bound at 10s. Below the budget the example reproduces the bug this page was fixing: the embedder tears the frame down before the client can name the stage that stalled. The prose around it says what the timer has to clear and why, since the timer also covers loading the frame and waking a sleeping browser. The timeout warning names `completed` alongside `connected`, which is the other way the media bound clears. The `media` row now reads "connected or completed ... covers a peer that stalls after checking", folded in from kernel/docs#698, which this supersedes. --- browsers/live-view.mdx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/browsers/live-view.mdx b/browsers/live-view.mdx index 4ee29dc6..eea7935c 100644 --- a/browsers/live-view.mdx +++ b/browsers/live-view.mdx @@ -109,7 +109,7 @@ Both terminal events carry the same `reason`, so branch on it instead of matchin | --- | --- | | `transport` | the socket never opened inside its bound, or closed before signaling | | `signaling` | the socket opened but no offer arrived inside its bound | -| `media` | a peer exists but ICE never reached `connected` inside its bound | +| `media` | a peer exists but ICE never reached `connected` or `completed` inside its bound — this covers a peer that stalls after `checking` | | `peer` | peer construction or the remote offer threw | | `unsupported` | the browser has no `RTCPeerConnection` | | `server` | the server closed the connect | @@ -130,7 +130,7 @@ Both terminal events carry the same `reason`, so branch on it instead of matchin ### Detecting a viewer that never starts -Gate on `KERNEL_PLAYING`. It fires only once frames actually arrive, so it is the signal that distinguishes a working viewer from one that is still connecting or has silently failed. The terminal events below tell you when the client has stopped trying. Start a timer when you mount the iframe and remount if `KERNEL_PLAYING` has not arrived. The timer has to outlast the client's connect bounds, which run in sequence — transport, then signaling, then media — and sum to 23s. Reload the frame sooner than that and you will tear it down before it can report which stage stalled: +Gate on `KERNEL_PLAYING`. It fires only once frames actually arrive, so it is the signal that distinguishes a working viewer from one that is still connecting or has silently failed. The terminal events below tell you when the client has stopped trying. Start a timer when you mount the iframe and remount if `KERNEL_PLAYING` has not arrived. The timer has to outlast the client's connect bounds, which run in sequence — transport, then signaling, then media — and sum to 28s by default. It also covers loading the frame, including waking a browser that has gone to sleep, so leave room above that budget when you set it. If you reload sooner, you'll tear the frame down before it can report which stage stalled: ```typescript Typescript/Javascript const iframe = document.querySelector('#kernel-live-view'); @@ -147,7 +147,7 @@ function arm() { iframe.src = 'about:blank'; iframe.src = src; arm(); - }, 25000); + }, 35000); } window.addEventListener('message', (event) => { @@ -161,7 +161,7 @@ window.addEventListener('message', (event) => { arm(); ``` -Do not gate on `KERNEL_CONNECTION_TIMEOUT` alone. It only covers a connect that is still negotiating: the media bound clears once ICE reaches `connected`, and a viewer whose frames stop after that never emits one. Log it alongside `KERNEL_PLAYING` to capture the connection state at the moment things stalled. +Do not gate on `KERNEL_CONNECTION_TIMEOUT` alone. It only covers a connect that is still negotiating: the media bound clears once ICE reaches `connected` or `completed`, and a viewer whose frames stop after it connected never emits one. Log it alongside `KERNEL_PLAYING` to capture the connection state at the moment things stalled. `KERNEL_CONNECTION_FAILED` and `KERNEL_CONNECTION_TIMEOUT` are both terminal, and a failed connect posts one of them, never both. Whichever arrives, nothing further happens until the iframe reloads. Surface a persistent message rather than remounting blindly: a `peer` or `unsupported` reason is a property of the browser environment, so a remount repeats it, while a `transport` reason is worth one retry. From 044c381cd4bf8b37316507a7c9903b0d790683cc Mon Sep 17 00:00:00 2001 From: robertjamesprior <83608739+robertjamesprior@users.noreply.github.com> Date: Tue, 20 Oct 2026 04:17:17 +0000 Subject: [PATCH 4/6] Keep the media bound at 30s and move the example watchdog above the budget The media bound stays at its interim ceiling. The stretch through ICE `connected` is unmeasured in every region, so shortening the default is not something the probe data supports yet; a region that has the timings lowers it through the live view proxy's override instead. With the defaults unchanged, the three bounds sum to 48s rather than 28s, so the example timer moves to 50s to stay above the budget it has to outlast. --- browsers/live-view.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/browsers/live-view.mdx b/browsers/live-view.mdx index eea7935c..e5286a9c 100644 --- a/browsers/live-view.mdx +++ b/browsers/live-view.mdx @@ -130,7 +130,7 @@ Both terminal events carry the same `reason`, so branch on it instead of matchin ### Detecting a viewer that never starts -Gate on `KERNEL_PLAYING`. It fires only once frames actually arrive, so it is the signal that distinguishes a working viewer from one that is still connecting or has silently failed. The terminal events below tell you when the client has stopped trying. Start a timer when you mount the iframe and remount if `KERNEL_PLAYING` has not arrived. The timer has to outlast the client's connect bounds, which run in sequence — transport, then signaling, then media — and sum to 28s by default. It also covers loading the frame, including waking a browser that has gone to sleep, so leave room above that budget when you set it. If you reload sooner, you'll tear the frame down before it can report which stage stalled: +Gate on `KERNEL_PLAYING`. It fires only once frames actually arrive, so it is the signal that distinguishes a working viewer from one that is still connecting or has silently failed. The terminal events below tell you when the client has stopped trying. Start a timer when you mount the iframe and remount if `KERNEL_PLAYING` has not arrived. The timer has to outlast the client's connect bounds, which run in sequence — transport, then signaling, then media — and sum to 48s by default. If you wait less than that, you'll tear the frame down before it can report a stage that stalled late. The timer also covers loading the frame, including waking a browser that has gone to sleep, so leave room above the budget when you set it: ```typescript Typescript/Javascript const iframe = document.querySelector('#kernel-live-view'); @@ -147,7 +147,7 @@ function arm() { iframe.src = 'about:blank'; iframe.src = src; arm(); - }, 35000); + }, 50000); } window.addEventListener('message', (event) => { From cf3da305fa1babc120d415fa236937dbf89dcd5d Mon Sep 17 00:00:00 2001 From: robertjamesprior <83608739+robertjamesprior@users.noreply.github.com> Date: Tue, 20 Oct 2026 15:21:37 +0000 Subject: [PATCH 5/6] Apply mason's notes on the watchdog paragraph The prose said the timer covers waking a browser. It doesn't: the proxy wakes the browser before the socket opens, so that time is already inside the transport bound, and only loading the page itself is extra. The example timer moves to 60s. 50s left 2s over the 48s budget, which is thin for "leave room above the budget", and a region that raises its bounds moves the budget past 48s anyway. The earlier terminal events still show the fallback as soon as they arrive, so this timer only catches a silent stall. Also contracts "Do not" to "Don't", which the docs style guide asks for. --- browsers/live-view.mdx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/browsers/live-view.mdx b/browsers/live-view.mdx index e5286a9c..4f51156d 100644 --- a/browsers/live-view.mdx +++ b/browsers/live-view.mdx @@ -130,7 +130,7 @@ Both terminal events carry the same `reason`, so branch on it instead of matchin ### Detecting a viewer that never starts -Gate on `KERNEL_PLAYING`. It fires only once frames actually arrive, so it is the signal that distinguishes a working viewer from one that is still connecting or has silently failed. The terminal events below tell you when the client has stopped trying. Start a timer when you mount the iframe and remount if `KERNEL_PLAYING` has not arrived. The timer has to outlast the client's connect bounds, which run in sequence — transport, then signaling, then media — and sum to 48s by default. If you wait less than that, you'll tear the frame down before it can report a stage that stalled late. The timer also covers loading the frame, including waking a browser that has gone to sleep, so leave room above the budget when you set it: +Gate on `KERNEL_PLAYING`. It fires only once frames actually arrive, so it is the signal that distinguishes a working viewer from one that is still connecting or has silently failed. The terminal events below tell you when the client has stopped trying. Start a timer when you mount the iframe and remount if `KERNEL_PLAYING` has not arrived. The timer has to outlast the client's connect bounds, which run in sequence — transport, then signaling, then media — and sum to 48s by default. If you wait less than that, you'll tear the frame down before it can report a stage that stalled late. The timer also covers loading the frame itself, so leave room above the budget when you set it: ```typescript Typescript/Javascript const iframe = document.querySelector('#kernel-live-view'); @@ -147,7 +147,7 @@ function arm() { iframe.src = 'about:blank'; iframe.src = src; arm(); - }, 50000); + }, 60000); } window.addEventListener('message', (event) => { @@ -161,7 +161,7 @@ window.addEventListener('message', (event) => { arm(); ``` -Do not gate on `KERNEL_CONNECTION_TIMEOUT` alone. It only covers a connect that is still negotiating: the media bound clears once ICE reaches `connected` or `completed`, and a viewer whose frames stop after it connected never emits one. Log it alongside `KERNEL_PLAYING` to capture the connection state at the moment things stalled. +Don't gate on `KERNEL_CONNECTION_TIMEOUT` alone. It only covers a connect that is still negotiating: the media bound clears once ICE reaches `connected` or `completed`, and a viewer whose frames stop after it connected never emits one. Log it alongside `KERNEL_PLAYING` to capture the connection state at the moment things stalled. `KERNEL_CONNECTION_FAILED` and `KERNEL_CONNECTION_TIMEOUT` are both terminal, and a failed connect posts one of them, never both. Whichever arrives, nothing further happens until the iframe reloads. Surface a persistent message rather than remounting blindly: a `peer` or `unsupported` reason is a property of the browser environment, so a remount repeats it, while a `transport` reason is worth one retry. From 46f1d17303fd9eaa751f47ebfe589ff4e25bf965 Mon Sep 17 00:00:00 2001 From: robertjamesprior <83608739+robertjamesprior@users.noreply.github.com> Date: Tue, 20 Oct 2026 15:45:26 +0000 Subject: [PATCH 6/6] Correct the transport and peer reason rows A socket close at any point before the connect succeeds reports `transport`, not only one before signaling, and `peer` also covers ICE reaching `failed` or `closed` and the data channel closing before the connect. The retry advice follows: remounting repeats a construction or offer throw, but it can fix an ICE failure, so that case is worth one retry. --- browsers/live-view.mdx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/browsers/live-view.mdx b/browsers/live-view.mdx index 4f51156d..921bb746 100644 --- a/browsers/live-view.mdx +++ b/browsers/live-view.mdx @@ -107,10 +107,10 @@ Both terminal events carry the same `reason`, so branch on it instead of matchin | `reason` | Meaning | | --- | --- | -| `transport` | the socket never opened inside its bound, or closed before signaling | +| `transport` | the socket never opened inside its bound, or closed at any point before the connect succeeded | | `signaling` | the socket opened but no offer arrived inside its bound | | `media` | a peer exists but ICE never reached `connected` or `completed` inside its bound — this covers a peer that stalls after `checking` | -| `peer` | peer construction or the remote offer threw | +| `peer` | peer construction or the remote offer threw, ICE reached `failed` or `closed`, or the data channel closed before the connect succeeded | | `unsupported` | the browser has no `RTCPeerConnection` | | `server` | the server closed the connect | @@ -163,7 +163,7 @@ arm(); Don't gate on `KERNEL_CONNECTION_TIMEOUT` alone. It only covers a connect that is still negotiating: the media bound clears once ICE reaches `connected` or `completed`, and a viewer whose frames stop after it connected never emits one. Log it alongside `KERNEL_PLAYING` to capture the connection state at the moment things stalled. -`KERNEL_CONNECTION_FAILED` and `KERNEL_CONNECTION_TIMEOUT` are both terminal, and a failed connect posts one of them, never both. Whichever arrives, nothing further happens until the iframe reloads. Surface a persistent message rather than remounting blindly: a `peer` or `unsupported` reason is a property of the browser environment, so a remount repeats it, while a `transport` reason is worth one retry. +`KERNEL_CONNECTION_FAILED` and `KERNEL_CONNECTION_TIMEOUT` are both terminal, and a failed connect posts one of them, never both. Whichever arrives, nothing further happens until the iframe reloads. Surface a persistent message rather than remounting blindly. `unsupported`, and a `peer` reason from peer construction or the remote offer, are properties of the browser environment, so a remount repeats them. A `transport` reason, or a `peer` reason from ICE reaching `failed` or `closed`, can come from the network path instead, so give those one retry. The client does not retry a connect itself, so whatever `reason` you get is the first failure, and whether to try again is your call.