Skip to content

SubhodeepSamanta/Mesh

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

89 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

mesh logo

mesh

Serverless, end-to-end encrypted, peer-to-peer file sharing β€” in your browser and your terminal.

Files travel machine-to-machine. No uploads. No accounts. No file ever touches a server.

npm live demo runtime dependencies node tests license PRs welcome

🌐 Try the web app Β· πŸ“¦ npm i -g mesh-share Β· 🧠 How it works


✨ Two clients, one philosophy

Servers coordinate. Peers transfer. The rendezvous infrastructure only ever sees tiny control messages; when a relay is unavoidable it forwards ciphertext it cannot read. File content exists on exactly two machines: yours and theirs.

🌐 Web β€” mesh-share.vercel.app ⌨️ CLI β€” mesh-share on npm
Share via 4-letter room code + QR Self-contained 84-char share code
Transport WebRTC DataChannel (DTLS 1.3) From-scratch stack: Kademlia DHT Β· STUN Β· TURN Β· reliable-UDP
Peer discovery Room on signaling server Distributed hash table (BitTorrent-style)
Encryption DTLS (built into WebRTC) X25519 ECDH β†’ HKDF β†’ AES-256-GCM (hand-wired)
Integrity SHA-256 chunks + Merkle proofs SHA-256 chunks + Merkle proofs
Install nothing β€” open a tab npm install -g mesh-share
# the entire CLI experience:
you:          mesh send movie.mp4
              β†’ AIB2Z-ROQMU-H2DQF-ILAA5-...

anyone, anywhere:  mesh receive AIB2ZROQMUH2DQFILAA5...
              β†’ movie.mp4  βœ” 500/500 chunks verified

πŸ“Έ Screenshots

Homepage Send β€” room code & QR
Landing page Sending β€” share the code or the QR
Transfer dashboard Streaming video mid-download
Live transfer β€” chunk grid & speed Watch a video while it downloads (MSE)
CLI send CLI receive
mesh send from a terminal mesh receive β€” direct or relayed, always verified

πŸš€ Quick start

Web

Open mesh-share.vercel.app β†’ drop a file β†’ share the code. That's it.

CLI

npm install -g mesh-share

mesh send ./anything.zip        # zero flags β€” prints a share code
mesh receive <SHARE-CODE>       # on any machine, any network
mesh diagnose                   # what can your NAT do?

🧠 How it works

The web client

sequenceDiagram
    participant A as Sender (browser)
    participant S as Signaling server
    participant B as Receiver (browser)
    A->>S: CREATE_ROOM β†’ code "WLF4" (+ 24h TURN credentials)
    B->>S: JOIN_ROOM "WLF4"
    A-->>B: SDP offer/answer + ICE candidates (relayed, opaque to server)
    A->>B: WebRTC DataChannel β€” DTLS encrypted, direct when possible
    Note over A,B: chunks + Merkle proofs, verified piece-by-piece
Loading

The CLI β€” a from-scratch P2P stack (zero runtime dependencies)

flowchart LR
    subgraph SEND["mesh send"]
        IDX["SHA-256 chunks<br/>+ Merkle tree"] --> SEED["chunk server<br/>+ TURN allocation"]
        SEED --> ANN["announce on<br/>Kademlia DHT"]
    end
    ANN -. "share code<br/>(addresses + content hash)" .-> DISC
    subgraph RECV["mesh receive"]
        DISC["DHT lookup<br/>O(log N)"] --> LADDER{"connection<br/>ladder"}
        LADDER -->|"tier 1"| DIRECT["direct TCP<br/>(LAN / UPnP / public)"]
        LADDER -->|"tier 2"| RELAY["TURN relay-to-relay<br/>(ciphertext only)"]
        DIRECT --> VERIFY["decrypt Β· hash Β·<br/>Merkle-verify Β· write"]
        RELAY --> VERIFY
    end
Loading

