summaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/src/meshbay_node/transport
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-08-26 10:27:45 +0200
committerChristophe Besson <cbesson@gmail.com>2026-08-26 10:27:45 +0200
commit3be8bd2a7885fbd141f6cc12c2d2073f9a0ac56c (patch)
treec735d6cc99d9d38af2a83f5c0be62f443917f911 /packages/meshbay-node/src/meshbay_node/transport
parent7126fd3c265ba75d77b449bfd0f83f5f3e584b74 (diff)
downloadmeshbay-3be8bd2a7885fbd141f6cc12c2d2073f9a0ac56c.tar.gz
debug(transport): opt-in WebRTC health tracing for mobile-lock investigation
Client (transport.js): a localStorage ring buffer of connection/ICE/ DataChannel state transitions, visibility changes, request timeouts, and periodic health pings — enabled once via ?trace=1 (persists), read back at any time via #mb-debug without devtools. Off by default, zero behavior change unless enabled. Node (webrtc_server.py): MESHBAY_WEBRTC_TRACE=1 gates ICE-state-change logging and a per-session heartbeat (message count, seconds since last message, ICE/connection state) every 30s. Debugging aid for the "stuck after several minutes of mobile screen lock" report — not a fix. Stays on this branch until confirmed useful/resolved.
Diffstat (limited to 'packages/meshbay-node/src/meshbay_node/transport')
-rw-r--r--packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py39
1 files changed, 39 insertions, 0 deletions
diff --git a/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py b/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py
index 6709fbc..724527b 100644
--- a/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py
+++ b/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py
@@ -199,6 +199,20 @@ def _pack(obj: dict) -> bytes:
return struct.pack(">I", len(data)) + data
+# Opt-in, off by default: a per-session heartbeat log (message count, time
+# since the last message, ICE state) and ICE-state-change logging, on top of
+# the connectionstatechange logging that already runs unconditionally. Added
+# while chasing a report of the browser side going unresponsive after a
+# mobile screen lock; --log-level DEBUG was not the right knob for this,
+# since it is already used for the per-message request/response tracing
+# every group index lookup produces, and turning that on for days of normal
+# operation just to catch one intermittent session is not viable. Set
+# MESHBAY_WEBRTC_TRACE=1 in the node's environment for the duration of a
+# debugging session.
+_WEBRTC_TRACE = os.environ.get("MESHBAY_WEBRTC_TRACE") == "1"
+_WEBRTC_TRACE_INTERVAL_S = 30.0
+
+
class _DataChannelBuffer:
"""
Accumulate DataChannel messages and extract length-prefixed msgpack.
@@ -295,6 +309,9 @@ class WebRTCPeerSession:
self._nonce_client: bytes = b""
self._admin_ops: dict[str, dict] = {} # op_id → pending admin operation
self._uploads: dict[str, dict] = {} # filename → {next_index, bytes}
+ # Diagnostics only (_WEBRTC_TRACE): when the last DataChannel message
+ # arrived, so the heartbeat can report silence duration.
+ self._last_msg_at: float = 0.0
def _setup_channel(self, channel: RTCDataChannel) -> None:
self._channel = channel
@@ -305,6 +322,7 @@ class WebRTCPeerSession:
if isinstance(message, str):
message = message.encode()
self._msg_count += 1
+ self._last_msg_at = time.monotonic()
if self._msg_count <= 3:
log.info("WebRTC data received: %d bytes, msg #%d (peer=%s)",
len(message), self._msg_count, self._peer_id)
@@ -312,6 +330,22 @@ class WebRTCPeerSession:
for msg in self._buffer.messages():
self._handle_message(msg)
+ if _WEBRTC_TRACE:
+ self._spawn(self._trace_heartbeat())
+
+ async def _trace_heartbeat(self) -> None:
+ """Diagnostics only (_WEBRTC_TRACE): periodic proof-of-life for this
+ session, so a gap in these lines pinpoints when the node stopped
+ hearing from a peer that (from its own side) may still look connected."""
+ while True:
+ await asyncio.sleep(_WEBRTC_TRACE_INTERVAL_S)
+ silence = time.monotonic() - self._last_msg_at if self._last_msg_at else -1
+ log.info(
+ "WebRTC heartbeat peer=%s msgs=%d silence=%.0fs pc=%s ice=%s",
+ self._peer_id, self._msg_count, silence,
+ self._pc.connectionState, self._pc.iceConnectionState,
+ )
+
def _handle_message(self, msg: dict) -> None:
mtype = msg.get("type")
log.debug("WebRTC recv: %s", mtype)
@@ -4353,6 +4387,11 @@ class WebRTCTransport:
log.info("WebRTC DataChannel opened: %s (peer=%s)", channel.label, peer_id)
session._setup_channel(channel)
+ if _WEBRTC_TRACE:
+ @pc.on("iceconnectionstatechange")
+ def on_ice_state_change():
+ log.info("WebRTC ICE state: %s (peer=%s)", pc.iceConnectionState, peer_id)
+
@pc.on("connectionstatechange")
async def on_state_change():
state = pc.connectionState