A fully decentralized, peer-to-peer video & text chat — meet strangers with real privacy.
Decengle is an anonymous video chat platform where you can talk to random strangers — like Omegle, but fully decentralized. There is no central server routing your video, audio, or messages. Everything flows directly between you and your match over encrypted WebRTC connections.
- Direct WebRTC media streams between peers — no relay servers
- Camera and microphone captured via the browser's native
getUserMediaAPI - All media encrypted at the transport level via DTLS-SRTP
- ECDH P-256 key exchange to derive a shared secret
- AES-GCM-256 encryption on every chat message
- Keys are never transmitted — only public keys are shared via the DHT
- Graceful fallback if E2EE derivation fails (transport encryption still active)
- Fully distributed peer discovery running on WebRTC data channels
- 160-bit peer IDs derived from SHA-256 of each peer's public key
- Peers announce their state (
idle,search,busy) into the DHT - DHT entries auto-expire (default: 60 seconds) to keep the network fresh
- Bootstrap DHT store is size-capped (default: 10,000 entries) with stale/offline-first eviction
- Data replicated across the K closest peers for resilience
- Discover searching peers via DHT lookups — no central matchmaker
- Match request/accept/decline flow handled entirely peer-to-peer
- Block list to prevent re-matching with specific users
- One-click Next to disconnect and instantly search for someone new
- A minimal Node.js WebSocket server for initial peer discovery only
- Bootstrap nodes peer with each other and share DHT state
- Once connected to the mesh, the bootstrap is no longer needed
- Fully configurable — add your own bootstrap nodes from the UI
- Custom bootstrap URLs persist in
localStorage - Per-connection message rate limiting and per-IP connection limits to prevent abuse
- Message schema validation rejects malformed payloads
- DHT persistence to disk across restarts (JSON file, configurable path)
- Structured JSON logging with configurable log levels
- Graceful shutdown on SIGTERM/SIGINT
- Pure vanilla JavaScript (ES6+), HTML5 and CSS3
- Web Crypto API for all cryptographic operations
- No frameworks, no bundlers, no build step — just open
index.html
┌─────────────┐ WebSocket ┌──────────────────┐
│ Browser │◄────── (discovery) ──────►│ Bootstrap Node │
│ │ └──────────────────┘
│ ┌────────┐ │
│ │ DHT │ │ WebRTC (P2P)
│ │Kademlia│ │◄═══════════════════════► Stranger's Browser
│ └────────┘ │ video / audio / chat
│ │ (DTLS-SRTP encrypted)
└─────────────┘
- Bootstrap — Your browser connects to a bootstrap node via WebSocket to discover peers
- DHT Join — Your peer ID and public key are announced into the distributed hash table
- Match — The matching service polls the DHT for peers in
searchstate - WebRTC — A direct peer connection is established (STUN for NAT traversal)
- Chat & Video — All data flows directly between browsers, encrypted end-to-end
cd bootstrap
npm install
npm startThe server starts on port 9000 by default.
| Environment Variable | Description |
|---|---|
PORT |
Server port (default: 9000) |
SELF_URL |
Public URL to advertise (e.g. wss://example.com) |
PEER_BOOTSTRAPS |
Comma-separated URLs of other bootstrap nodes to peer with |
LOG_LEVEL |
Logging verbosity: debug, info, warn, error (default: info) |
MAX_MESSAGES_PER_SEC |
Max WebSocket messages per second per connection (default: 30) |
MAX_CONNECTIONS_PER_IP |
Max concurrent connections per IP (default: 5) |
RATE_LIMIT_BAN_MS |
Temporary ban duration in ms when rate limit is exceeded (default: 60000) |
MAX_PAYLOAD_BYTES |
Max allowed WebSocket message size in bytes (default: 65536) |
DHT_MAX_ENTRIES |
Max DHT entries kept in bootstrap memory (default: 10000) |
DHT_ENTRY_TTL_MS |
DHT entry TTL in ms before cleanup/expiry (default: 60000) |
DHT_PERSIST_PATH |
JSON file path used to persist bootstrap DHT data (default: bootstrap/dht-store.json) |
DHT_PERSIST_DEBOUNCE_MS |
Debounce interval for DHT disk writes in ms (default: 1000) |
Open index.html in your browser. That's it — no build step required.
- Click Start to enable your camera and begin searching
- Wait for a match (or add more bootstrap nodes to grow the network)
- Chat via video and text — encrypted end-to-end
- Click Next to find someone new, or Stop to disconnect
| Layer | Protection | Always Active? |
|---|---|---|
| Media (video/audio) | DTLS-SRTP | Yes |
| Data channel transport | DTLS | Yes |
| Chat message content | AES-GCM-256 (ECDH-derived key) | When both peers' public keys are in the DHT |
- No TURN servers — your traffic never passes through a relay. Either a direct connection is established, or the connection doesn't happen.
- Peer IDs are derived from the SHA-256 hash of your public key — no accounts, no usernames, no tracking.
- Keys are ephemeral — a new ECDH keypair is generated every session.
| Component | Technology |
|---|---|
| Frontend | Vanilla JS, HTML5, CSS3 |
| Crypto | Web Crypto API (ECDH P-256, AES-GCM-256, SHA-256) |
| Networking | WebRTC (media + data channels) |
| Peer Discovery | Kademlia DHT over WebRTC |
| Bootstrap Server | Node.js + ws |
| Storage | Browser localStorage, bootstrap JSON file persistence |
Decengle/
├── index.html # Single-page app
├── Style/
│ └── style.css # Dark theme UI
├── Scripts/
│ ├── app.js # Main entry — wires everything together
│ ├── chat.js # E2EE text chat module
│ ├── crypto.js # ECDH key exchange, AES-GCM, peer ID generation
│ ├── dht.js # Kademlia DHT implementation
│ ├── matching.js # Random peer matching service
│ ├── state.js # Local state (favorites, blocks, history)
│ ├── userWebcamService.js # Camera/mic access
│ └── webrtc.js # WebRTC connection manager
└── bootstrap/
├── package.json
└── server.js # Lightweight WebSocket bootstrap node
This project is open source. Use it, fork it, run your own node.