Every layer of the CLI's network stack is implemented by hand in packages/engine:

  • Kademlia DHT β€” XOR-metric routing, k-buckets, iterative O(log N) lookups, 90s liveness TTL (dht.js)
  • STUN client β€” RFC 5389 binding, XOR-MAPPED-ADDRESS decoding (net/stun.js)
  • TURN client β€” RFC 5766 allocations, permissions, HMAC credentials (net/turn.js)
  • UPnP β€” SSDP discovery + SOAP port mapping (net/upnp.js)
  • Reliable-UDP β€” sliding-window ARQ with retransmit, reordering, backpressure (net/reliableDatagram.js)
  • E2E crypto β€” ephemeral X25519 β†’ HKDF β†’ AES-256-GCM per connection (crypto.js)
  • Merkle verification β€” every chunk ships a logβ‚‚(N)-hash proof chained to the share code's root
  • Multi-peer swarm β€” up to 30 seeders, pipelined requests, misbehaving peers evicted after 5 strikes (swarm.js)
  • Self-healing transfers β€” retransmits back off exponentially through mobile-network stalls, and if every connection drops the receiver re-discovers peers via the DHT and continues from the last verified chunk (transfer.js)
  • Pause/resume β€” Ctrl+C checkpoints to a .meshstate sidecar; re-run the same command to continue

πŸ“Š By the numbers

Runtime dependencies in the published CLI 0
Published package 3 files, ~500 kB (single esbuild bundle)
Automated tests across engine / CLI / signaling 180+ (including a seeder killed and resurrected mid-transfer)
Connection tiers direct TCP β†’ TURN relay (hole-punch tier on the roadmap)
Chunk size adaptive 64 kB β†’ 32 MB (≀ ~50k chunks per file)
Merkle proof per chunk logβ‚‚(N) hashes β€” ~300 bytes for a 500-chunk file
Largest real-world web transfer tested 1.8 GB (streamed to disk, no RAM blow-up)
Encryption X25519 ECDH + HKDF-SHA256 + AES-256-GCM Β· DTLS 1.3 on web

πŸ—‚ Monorepo

packages/
β”œβ”€β”€ engine/      the from-scratch P2P stack (DHT, STUN, TURN, UPnP, reliable-UDP,
β”‚                Merkle, swarm, resume) β€” zero dependencies, 19 test suites
β”œβ”€β”€ cli/         `mesh` command β€” published to npm as mesh-share
β”œβ”€β”€ signaling/   WebSocket rooms + TURN credential minting (Docker)
└── web/         React 19 + Vite + zustand client β€” deployed on Vercel
screenshots/     images used by the READMEs
docker-compose.yml   signaling + Caddy (TLS) + coturn β€” one command deploys the backend

πŸ–₯ Self-hosting the backend

Everything server-side runs from one compose file on any VM:

cp .env.example .env        # set EXTERNAL_IP, PRIVATE_IP, TURN_SECRET, TURN_REALM
docker compose up -d        # signaling + Caddy TLS + coturn (host networking)
nohup mesh daemon &         # DHT bootstrap node (or install it as a systemd unit)

Open these inbound ports: 80,443/tcp (TLS), 8080/tcp (signaling), 3478/udp+tcp + 49160-49200/udp (TURN), 4001/udp (DHT bootstrap). Point clients at your box with --bootstrap <ip>:4001 --turn-host <ip> --turn-secret <secret> or the MESH_* env vars β€” the defaults baked into the npm package are just a public courtesy instance.

Why does a P2P app have servers at all? The same reason BitTorrent ships bootstrap routers and Tailscale runs DERP relays: something public must introduce two hidden machines, and when both peers are behind hostile NAT, physics requires a relay. Mesh's relay forwards ciphertext only, and every layer prefers a direct path first.

πŸ§ͺ Testing

npm test                    # all workspaces
npm test -w packages/engine # loopback DHT meshes, swarm failure injection,
                            # TURN message encoding, resume, reliable-UDP
npm test -w packages/cli    # spawns the real binary end-to-end

180+ tests β€” including a network-blackout survival test and one that kills the only seeder mid-transfer, resurrects it, and asserts the file still arrives byte-identical β€” plus real-world verification: cross-continent transfers (India ↔ Azure), CGNAT and mobile-hotspot relay paths, and fresh-machine installs from the public registry.

πŸ—Ί Roadmap

  • UDP hole punching for the CLI β€” direct NAT↔NAT connections with TURN as last resort (the web already gets this via ICE)
  • Multiple default bootstrap nodes, raced in parallel
  • Share-code v3: IPv6 candidates
  • Skip the download entirely when the output file already matches the Merkle root ("you already have this file")
  • AIMD congestion control in the reliable-UDP layer
  • Authenticated key exchange bound to the share code

🀝 Contributing

Mesh is open source and PRs are welcome β€” the codebase is deliberately dependency-light and every protocol layer is readable in one sitting. Start with packages/engine/src/net/connect.js (the connection ladder) to get oriented, run npm test, and open an issue or PR.

πŸ“„ License

ISC Β© Subhodeep Samanta

Releases

Packages

Contributors

Languages