Skip to main content

๐Ÿ’ฌ Meshcore โ€” P2P Chat

๐Ÿงช View animated test results โ€” 22/22 โ†’

Serverless peer-to-peer chat with an automatic relay fallback. It runs on static hosting (GitHub Pages) โ€” zero servers, zero databases, zero accounts.

Try it now: ๐Ÿš€ Open Meshcore (opens in a new tab)

How it worksโ€‹

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” WebRTC data channel โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Peer A โ”‚โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บโ”‚ Peer B โ”‚
โ”‚ (your tab) โ”‚ (direct, P2P) โ”‚ (their tab) โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜
โ”‚ SDP offer/answer/ICE via โ”‚
โ”‚ public MQTT topic (signaling only) โ”‚
โ–ผ โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Public MQTT broker (per-room topic) โ”‚
โ”‚ presence + signaling + fallback for messages โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Layer 1 โ€” P2P (WebRTC)โ€‹

  1. Peers announce their presence on a per-room MQTT topic (public broker, no account โ€” the same way messengers discover each other).
  2. When two peers see each other, they exchange WebRTC offer/answer/ICE candidates through that topic (the classic signaling-server pattern used by PeerJS & co.). The broker only introduces peers โ€” it never sees message payloads.
  3. Once connected, chat messages travel over the WebRTC data channel directly between browsers โ€” real peer-to-peer.

Layer 2 โ€” Relay (automatic fallback)โ€‹

Pure browser P2P cannot connect on mobile networks: carriers use CGNAT and block NAT hole-punching, and the free public TURN servers that used to bridge it are dead or paid (2026). That is why every message is also published in parallel to a per-room topic on a public MQTT broker (WSS, no account). Every receiver deduplicates by message ID, so:

  • peers that can connect P2P โ†’ messages travel directly over the WebRTC data channel,
  • devices that cannot (phones on LTE/5G) โ†’ exchange messages via the relay.

P2P first, relay as a fallback โ€” the same pattern real messengers use.

Why not WebTorrent trackers? The original build found peers through public WebTorrent WebSocket trackers (tracker.webtorrent.dev, tracker.openwebtorrent.com). Those trackers now accept announces and see the swarm, but no longer relay WebRTC offers between peers (verified live: two peers registered in the same swarm, zero offers ever returned). Since the browser build of WebTorrent can only use WebSocket trackers (no UDP/DHT in the browser), peers could never find each other โ€” so signaling moved to the MQTT relay.

Featuresโ€‹

  • ๐Ÿ”— Rooms โ€” the same room name = the same signaling topic = the same peer group
  • ๐Ÿ“ก Relay fallback โ€” a dependency-free MQTT 3.1.1 client (~100 lines, no libraries), publishing to n2mesh/{room}, room isolation by exact topic
  • ๐Ÿ”€ Deduplication by ID โ€” a message received over P2P and relay and the local bridge is displayed once
  • ๐Ÿ’ฌ True P2P messages โ€” over WebRTC data channels, with MQTT fallback
  • ๐Ÿท๏ธ Nicknames, peer counter, connection status (P2P / P2P+relay / relay)
  • ๐Ÿ”— Shareable room links (#/room-name)
  • ๐ŸŒ™ Dark, keyboard-accessible interface, zero dependencies (no CDN, no build)
  • ๐Ÿ–ฅ๏ธ Local bridge โ€” tabs of the same browser cannot connect via WebRTC (browsers block WebRTC loopback), so a BroadcastChannel bridge connects them locally

How to use itโ€‹

  1. Open Meshcore on two devices (or two tabs of one browser).
  2. Set the same room on both sides (default: lobby).
  3. Pick a nickname and send messages โ€” on a desktop they travel P2P, on a phone automatically via the relay. Both cases work from the same link.

โš ๏ธ This is a demo-grade mesh: scrollback is cached per room in your browser (last 100 messages), but there is no server-side history โ€” messages sent while you are offline are lost (no broker-side queueing).

Run locallyโ€‹

git clone https://github.com/BartoszOsiej/Meshcore.git
cd n2-mesh
python3 -m http.server 8080
# open http://localhost:8080

Securityโ€‹

  • P2P mode: messages travel exclusively peer-to-peer; the MQTT broker only performs signaling and never receives content.
  • Relay mode: messages pass through a public MQTT broker โ€” a fallback for networks where P2P is impossible. Rooms are isolated (each subscriber only gets its own topic).
  • Everything is plain JavaScript (zero dependencies โ€” no CDN), no tracking, no data stored.

Moreโ€‹