Replies: 5 comments
-
|
@seetadev : Grateful for your guidance above. Trial 1: (connection.py)
Trial 2: (transport.py & connection.py)
Trial 3: (connect.py)
|
Beta Was this translation helpful? Give feedback.
-
WebRTC-Direct Stabilization:
Key Fixes1. Ownership & Scope
2. Lifecycle Ordering
3. Inbound Pipeline Integrity
4. Thread-Safe Bridging
5. Write Path Discipline
6. Event Loop Persistence
7. Error Visibility
Net Outcome
|
Beta Was this translation helpful? Give feedback.
-
WebRTC-Direct Fixes
1. ICE timeout and connectivity (FIXED)Root problems
Fixes
Result
2. Handshake data channel timeout (FIXED)Root problems
Fixes
Result
3. Current failure: muxer negotiation timeout (IN PROGRESS)What I verified
What’s likely wrong
Diagnostics added
Next steps
|
Beta Was this translation helpful? Give feedback.
-
Fixed Muxer Negotiation Timeouts and other propagating issues in webrtc-direct
WEBRTC-DIRECT Status (demonstrated in examples/webrtc/test_webrtc_pvt_to_public_example.py)
Example Test outputs `test_webrtc_pvt_to_public_example.py`((.venv) ) ➜ py-libp2p git:(feat/add-webrtc-transport) ✗ python examples/webrtc/test_webrtc_pvt_to_public_example.py
ICE config fixes not available - using default configuration
2026-02-22 23:48:10,934 [INFO] [__main__] ======================================================================
2026-02-22 23:48:10,934 [INFO] [__main__] 🧪 WebRTC-Direct (Private-to-Public) Connection Test
2026-02-22 23:48:10,935 [INFO] [__main__] ======================================================================
2026-02-22 23:48:10,935 [INFO] [__main__]
2026-02-22 23:48:10,935 [INFO] [__main__] This test demonstrates:
2026-02-22 23:48:10,935 [INFO] [__main__] 1. Public server node setup (listening on webrtc-direct)
2026-02-22 23:48:10,935 [INFO] [__main__] 2. Client peer (browser simulation) connecting to server
2026-02-22 23:48:10,935 [INFO] [__main__] 3. WebRTC-Direct connection establishment
2026-02-22 23:48:10,935 [INFO] [__main__] 4. Data exchange over WebRTC streams
2026-02-22 23:48:10,935 [INFO] [__main__]
2026-02-22 23:48:10,935 [INFO] [__main__] 🔧 Setting up Server Peer (Public Node)...
2026-02-22 23:48:11,153 [INFO] [__main__] ✅ WebRTC-Direct transport started on server
2026-02-22 23:48:11,156 [INFO] [__main__] ✅ Server listening on WebRTC-Direct
2026-02-22 23:48:11,156 [INFO] [__main__] 📍 Server listening addresses:
2026-02-22 23:48:11,156 [INFO] [__main__] /ip4/127.0.0.1/udp/54111/webrtc-direct/certhash/uEiB8Y41CbvDzmdZql605nZJBvjlUwQH8vjUTDAh3malNmg/p2p/12D3KooWCDL5kZ7AXGEVcdPoCMm6Cg9wYowP1cUsY7CGa169KANB
2026-02-22 23:48:12,157 [INFO] [__main__] 🔧 Setting up Client Peer (Browser Simulation)...
2026-02-22 23:48:12,368 [INFO] [__main__] ✅ WebRTC-Direct transport started on client
2026-02-22 23:48:13,369 [INFO] [__main__] Stored server address for 12D3KooWCDL5kZ7AXGEVcdPoCMm6Cg9wYowP1cUsY7CGa169KANB in client peerstore
2026-02-22 23:48:13,370 [INFO] [__main__]
2026-02-22 23:48:13,370 [INFO] [__main__] 🔌 Client dialing server: /ip4/127.0.0.1/udp/54111/webrtc-direct/certhash/uEiB8Y41CbvDzmdZql605nZJBvjlUwQH8vjUTDAh3malNmg/p2p/12D3KooWCDL5kZ7AXGEVcdPoCMm6Cg9wYowP1cUsY7CGa169KANB
2026-02-22 23:48:13,370 [INFO] [__main__] (This mimics: browser connecting to public server)
Dialer using certificate fingerprint=45:85:65:28:1D:E7:55:B9:E0:88:E6:7F:C2:E3:0C:A5:E1:48:B2:87:49:1A:C1:E6:56:47:7C:C1:4A:55:0F:C4 certhash=uEiBFhWUoHedVueCI5n_C4wyl4Uiyh0kaweZWR3zBSlUPxA
UDP punch toward 127.0.0.1:54111 with metadata {'ufrag': 'libp2p+webrtc+v1/1g2e21mr59b/cux', 'peer_id': '12D3KooWHBr5VUHqY1nRBebMhHr99jxYJENZHNkuM5cE3Do7vBdE', 'certhash': 'uEiBFhWUoHedVueCI5n_C4wyl4Uiyh0kaweZWR3zBSlUPxA', 'fingerprint': '45:85:65:28:1D:E7:55:B9:E0:88:E6:7F:C2:E3:0C:A5:E1:48:B2:87:49:1A:C1:E6:56:47:7C:C1:4A:55:0F:C4', 'role': 'client'}
Config certificate, expected fingerprint=7C:63:8D:42:6E:F0:F3:99:D6:6A:97:AD:39:9D:92:41:BE:39:54:C1:01:FC:BE:35:13:0C:08:77:99:A9:4D:9A, aiortc fingerprints=['sha-256 7C:63:8D:42:6E:F0:F3:99:D6:6A:97:AD:39:9D:92:41:BE:39:54:C1:01:FC:BE:35:13:0C:08:77:99:A9:4D:9A', 'sha-384 ED:A1:79:D7:EA:8D:F4:91:70:33:E9:06:DC:B0:F1:49:7A:6E:9F:65:01:DE:AB:99:8F:A2:F5:C8:1B:FC:06:FB:51:DF:B7:0D:79:C5:E0:0E:B8:02:2C:F0:89:46:2D:09', 'sha-512 86:53:46:17:DD:A7:55:DB:F7:23:EF:81:15:7E:AB:58:19:FE:76:E3:30:28:1F:79:4C:1A:F9:08:AD:B6:8A:5C:D7:04:23:4A:9D:A1:02:FB:FF:D2:BF:21:EC:05:0F:61:0B:50:C1:18:46:9E:D3:7E:E9:CC:8B:A6:BA:1C:3B:AB']
sent punch to 127.0.0.1:54111 metadata={'ufrag': 'libp2p+webrtc+v1/1g2e21mr59b/cux', 'peer_id': '12D3KooWHBr5VUHqY1nRBebMhHr99jxYJENZHNkuM5cE3Do7vBdE', 'certhash': 'uEiBFhWUoHedVueCI5n_C4wyl4Uiyh0kaweZWR3zBSlUPxA', 'fingerprint': '45:85:65:28:1D:E7:55:B9:E0:88:E6:7F:C2:E3:0C:A5:E1:48:B2:87:49:1A:C1:E6:56:47:7C:C1:4A:55:0F:C4', 'role': 'client'}
sent punch to 127.0.0.1:54111 metadata={'ufrag': 'libp2p+webrtc+v1/1g2e21mr59b/cux', 'peer_id': '12D3KooWHBr5VUHqY1nRBebMhHr99jxYJENZHNkuM5cE3Do7vBdE', 'certhash': 'uEiBFhWUoHedVueCI5n_C4wyl4Uiyh0kaweZWR3zBSlUPxA', 'fingerprint': '45:85:65:28:1D:E7:55:B9:E0:88:E6:7F:C2:E3:0C:A5:E1:48:B2:87:49:1A:C1:E6:56:47:7C:C1:4A:55:0F:C4', 'role': 'client'}
sent punch to 127.0.0.1:54111 metadata={'ufrag': 'libp2p+webrtc+v1/1g2e21mr59b/cux', 'peer_id': '12D3KooWHBr5VUHqY1nRBebMhHr99jxYJENZHNkuM5cE3Do7vBdE', 'certhash': 'uEiBFhWUoHedVueCI5n_C4wyl4Uiyh0kaweZWR3zBSlUPxA', 'fingerprint': '45:85:65:28:1D:E7:55:B9:E0:88:E6:7F:C2:E3:0C:A5:E1:48:B2:87:49:1A:C1:E6:56:47:7C:C1:4A:55:0F:C4', 'role': 'client'}
sent punch to 127.0.0.1:54111 metadata={'ufrag': 'libp2p+webrtc+v1/1g2e21mr59b/cux', 'peer_id': '12D3KooWHBr5VUHqY1nRBebMhHr99jxYJENZHNkuM5cE3Do7vBdE', 'certhash': 'uEiBFhWUoHedVueCI5n_C4wyl4Uiyh0kaweZWR3zBSlUPxA', 'fingerprint': '45:85:65:28:1D:E7:55:B9:E0:88:E6:7F:C2:E3:0C:A5:E1:48:B2:87:49:1A:C1:E6:56:47:7C:C1:4A:55:0F:C4', 'role': 'client'}
sent punch to 127.0.0.1:54111 metadata={'ufrag': 'libp2p+webrtc+v1/1g2e21mr59b/cux', 'peer_id': '12D3KooWHBr5VUHqY1nRBebMhHr99jxYJENZHNkuM5cE3Do7vBdE', 'certhash': 'uEiBFhWUoHedVueCI5n_C4wyl4Uiyh0kaweZWR3zBSlUPxA', 'fingerprint': '45:85:65:28:1D:E7:55:B9:E0:88:E6:7F:C2:E3:0C:A5:E1:48:B2:87:49:1A:C1:E6:56:47:7C:C1:4A:55:0F:C4', 'role': 'client'}
Config certificate, expected fingerprint=45:85:65:28:1D:E7:55:B9:E0:88:E6:7F:C2:E3:0C:A5:E1:48:B2:87:49:1A:C1:E6:56:47:7C:C1:4A:55:0F:C4, aiortc fingerprints=['sha-256 45:85:65:28:1D:E7:55:B9:E0:88:E6:7F:C2:E3:0C:A5:E1:48:B2:87:49:1A:C1:E6:56:47:7C:C1:4A:55:0F:C4', 'sha-384 7A:CB:60:CE:53:C5:40:59:6B:69:F1:38:9E:DA:A3:6C:9F:28:A8:A0:0B:42:C9:54:E9:97:AD:A9:15:27:17:61:6F:67:58:79:85:D6:4A:17:C4:A5:B6:52:11:F0:84:74', 'sha-512 D9:BC:26:88:1D:2F:93:A4:22:A7:8E:4B:6F:77:7F:25:2C:54:B7:88:B9:AE:AC:1F:B7:65:DF:6E:D4:03:16:DE:69:16:80:64:D5:85:3D:2A:59:09:08:B2:55:D1:6F:B9:FE:F1:B8:79:70:D1:85:B5:9D:74:31:7F:F5:47:E7:D6']
🔵 Inbound Data Pump STARTED for 12D3KooWCDL5kZ7AXGEVcdPoCMm6Cg9wYowP1cUsY7CGa169KANB
client SDP has 5 candidates (SDP length: 1370 chars, iceGatheringState: complete)
client candidate 0: a=candidate:ed51efaaed2c1e66f90f36e894e04973 1 udp 2130706431 2406:b400:66:422:890:2967:eadd:e9ad 56113 typ host
client candidate 1: a=candidate:529be26347044c6ecb17d48c88ee347b 1 udp 2130706431 2406:b400:66:422:4c1:4826:2bee:e479 54940 typ host
client candidate 2: a=candidate:a40dd52ba0d2090fef6f2a272f77aca3 1 udp 2130706431 192.168.0.5 49757 typ host
client candidate 3: a=candidate:16572de626da4e5384a0ce2d0d93678a 1 udp 2130706431 127.0.0.1 64795 typ host
client candidate 4: a=candidate:365cbcb59afe9901aaa0f5cc9c9b5eea 1 udp 2130706431 ::1 63769 typ host
server setting remote description (offer) with 5 candidates, current state: new, current state: new, ICE: new
🔵 Inbound Data Pump STARTED for 12D3KooWHBr5VUHqY1nRBebMhHr99jxYJENZHNkuM5cE3Do7vBdE
server set remote description, connection state: new, ICE: new, iceGatheringState: new
server after setRemoteDescription: 0 candidate pairs (0 expected here; pairs form after createAnswer)
server SDP has 6 candidates (SDP length: 1492 chars, iceGatheringState: complete)
server candidate 0: a=candidate:ed51efaaed2c1e66f90f36e894e04973 1 udp 2130706431 2406:b400:66:422:890:2967:eadd:e9ad 64158 typ host
server candidate 1: a=candidate:529be26347044c6ecb17d48c88ee347b 1 udp 2130706431 2406:b400:66:422:4c1:4826:2bee:e479 50111 typ host
server candidate 2: a=candidate:a40dd52ba0d2090fef6f2a272f77aca3 1 udp 2130706431 192.168.0.5 65363 typ host
server candidate 3: a=candidate:16572de626da4e5384a0ce2d0d93678a 1 udp 2130706431 127.0.0.1 61443 typ host
server candidate 4: a=candidate:365cbcb59afe9901aaa0f5cc9c9b5eea 1 udp 2130706431 ::1 58177 typ host
server candidate 5: a=candidate:eff3cf86d71c475c90bfe781a04cc0ff 1 udp 1694498815 183.82.163.20 31197 typ srflx raddr 192.168.0.5 rport 65363
client setting remote description (answer) with 6 candidates, current state: new, ICE: new
client set remote description, connection state: connecting, ICE: checking, iceGatheringState: complete
client after setRemoteDescription: 15 candidate pairs
client local fingerprint extracted: 45:85:65:28:1D:E7:55:B9:E0:88:E6:7F:C2:E3:0C:A5:E1:48:B2:87:49:1A:C1:E6:56:47:7C:C1:4A:55:0F:C4
client remote fingerprint as seen in SDP/DTLS: 7C:63:8D:42:6E:F0:F3:99:D6:6A:97:AD:39:9D:92:41:BE:39:54:C1:01:FC:BE:35:13:0C:08:77:99:A9:4D:9A
client expected certhash=uEiB8Y41CbvDzmdZql605nZJBvjlUwQH8vjUTDAh3malNmg actual certhash=uEiB8Y41CbvDzmdZql605nZJBvjlUwQH8vjUTDAh3malNmg
server local fingerprint extracted: 7C:63:8D:42:6E:F0:F3:99:D6:6A:97:AD:39:9D:92:41:BE:39:54:C1:01:FC:BE:35:13:0C:08:77:99:A9:4D:9A
server remote fingerprint as seen in SDP/DTLS: 45:85:65:28:1D:E7:55:B9:E0:88:E6:7F:C2:E3:0C:A5:E1:48:B2:87:49:1A:C1:E6:56:47:7C:C1:4A:55:0F:C4
server expected certhash=uEiBFhWUoHedVueCI5n_C4wyl4Uiyh0kaweZWR3zBSlUPxA actual certhash=uEiBFhWUoHedVueCI5n_C4wyl4Uiyh0kaweZWR3zBSlUPxA
🔵 FIRST MESSAGE CONSUMED from buffer for 12D3KooWCDL5kZ7AXGEVcdPoCMm6Cg9wYowP1cUsY7CGa169KANB
🔵 FIRST MESSAGE CONSUMED from buffer for 12D3KooWHBr5VUHqY1nRBebMhHr99jxYJENZHNkuM5cE3Do7vBdE
[webrtc-direct] dialer muxer negotiation: peer=12D3KooWCDL5kZ7AXGEVcdPoCMm6Cg9wYowP1cUsY7CGa169KANB is_initiator=True
[DEBUG] About to call upgrade_connection() for 12D3KooWCDL5kZ7AXGEVcdPoCMm6Cg9wYowP1cUsY7CGa169KANB
[DEBUG] Stack before upgrade_connection:
File "/Users/matrix/Documents/Season1/py-libp2p/.venv/lib/python3.12/site-packages/trio/_core/_run.py", line 2538, in run
timeout = gen.send(next_send)
File "/Users/matrix/Documents/Season1/py-libp2p/.venv/lib/python3.12/site-packages/trio/_core/_run.py", line 2872, in unrolled_run
msg = task.context.run(next_send_fn, next_send)
File "/Users/matrix/Documents/Season1/py-libp2p/examples/webrtc/test_webrtc_pvt_to_public_example.py", line 334, in main
await test_webrtc_direct()
File "/Users/matrix/Documents/Season1/py-libp2p/examples/webrtc/test_webrtc_pvt_to_public_example.py", line 255, in test_webrtc_direct
connection = await client_transport.dial(server_addr)
File "/Users/matrix/Documents/Season1/py-libp2p/libp2p/transport/webrtc/private_to_public/transport.py", line 753, in dial
stack_str = "".join(traceback.format_stack()[-5:])
[LISTENER] About to call register_incoming_connection() for 12D3KooWHBr5VUHqY1nRBebMhHr99jxYJENZHNkuM5cE3Do7vBdE
[LISTENER] connection type: SecureSession, is_initiator=False
[LISTENER] connection has conn attr: True
[LISTENER] conn.conn type: NoiseTransportReadWriter
[LISTENER] conn.conn.read_writer type: NoisePacketReadWriter
[LISTENER] register_incoming_connection() called
[LISTENER] remote_peer_id=12D3KooWHBr5VUHqY1nRBebMhHr99jxYJENZHNkuM5cE3Do7vBdE
[LISTENER] register_incoming_connection() completed for 12D3KooWHBr5VUHqY1nRBebMhHr99jxYJENZHNkuM5cE3Do7vBdE
[DEBUG] upgrade_connection() returned for 12D3KooWCDL5kZ7AXGEVcdPoCMm6Cg9wYowP1cUsY7CGa169KANB
2026-02-22 23:48:21,358 [INFO] [__main__] ✅ WebRTC-Direct connection established!
2026-02-22 23:48:21,358 [INFO] [__main__]
2026-02-22 23:48:21,358 [INFO] [__main__] 📤 Testing data exchange over WebRTC-Direct connection...
2026-02-22 23:48:21,374 [INFO] [__main__] 📤 Sending: Hello from Client!
2026-02-22 23:48:21,376 [INFO] [__main__] 📨 Echo handler received: Hello from Client!
2026-02-22 23:48:21,377 [INFO] [__main__] Received echo: Hello from Client!
2026-02-22 23:48:21,377 [INFO] [__main__]
2026-02-22 23:48:21,377 [INFO] [__main__] 📤 Sending: This is a WebRTC-Direct test message
2026-02-22 23:48:21,381 [INFO] [__main__] 📨 Echo handler received: This is a WebRTC-Direct test message
2026-02-22 23:48:21,385 [INFO] [__main__] Received echo: This is a WebRTC-Direct test message
2026-02-22 23:48:21,385 [INFO] [__main__]
2026-02-22 23:48:21,385 [INFO] [__main__] 📤 Sending: WebRTC-Direct works!
2026-02-22 23:48:21,388 [INFO] [__main__] 📨 Echo handler received: WebRTC-Direct works!
2026-02-22 23:48:21,389 [INFO] [__main__] Received echo: WebRTC-Direct works!
2026-02-22 23:48:21,389 [INFO] [__main__]
2026-02-22 23:48:21,389 [INFO] [__main__] 🧹 Cleaning up...
SCTP _set_state(CLOSED) BLOCKED on 4364374800 - current_state=None, peer_conn=unknown (cannot verify ownership, blocking to be safe)
client ICE connection state changed to: closed (connectionState: connected, iceGatheringState: complete)
DTLS _set_state(CLOSED) BLOCKED on 4461029104 - current_state=CONNECTED, peer_conn=unknown (cannot verify ownership, blocking to be safe)
SCTP _set_state(CLOSED) BLOCKED on 4461730352 - current_state=None, peer_conn=unknown (cannot verify ownership, blocking to be safe)
DTLS _set_state(CLOSED) BLOCKED on 4461730064 - current_state=CONNECTED, peer_conn=unknown (cannot verify ownership, blocking to be safe)
Error reading from WebRTC connection:
Traceback (most recent call last):
File "/Users/matrix/Documents/Season1/py-libp2p/libp2p/transport/webrtc/connection.py", line 1029, in read
data = await self.receive_channel.receive()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/matrix/Documents/Season1/py-libp2p/.venv/lib/python3.12/site-packages/trio/_channel.py", line 370, in receive
return await trio.lowlevel.wait_task_rescheduled(abort_fn) # type: ignore[no-any-return]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/matrix/Documents/Season1/py-libp2p/.venv/lib/python3.12/site-packages/trio/_core/_traps.py", line 214, in wait_task_rescheduled
return (await _async_yield(WaitTaskRescheduled(abort_func))).unwrap()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/matrix/Documents/Season1/py-libp2p/.venv/lib/python3.12/site-packages/outcome/_impl.py", line 213, in unwrap
raise captured_error
trio.EndOfChannel
Noise read_msg failed: Connection closed during read operation: expected 2 bytes but received 0 bytes
Traceback (most recent call last):
File "/Users/matrix/Documents/Season1/py-libp2p/libp2p/security/noise/io.py", line 68, in read_msg
noise_msg_encrypted = await self.read_writer.read_msg()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/matrix/Documents/Season1/py-libp2p/libp2p/io/msgio.py", line 60, in read_msg
length = await self.next_msg_len()
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/matrix/Documents/Season1/py-libp2p/libp2p/io/msgio.py", line 81, in next_msg_len
return await read_length(self.read_write_closer, self.size_len_bytes)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/matrix/Documents/Season1/py-libp2p/libp2p/io/msgio.py", line 37, in read_length
length_bytes = await read_exactly(reader, size_len_bytes)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/matrix/Documents/Season1/py-libp2p/libp2p/io/utils.py", line 74, in read_exactly
raise IncompleteReadError(
libp2p.io.exceptions.IncompleteReadError: Connection closed during read operation: expected 2 bytes but received 0 bytes
2026-02-22 23:48:21,428 [INFO] [__main__]
2026-02-22 23:48:21,428 [INFO] [__main__] ======================================================================
2026-02-22 23:48:21,428 [INFO] [__main__] ✅ All tests passed!
2026-02-22 23:48:21,428 [INFO] [__main__] ======================================================================
2026-02-22 23:48:21,428 [INFO] [__main__]
2026-02-22 23:48:21,428 [INFO] [__main__] This demonstrates that WebRTC-Direct (private-to-public)
2026-02-22 23:48:21,428 [INFO] [__main__] connections work correctly in py-libp2p.
2026-02-22 23:48:21,428 [INFO] [__main__]
|
Beta Was this translation helpful? Give feedback.
-
WebRTC Private-to-Private FixesPR CommitExample Test outputs `test_webrtc_pvt_to_pvt_example.py`((.venv) ) ➜ py-libp2p git:(feat/add-webrtc-transport) ✗ python examples/webrtc/test_webrtc_pvt_to_pvt_example.py
ICE config fixes not available - using default configuration
2026-02-22 23:40:02,116 [INFO] [__main__] ======================================================================
2026-02-22 23:40:02,116 [INFO] [__main__] 🧪 WebRTC Private-to-Private Connection Test
2026-02-22 23:40:02,116 [INFO] [__main__] ======================================================================
2026-02-22 23:40:02,116 [INFO] [__main__]
2026-02-22 23:40:02,116 [INFO] [__main__] This test demonstrates:
2026-02-22 23:40:02,116 [INFO] [__main__] 1. Circuit Relay v2 server setup
2026-02-22 23:40:02,116 [INFO] [__main__] 2. Two NAT peers connecting to relay
2026-02-22 23:40:02,116 [INFO] [__main__] 3. WebRTC connection establishment through relay
2026-02-22 23:40:02,116 [INFO] [__main__] 4. Data exchange over WebRTC streams
2026-02-22 23:40:02,116 [INFO] [__main__]
2026-02-22 23:40:02,116 [INFO] [__main__] 🔧 Setting up Circuit Relay v2 server...
Stream handlers registered successfully
2026-02-22 23:40:02,823 [INFO] [__main__] ✅ Relay server started on: [<Multiaddr /ip4/127.0.0.1/tcp/58465/p2p/12D3KooWQkGc15oB3u9ho98BBuRPfEzEKCQ8XJBYtsrb6kb6sEfe>]
2026-02-22 23:40:03,825 [INFO] [__main__] 🔧 Setting up Peer A (Dialer)...
2026-02-22 23:40:04,032 [INFO] [__main__] 📝 Stored relay addresses for Peer A (Dialer): [<Multiaddr /ip4/127.0.0.1/tcp/58465/p2p/12D3KooWQkGc15oB3u9ho98BBuRPfEzEKCQ8XJBYtsrb6kb6sEfe>]
2026-02-22 23:40:04,550 [INFO] [__main__] ✅ Peer A (Dialer) connected to relay 12D3KooWQkGc15oB3u9ho98BBuRPfEzEKCQ8XJBYtsrb6kb6sEfe
2026-02-22 23:40:04,551 [INFO] [__main__] 🔧 Setting up Peer B (Listener)...
2026-02-22 23:40:04,760 [INFO] [__main__] 📝 Stored relay addresses for Peer B (Listener): [<Multiaddr /ip4/127.0.0.1/tcp/58465/p2p/12D3KooWQkGc15oB3u9ho98BBuRPfEzEKCQ8XJBYtsrb6kb6sEfe>]
2026-02-22 23:40:05,275 [INFO] [__main__] ✅ Peer B (Listener) connected to relay 12D3KooWQkGc15oB3u9ho98BBuRPfEzEKCQ8XJBYtsrb6kb6sEfe
2026-02-22 23:40:05,276 [INFO] [__main__] 📊 Peer A connected peers: ['12D3KooWQkGc15oB3u9ho98BBuRPfEzEKCQ8XJBYtsrb6kb6sEfe']
2026-02-22 23:40:05,276 [INFO] [__main__] 📊 Peer B connected peers: ['12D3KooWQkGc15oB3u9ho98BBuRPfEzEKCQ8XJBYtsrb6kb6sEfe']
2026-02-22 23:40:06,277 [INFO] [__main__] 🔧 Initializing WebRTC transports...
Stream handlers registered successfully
2026-02-22 23:40:06,281 [INFO] [__main__] ✅ WebRTC transport A started
2026-02-22 23:40:06,282 [INFO] [__main__] 📝 Stored relay addresses in transport A peerstore
relay discovery connected peers: ['12D3KooWQkGc15oB3u9ho98BBuRPfEzEKCQ8XJBYtsrb6kb6sEfe']
relay discovery: peerstore protocols for 12D3KooWQkGc15oB3u9ho98BBuRPfEzEKCQ8XJBYtsrb6kb6sEfe = {'/libp2p/circuit/relay/2.0.0', '/libp2p/circuit/relay/2.0.0/stop'}
Stream handlers registered successfully
2026-02-22 23:40:06,283 [INFO] [__main__] ✅ WebRTC transport B started
2026-02-22 23:40:06,283 [INFO] [__main__] 📝 Stored relay addresses in transport B peerstore
relay discovery connected peers: ['12D3KooWQkGc15oB3u9ho98BBuRPfEzEKCQ8XJBYtsrb6kb6sEfe']
relay discovery: peerstore protocols for 12D3KooWQkGc15oB3u9ho98BBuRPfEzEKCQ8XJBYtsrb6kb6sEfe = {'/libp2p/circuit/relay/2.0.0', '/libp2p/circuit/relay/2.0.0/stop'}
2026-02-22 23:40:07,284 [INFO] [__main__] 🔧 Starting WebRTC listener on Peer B...
relay discovery initial relays: ['12D3KooWQkGc15oB3u9ho98BBuRPfEzEKCQ8XJBYtsrb6kb6sEfe']
ensure_listener_ready relays: ['12D3KooWQkGc15oB3u9ho98BBuRPfEzEKCQ8XJBYtsrb6kb6sEfe'] reservation_failed= False
advertised listener addrs: ['/ip4/127.0.0.1/tcp/58465/p2p/12D3KooWQkGc15oB3u9ho98BBuRPfEzEKCQ8XJBYtsrb6kb6sEfe/p2p-circuit/webrtc/p2p/12D3KooWNnGaN4Vdf2h1pLdUtwgSu1JZeVyaxCM7U4XRdqbi6AB8']
2026-02-22 23:40:07,293 [INFO] [__main__] ✅ WebRTC listener started on Peer B
2026-02-22 23:40:07,293 [INFO] [__main__] Waiting for relay discovery & addr advertisement
2026-02-22 23:40:09,295 [INFO] [__main__]
2026-02-22 23:40:09,295 [INFO] [__main__] 📍 Peer B listening addresses:
2026-02-22 23:40:09,295 [INFO] [__main__] /ip4/127.0.0.1/tcp/58465/p2p/12D3KooWQkGc15oB3u9ho98BBuRPfEzEKCQ8XJBYtsrb6kb6sEfe/p2p-circuit/webrtc/p2p/12D3KooWNnGaN4Vdf2h1pLdUtwgSu1JZeVyaxCM7U4XRdqbi6AB8
2026-02-22 23:40:09,295 [INFO] [__main__]
2026-02-22 23:40:09,296 [INFO] [__main__] ✅ Stored relay address for 12D3KooWNnGaN4Vdf2h1pLdUtwgSu1JZeVyaxCM7U4XRdqbi6AB8 in Peer A's peerstore
2026-02-22 23:40:09,296 [INFO] [__main__]
2026-02-22 23:40:09,297 [INFO] [__main__] 🔌 Peer A dialing Peer B through relay: /ip4/127.0.0.1/tcp/58465/p2p/12D3KooWQkGc15oB3u9ho98BBuRPfEzEKCQ8XJBYtsrb6kb6sEfe/p2p-circuit/webrtc/p2p/12D3KooWNnGaN4Vdf2h1pLdUtwgSu1JZeVyaxCM7U4XRdqbi6AB8
2026-02-22 23:40:09,297 [INFO] [__main__] (This mimics: browser A clicking 'Connect' button)
2026-02-22 23:40:10,177 [INFO] [webrtc.private.initiate_connection] Initiating WebRTC connection to /ip4/127.0.0.1/tcp/58465/p2p/12D3KooWQkGc15oB3u9ho98BBuRPfEzEKCQ8XJBYtsrb6kb6sEfe/p2p-circuit/webrtc/p2p/12D3KooWNnGaN4Vdf2h1pLdUtwgSu1JZeVyaxCM7U4XRdqbi6AB8
2026-02-22 23:40:10,178 [INFO] [webrtc.private.initiate_connection] Circuit address: /ip4/127.0.0.1/tcp/58465/p2p/12D3KooWQkGc15oB3u9ho98BBuRPfEzEKCQ8XJBYtsrb6kb6sEfe/p2p-circuit/p2p/12D3KooWNnGaN4Vdf2h1pLdUtwgSu1JZeVyaxCM7U4XRdqbi6AB8, Target peer: 12D3KooWNnGaN4Vdf2h1pLdUtwgSu1JZeVyaxCM7U4XRdqbi6AB8
2026-02-22 23:40:10,186 [INFO] [webrtc.private.signaling_stream_handler] Handling incoming signaling stream for WebRTC connection
2026-02-22 23:40:10,188 [INFO] [webrtc.private.initiate_connection] Established signaling stream through circuit relay to 12D3KooWNnGaN4Vdf2h1pLdUtwgSu1JZeVyaxCM7U4XRdqbi6AB8
2026-02-22 23:40:10,189 [INFO] [webrtc.private.initiate_connection] Created RTCPeerConnection and negotiated 'init' data channel (id=0, state=connecting)
2026-02-22 23:40:15,199 [INFO] [webrtc.private.signaling_stream_handler] Received SDP offer (796 bytes)
2026-02-22 23:40:20,209 [INFO] [webrtc.private.signaling_stream_handler] Created and set local description (answer)
2026-02-22 23:40:20,210 [INFO] [webrtc.private.signaling_stream_handler] Sent SDP answer successfully
2026-02-22 23:40:20,224 [INFO] [webrtc.private.initiate_connection] Set remote description from answer
2026-02-22 23:40:20,289 [INFO] [webrtc.private.initiate_connection] WebRTC connection established successfully (connectionState=connected, iceConnectionState=completed)
2026-02-22 23:40:20,290 [INFO] [webrtc.private.initiate_connection] Init data channel opened
2026-02-22 23:40:20,790 [INFO] [webrtc.private.initiate_connection] ✅ Init channel already open - SCTP is ready
2026-02-22 23:40:20,894 [INFO] [webrtc.private.initiate_connection] Init data channel closed
2026-02-22 23:40:20,894 [INFO] [webrtc.private.initiate_connection] ✅ Init channel closed successfully
2026-02-22 23:40:21,096 [INFO] [webrtc.private.initiate_connection] 🚀 Creating new data channel - this should trigger datachannel event on answerer
2026-02-22 23:40:21,096 [INFO] [webrtc.private.initiate_connection] ✅ Created new data channel for communication (label='', id=None, initial state=connecting)
2026-02-22 23:40:21,097 [INFO] [webrtc.private.signaling_stream_handler] 🔔🔔🔔 datachannel event FIRED! Received channel from initiator: label='', state=open, id=1
2026-02-22 23:40:21,097 [INFO] [webrtc.private.signaling_stream_handler] ✅ Set data_channel_received event (channel label: '', state: open, id=1)
2026-02-22 23:40:21,252 [INFO] [webrtc.private.signaling_stream_handler] WebRTC connection established successfully (connectionState=connected, iceConnectionState=completed)
2026-02-22 23:40:21,252 [INFO] [webrtc.private.signaling_stream_handler] Received application data channel from initiator: label='', state=open
2026-02-22 23:40:21,253 [INFO] [webrtc.private.signaling_stream_handler] WebRTC connection established with ED25519 peer: 12D3KooWGRZq8DpUtcboDgWLChExUHFaUj7SZqtd6EiGEPBBBNCY (channel state: open, conn_state: connected)
🔵 Inbound Data Pump STARTED for 12D3KooWGRZq8DpUtcboDgWLChExUHFaUj7SZqtd6EiGEPBBBNCY
2026-02-22 23:40:21,354 [INFO] [webrtc.private.signaling_stream_handler] DTLS and SCTP verified before returning connection: DTLS=connected, SCTP=connected
2026-02-22 23:40:21,597 [INFO] [webrtc.private.initiate_connection] New data channel already open
2026-02-22 23:40:21,599 [INFO] [webrtc.private.initiate_connection] Successfully established WebRTC connection to 12D3KooWNnGaN4Vdf2h1pLdUtwgSu1JZeVyaxCM7U4XRdqbi6AB8 (channel state: open, conn_state: connected)
2026-02-22 23:40:21,599 [INFO] [webrtc.private.initiate_connection] Starting buffer consumer in async context for 12D3KooWNnGaN4Vdf2h1pLdUtwgSu1JZeVyaxCM7U4XRdqbi6AB8...
🔵 Inbound Data Pump STARTED for 12D3KooWNnGaN4Vdf2h1pLdUtwgSu1JZeVyaxCM7U4XRdqbi6AB8
2026-02-22 23:40:21,599 [INFO] [webrtc.private.initiate_connection] ✅ Buffer consumer is ready for 12D3KooWNnGaN4Vdf2h1pLdUtwgSu1JZeVyaxCM7U4XRdqbi6AB8 - messages will flow correctly
2026-02-22 23:40:21,701 [INFO] [webrtc.private.initiate_connection] DTLS and SCTP verified before returning connection: DTLS=connected, SCTP=connected
🔵 FIRST MESSAGE CONSUMED from buffer for 12D3KooWGRZq8DpUtcboDgWLChExUHFaUj7SZqtd6EiGEPBBBNCY
🔵 FIRST MESSAGE CONSUMED from buffer for 12D3KooWNnGaN4Vdf2h1pLdUtwgSu1JZeVyaxCM7U4XRdqbi6AB8
2026-02-22 23:40:21,726 [INFO] [__main__] ✅ WebRTC connection established through relay!
2026-02-22 23:40:21,726 [INFO] [__main__]
2026-02-22 23:40:21,726 [INFO] [__main__] 📤 Testing data exchange over WebRTC connection...
2026-02-22 23:40:21,739 [INFO] [__main__] 📤 Sending: Hello from Peer A!
2026-02-22 23:40:21,747 [INFO] [__main__] 📨 Echo handler received: Hello from Peer A!
2026-02-22 23:40:21,751 [INFO] [__main__] Received echo: Hello from Peer A!
2026-02-22 23:40:21,751 [INFO] [__main__]
2026-02-22 23:40:21,751 [INFO] [__main__] 📤 Sending: This is a test message
2026-02-22 23:40:21,755 [INFO] [__main__] 📨 Echo handler received: This is a test message
2026-02-22 23:40:21,760 [INFO] [__main__] Received echo: This is a test message
2026-02-22 23:40:21,760 [INFO] [__main__]
2026-02-22 23:40:21,760 [INFO] [__main__] 📤 Sending: WebRTC private-to-private works!
2026-02-22 23:40:21,763 [INFO] [__main__] 📨 Echo handler received: WebRTC private-to-private works!
2026-02-22 23:40:21,768 [INFO] [__main__] Received echo: WebRTC private-to-private works!
2026-02-22 23:40:21,768 [INFO] [__main__]
2026-02-22 23:40:21,768 [INFO] [__main__] 🧹 Cleaning up...
Unexpected error reading from stream:
Unexpected error reading from stream:
Unexpected error reading from stream:
Unexpected error reading from stream:
SCTP _set_state(CLOSED) BLOCKED on 4496692944 - current_state=None, peer_conn=unknown (cannot verify ownership, blocking to be safe)
2026-02-22 23:40:22,025 [WARNING] [webrtc.private.signaling_stream_handler] Connection state became closed
peer_connection.close() BLOCKED 4496457360 - handshake active; conn=connected ice=completed sctp=connected
DTLS _set_state(CLOSED) BLOCKED on 4418850688 - current_state=CONNECTED, peer_conn=unknown (cannot verify ownership, blocking to be safe)
SCTP _set_state(CLOSED) BLOCKED on 4496693184 - current_state=None, peer_conn=unknown (cannot verify ownership, blocking to be safe)
DTLS _set_state(CLOSED) BLOCKED on 4496691120 - current_state=CONNECTED, peer_conn=unknown (cannot verify ownership, blocking to be safe)
Error reading from WebRTC connection:
Traceback (most recent call last):
File "/Users/matrix/Documents/Season1/py-libp2p/libp2p/transport/webrtc/connection.py", line 1029, in read
data = await self.receive_channel.receive()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/matrix/Documents/Season1/py-libp2p/.venv/lib/python3.12/site-packages/trio/_channel.py", line 370, in receive
return await trio.lowlevel.wait_task_rescheduled(abort_fn) # type: ignore[no-any-return]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/matrix/Documents/Season1/py-libp2p/.venv/lib/python3.12/site-packages/trio/_core/_traps.py", line 214, in wait_task_rescheduled
return (await _async_yield(WaitTaskRescheduled(abort_func))).unwrap()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/matrix/Documents/Season1/py-libp2p/.venv/lib/python3.12/site-packages/outcome/_impl.py", line 213, in unwrap
raise captured_error
trio.EndOfChannel
Noise read_msg failed: Connection closed during read operation: expected 2 bytes but received 0 bytes
Traceback (most recent call last):
File "/Users/matrix/Documents/Season1/py-libp2p/libp2p/security/noise/io.py", line 68, in read_msg
noise_msg_encrypted = await self.read_writer.read_msg()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/matrix/Documents/Season1/py-libp2p/libp2p/io/msgio.py", line 60, in read_msg
length = await self.next_msg_len()
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/matrix/Documents/Season1/py-libp2p/libp2p/io/msgio.py", line 81, in next_msg_len
return await read_length(self.read_write_closer, self.size_len_bytes)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/matrix/Documents/Season1/py-libp2p/libp2p/io/msgio.py", line 37, in read_length
length_bytes = await read_exactly(reader, size_len_bytes)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/matrix/Documents/Season1/py-libp2p/libp2p/io/utils.py", line 74, in read_exactly
raise IncompleteReadError(
libp2p.io.exceptions.IncompleteReadError: Connection closed during read operation: expected 2 bytes but received 0 bytes
2026-02-22 23:40:22,232 [INFO] [__main__]
2026-02-22 23:40:22,232 [INFO] [__main__] ======================================================================
2026-02-22 23:40:22,232 [INFO] [__main__] ✅ All tests passed!
2026-02-22 23:40:22,232 [INFO] [__main__] ======================================================================
2026-02-22 23:40:22,232 [INFO] [__main__]
2026-02-22 23:40:22,232 [INFO] [__main__] This demonstrates that WebRTC private-to-private
2026-02-22 23:40:22,232 [INFO] [__main__] connections work correctly in py-libp2p.
2026-02-22 23:40:22,232 [INFO] [__main__]
2026-02-22 23:40:22,236 [INFO] [__main__] 🧹 Shutting down transports and hosts...1. Persistent Asyncio Loop (Connection Lifecycle)Issue: The asyncio loop was only active during short Fix: Use the same pattern as webrtc-direct:
Result: Loop stays active from 2. Handshake Timeouts / “Failed to Negotiate Secure Protocol”Issue: Fix: Run Result: The upgrade path runs with the asyncio loop active, so aiortc callbacks can deliver Noise handshake messages to the answerer’s 3. Data Pump Before UpgradeIssue: The answerer’s Fix:
Result: The data pump is running and ready before the upgrade, so the handshake can proceed. 4. Example Dial Path (Wrong Upgrade Path)Issue: The example used Fix: Use the host’s dial path:
Result: The dialer goes through the same upgrade path as the answerer (Noise + muxer). 5. Bridge Context for All WebRTC OperationsIssue: The loop holder runs in a separate system task. The dialer and answerer tasks did not inherit the bridge context, so Fix: Wrap all WebRTC operations in
Result: Each operation runs in the bridge context, so the asyncio loop is available and aiortc callbacks work. 6. Removed Temporary
|
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Hi @Nkovaturient — thank you for the very clear and structured breakdown of the WebRTC-Direct connection workflow. This is excellent debugging work. The step-by-step analysis (especially Steps 6–10) pinpoints the problem area very accurately and already narrows the issue to the correct abstraction boundary.
Wish to share some thoughts, pointers, and suggested next steps here so others can also follow and contribute.
High-level view: where muxer negotiation lives
Across libp2p implementations (Go / Rust / JS / Python), the upgrade pipeline is conceptually:
Important invariants:
Muxer negotiation is transport-agnostic
read()/write()making progressupgrade_connection()is the critical boundaryYour observation that
upgrade_connection()never returns is therefore the key signal.Why your diagnosis is likely correct
These signals you listed are especially important:
DataChannel is open, but:
read()callsMultiselectCommunicator.read/write()never invokedDialer and listener both waiting → classic multistream deadlock
Ownership event (
libp2p_owner_ready) never setThis strongly suggests not a Noise or DTLS issue, but a byte-flow / ownership / read-loop issue at the WebRTC transport → upgrader boundary.
In other words:
That’s why multistream-select never exchanges even the first message.
WebRTC-Direct–specific gotchas to check
From experience with WebRTC-based transports, the most common pitfalls are:
Read loop not started early enough
on_message/ read handler must be active before muxer negotiation beginsOwnership transfer gated too late
Writes buffered but never flushed
Close logic interfering with upgrade
Your Step 11–12 analysis captures this perfectly.
Where to look (concretely)
I’d recommend focusing on:
The DataChannel → RawConnection adapter
read()is implementedThe point where
upgrade_connection()is calledThe ownership handoff logic
libp2p_owner_readyshould not be blocked on muxer completionComparing with:
net.Connadapter)can be very helpful here, especially around when reads are scheduled.
I’d suggest:
Sharing your 12-step workflow as-is (it’s very clear)
Emphasizing that:
Framing it as an upgrade lifecycle / invariants issue, not TLS vs Noise
This will help align WebRTC-Direct behavior with expectations across transports.
Final note
You’re already past the “need more resources” stage — this is now about aligning implementation details with libp2p invariants. The analysis you’ve done here is exactly what’s needed to resolve this class of issue.
Thanks again for documenting this so clearly. Happy to continue reviewing code paths, logs, or a draft PR as you dig further — and would be great to collaborate closely with Luca on this as well.
🚀
CCing @acul71 and @asmit27rai, who will help you arrive at a good conclusion on getting webrtc ready for final review + merge at py-libp2p.
Beta Was this translation helpful? Give feedback.
All reactions