End-to-end encrypted P2P chat, correct successful your browser. No server, nary accounts, nary stored history.
by Hardlint Cybersecurity Team
This task is distributed for acquisition and information investigation purposes. It does not guarantee network-level anonymity: it protects the content of conversations, not needfully who is connecting. Read the Attack Surface and Known Limitations conception earlier utilizing it for delicate communications.
Use of a trustworthy VPN connected some devices is powerfully recommended.
- 🔐 End-to-end encryption — AES-GCM 256-bit, cardinal derived via PBKDF2 (100,000 iterations)
- 🌐 True P2P connection — nonstop WebRTC nexus betwixt the 2 devices, nary cardinal server relaying messages
- 🚫 Zero persistence — nary cookies, nary localStorage, nary database: adjacent the tab and thing remains
- 🔑 Single shared secret — a randomly generated Room Key (100 characters), nary manual method configuration required
- 🧹 Panic Purge — 1 fastener instantly wipes keys, relationship state, and visible chat history
- 📡 Reliable connectivity — 18 STUN/TURN servers configured arsenic fallbacks to activity moreover down restrictive NATs (4G/5G, firm networks)
- Open the page (must beryllium served complete HTTPS — e.g. via GitHub Pages, not opened arsenic a section file)
- Host: click [1] INITIALIZE ROOM → transcript the generated Room Key
- Send the Room Key to your interaction through a different channel (in person, sound call, different encrypted app)
- Guest: click [2] CONNECT TO ROOM → paste the received Room Key
- Wait for the relationship (usually a fewer seconds) → the chat opens
- If the relationship isn't established wrong 2 minutes, the Room Key expires automatically: make a caller 1 pinch the dedicated button
- Modern browser pinch WebRTC and Web Crypto API support (recent Chrome, Firefox, Edge, Safari)
- Internet entree connected some devices
- The page must beryllium served complete HTTPS (Secure Context is required for Web Crypto API and WebRTC) — it does not activity erstwhile opened arsenic a section file
- Both parties must person the page unfastened at the aforesaid time during the relationship attempt
- The Room Key must beryllium copied in full, precisely 100 characters, pinch nary other spaces aliases statement breaks
🏗️ Architectural Overview
Zero-Trace Terminal is simply a fixed web exertion (HTML/CSS/JS, nary proprietary backend) that allows 2 devices to found a direct peer-to-peer connection via WebRTC, exchanging end-to-end encrypted matter messages.
Main components:
| User interface | Retro terminal UI | Plain HTML/CSS |
| Signaling | Makes the 2 peers "find" each other | PeerJS (public unreality broker) |
| Data transport | Encrypted P2P channel | WebRTC DataChannel |
| NAT traversal | Punching done firewalls/NAT | STUN + TURN (ICE) |
| Encryption | Message contented protection | AES-GCM 256-bit + PBKDF2 |
| Hosting | Code distribution | GitHub Pages (static) |
There is nary proprietary exertion server: the codification runs wholly successful each user's browser. The only outer infrastructure progressive is utilized to "introduce" the 2 devices to each different (signaling) and, if needed, to relay postulation erstwhile a nonstop relationship isn't imaginable (TURN).
When a personification clicks "INITIALIZE ROOM (HOST)":
- A random 100-character drawstring is generated (generate100CharCode()), utilizing crypto.getRandomValues() — a cryptographically unafraid random number generator (not Math.random(), which is unsuitable for cryptographic purposes).
- The characteristic group includes uppercase/lowercase letters, digits, and typical symbols (-_!@#$%^&*), maximizing entropy wrong 100 characters.
This drawstring (the Room Key) is the only shared concealed the 2 parties request to exchange, out-of-band (e.g. sound message, successful person, different encrypted channel).
Deriving Keys from the Room Key
Two independent values, each pinch a different purpose, are derived from the Room Key:
A. Message encryption cardinal (PBKDF2 → AES-GCM)
B. PeerJS identifier (truncated SHA-256)
This ID is utilized solely truthful that Host and Guest tin "find" each different connected the PeerJS signaling broker, without exchanging thing beyond the Room Key. It plays nary cryptographic role.
Note connected the fixed salt: the PBKDF2 brackish is hardcoded and identical crossed each sessions. This is acceptable because the "password" (Room Key) already has very precocious entropy (100 random characters) — a fixed brackish only weakens information successful scenarios involving weak, reused passwords, which does not use here.
- The Host creates a Peer object, registering pinch the nationalist PeerJS unreality agent utilizing the ID derived from the Room Key.
- The Guest, aft pasting the aforesaid Room Key, computes the aforesaid ID and calls peer.connect(id).
- The PeerJS agent only mediates this first speech (who wants to talk to whom) — it ne'er sees aliases transmits connection content, which by that constituent travels complete a abstracted WebRTC channel.
ICE Negotiation (NAT Traversal)
Once the 2 Peers person "introduced" themselves, WebRTC starts ICE speech to find a valid web path:
- Host candidates — the device's section IP addresses
- Server-reflexive (srflx) candidates — nationalist IP discovered via STUN
- Relay candidates — allocated via TURN, utilized only if a nonstop relationship fails
Configured ICE servers (in privilege order):
- Dedicated Metered.ca TURN (own credentials, not shared) — stun.relay.metered.ca / global.relay.metered.ca
- 7 nationalist STUN fallbacks (Google ×3, Cloudflare, Twilio, Nextcloud, stunprotocol.org, freestun)
- 10 further nationalist TURN fallback endpoints (OpenRelay, freestun, numb.viagenie, ExpressTurn) — utilized only if the dedicated TURN besides fails
The browser automatically tries each operation and selects the first 1 that establishes a moving transmission (standard ICE algorithm, handled internally by WebRTC).
Timeout and Session Expiry
- If the relationship isn't established wrong 120 seconds, the convention is considered expired:
- The Peer and DataConnection are destroyed (peer.destroy(), conn.close())
- The position shows [EXPIRED] Room Key nary longer valid
- A fastener appears to make a caller Room Key (Host) aliases participate a caller 1 (Guest)
- This prevents a Room Key from remaining "listening" indefinitely connected the nationalist broker.
🔐 Message Cryptographic Model
Every connection is individually encrypted earlier being sent complete the DataChannel:
On receipt:
Security properties guaranteed by AES-GCM:
- Confidentiality — cipher without the cardinal tin publication the content
- Integrity/authenticity — immoderate tampering pinch the packet successful transit causes decryption to neglect ([ERR: DECRYPTION_FAILED]), alternatively than silently producing corrupted output
What this strategy does NOT cover:
- Forward secrecy crossed sessions — if the aforesaid Room Key were reused crossed aggregate sessions (not the normal flow, which generates a caller 1 each time), each those sessions would stock the aforesaid derived key
- Peer personality authentication — anyone who knows the Room Key tin connect; location is nary cryptographic verification of "who" is connected the different extremity beyond possession of the shared key
💾 Data Persistence (Client-Side)
| Room Key | None | Only successful a JS variable, gone connected close/reload |
| Derived AES-GCM key | None | Same, ne'er written to disk |
| Chat messages | None | Only unrecorded successful the DOM/RAM, nary localStorage/IndexedDB |
| Cookies | None | The task uses nary astatine all |
| Application logs | Local DevTools console only | Never sent anywhere, gone erstwhile the tab closes |
The "PANIC: PURGE SESSION" fastener explicitly forces:
- Closure of the PeerConnection/DataConnection
- Zeroing of the encryption cardinal successful memory
- Wiping of each UI fields and the displayed connection history
👁️ What External Infrastructure Can See (Metadata)
Key constituent to understand: encryption protects content, not relationship metadata.
| GitHub Pages | IP and timestamp of whoever loads the page | Message content, Room Key |
| PeerJS broker (public cloud) | IP of Host and Guest, erstwhile they connect, their Peer ID (a hash of the Room Key, not the Key itself) | Message content |
| Metered.ca TURN (if utilized arsenic relay) | Source/destination IP, ports, magnitude of information transferred | Message contented (already travels encrypted) |
| Each user's ISP | That a relationship is being made to github.io / metered.ca / a PeerJS server | Message content |
Recommended mitigation (outside the code): usage a trustworthy VPN (e.g. Mullvad, pinch an anonymously created account) connected some devices, to debar exposing existent IP addresses to these third-party services. The task displays an definitive informing to this effect connected the scatter screen.
🎯 Attack Surface and Known Limitations
| Message contented interception | MITM connected TURN/network traffic | ✅ Yes — end-to-end AES-GCM |
| Message tampering successful transit | Packet manipulation | ✅ Yes — GCM authentication tag |
| Deanonymization via IP metadata | IP↔identity relationship done third-party logs | ⚠️ Partial — requires a VPN client-side, not solved by the codification itself |
| Metered.ca relationship compromise | TURN credentials are successful the nationalist codification (base64-obfuscated, not encrypted) | ⚠️ Minimal deterrent, not existent security |
| Dependency connected third-party services | GitHub Pages, PeerJS broker, Metered TURN — if suspended, the app stops working | ⚠️ Not mitigated (would require afloat self-hosting) |
| Room Key reuse | Would discuss guardant secrecy crossed sessions that reuse it | ✅ Not applicable successful normal travel (a caller Key each session) |
- Frontend: HTML5, CSS3 (no framework)
- Cryptography: Browser-native Web Crypto API (crypto.subtle) — PBKDF2, AES-GCM, SHA-256
- P2P/Signaling: PeerJS v1.5.4 (a wrapper room complete autochthonal WebRTC), loaded from a nationalist CDN (unpkg.com)
- NAT Traversal: WebRTC ICE (STUN/TURN) — 18 endpoints configured successful total
- Hosting: GitHub Pages (static, automatic HTTPS)
- Browser requirements: WebRTC support, Web Crypto API, ES6+ — requires a unafraid discourse (HTTPS); does not activity from file:// aliases content://
📝 Changelog of Major Versions
- v1 — Native WebRTC pinch manual SDP speech (copy/paste offer/answer)
- v2 — Migrated to PeerJS, automatic relationship based connected the Room Key, removed manual SDP fields
- v3 — Added dedicated Metered.ca TURN + aggregate nationalist STUN/TURN fallbacks
- v4 — Detailed ICE diagnostics (candidate logging, relationship states) — fixed a bug that overwrote PeerJS's soul arena handlers
- v5 — Timeout extended to 120s, Room Key expiry strategy pinch manual regeneration, VPN informing connected scatter screen, TURN credential obfuscation
Hard-Chat is 100% free, open-source, and maintained by the Hardlint Cybersecurity Team. We don't tally ads and we don't waste data. If you judge successful our ngo and want to thief america money our early self-hosted infrastructure (custom STUN/TURN servers), see supporting us!
Solana (SOL) aid address:
This package is provided "as is", without warranties of immoderate kind. The developers are not responsible for immoderate improper aliases forbidden usage of this tool. Users are solely responsible for complying pinch applicable laws successful their jurisdiction.
This archive is provided for informational and method archiving purposes only. It does not represent ineligible proposal regarding regulatory compliance, privacy, aliases liability for use.
Hardlint Cybersecurity Team
English (US) ·
Indonesian (ID) ·