首页 > AI前沿 > Hard-Chat – A serverless, RAM-only P2P terminal chat

Hard-Chat – A serverless, RAM-only P2P terminal chat

Hacker News 2026-09-07 07:59 2 阅读 查看原文
🔒 Zero-Trace Terminal End-to-end encrypted P2P chat, right in your browser. No server, no accounts, no stored history. End-to-end encrypted P2P chat, right in your browser. No server, no accounts, no stored history. by Hardlint Cybersecurity Team ⚠️ Important Notice This project is distributed for educational and security research purposes. It does not guarantee network-level anonymity: it protects the content of conversations, not necessarily who is connecting. Read the Attack Surface and Known Limitations section before using it for sensitive communications. Use of a trustworthy VPN on both devices is strongly recommended. ✨ Features 🔐 End-to-end encryption — AES-GCM 256-bit, key derived via PBKDF2 (100,000 iterations) 🌐 True P2P connection — direct WebRTC link between the two devices, no central server relaying messages 🚫 Zero persistence — no cookies, no localStorage, no database: close the tab and nothing remains 🔑 Single shared secret — a randomly generated Room Key (100 characters), no manual technical configuration required 🧹 Panic Purge — one button instantly wipes keys, connection state, and visible chat history 📡 Reliable connectivity — 18 STUN/TURN servers configured as fallbacks to work even behind restrictive NATs (4G/5G, corporate networks) 🚀 How to Use Open the page (must be served over HTTPS — e.g. via GitHub Pages, not opened as a local file) Host: click [1] INITIALIZE ROOM → copy the generated Room Key Send the Room Key to your contact through a different channel (in person, voice call, another encrypted app) Guest: click [2] CONNECT TO ROOM → paste the received Room Key Wait for the connection (usually a few seconds) → the chat opens If the connection isn't established within 2 minutes, the Room Key expires automatically: generate a new one with the dedicated button 📋 Requirements Modern browser with WebRTC and Web Crypto API support (recent Chrome, Firefox, Edge, Safari) Internet access on both devices The page must be served over HTTPS (Secure Context is required for Web Crypto API and WebRTC) — it does not work when opened as a local file Both parties must have the page open at the same time during the connection attempt The Room Key must be copied in full, exactly 100 characters, with no extra spaces or line breaks 🏗️ Architectural Overview Zero-Trace Terminal is a static web application (HTML/CSS/JS, no proprietary backend) that allows two devices to establish a direct peer-to-peer connection via WebRTC, exchanging end-to-end encrypted text messages. Main components: There is no proprietary application server: the code runs entirely in each user's browser. The only external infrastructure involved is used to "introduce" the two devices to each other (signaling) and, if needed, to relay traffic when a direct connection isn't possible (TURN). 🔄 Operational Flow Room Key Generation When a user clicks "INITIALIZE ROOM (HOST)": A random 100-character string is generated (generate100CharCode()), using crypto.getRandomValues() — a cryptographically secure random number generator (not Math.random(), which is unsuitable for cryptographic purposes). The character set includes uppercase/lowercase letters, digits, and special symbols (-_!@#$%^&*), maximizing entropy within 100 characters. This string (the Room Key) is the only shared secret the two parties need to exchange, out-of-band (e.g. voice message, in person, another encrypted channel). Deriving Keys from the Room Key Two independent values, each with a different purpose, are derived from the Room Key: A. Message encryption key (PBKDF2 → AES-GCM) PBKDF2( password = Room Key, salt = "p2p-zero-trace-salt-v1" (fixed, hardcoded), iterations = 100,000, hash = SHA-256 ) → 256-bit AES-GCM key B. PeerJS identifier (truncated SHA-256) SHA-256(Room Key) → first 32 hex characters, prefixed with "ztt-" This ID is used solely so that Host and Guest can "find" each other on the PeerJS signaling broker, without exchanging anything beyond the Room Key. It plays no cryptographic role. Note on the fixed salt: the PBKDF2 salt is hardcoded and identical across all sessions. This is acceptable because the "password" (Room Key) already has very high entropy (100 random characters) — a fixed salt only weakens security in scenarios involving weak, reused passwords, which does not apply here. Note on the fixed salt: the PBKDF2 salt is hardcoded and identical across all sessions. This is acceptable because the "password" (Room Key) already has very high entropy (100 random characters) — a fixed salt only weakens security in scenarios involving weak, reused passwords, which does not apply here. Signaling Phase (PeerJS) The Host creates a Peer object, registering with the public PeerJS cloud broker using the ID derived from the Room Key. The Guest, after pasting the same Room Key, computes the same ID and calls peer.connect(id). The PeerJS broker only mediates this initial exchange (who wants to talk to whom) — it never sees or transmits message content, which by that point travels over a separate WebRTC channel. ICE Negotiation (NAT Traversal) Once the two Peers have "introduced" themselves, WebRTC starts ICE negotiation to find a valid network path: Host candidates — the device's local IP addresses Server-reflexive (srflx) candidates — public IP discovered via STUN Relay candidates — allocated via TURN, used only if a direct connection fails Configured ICE servers (in priority order): Dedicated Metered.ca TURN (own credentials, not shared) — stun.relay.metered.ca / global.relay.metered.ca 7 public STUN fallbacks (Google ×3, Cloudflare, Twilio, Nextcloud, stunprotocol.org, freestun) 10 additional public TURN fallback endpoints (OpenRelay, freestun, numb.viagenie, ExpressTurn) — used only if the dedicated TURN also fails The browser automatically tries every combination and selects the first one that establishes a working channel (standard ICE algorithm, handled internally by WebRTC). Timeout and Session Expiry If the connection isn't established within 120 seconds, the session is considered expired: The Peer and DataConnection are destroyed (peer.destroy(), conn.close()) The status shows [EXPIRED] Room Key no longer valid A button appears to generate a new Room Key (Host) or enter a new one (Guest) The Peer and DataConnection are destroyed (peer.destroy(), conn.close()) The status shows [EXPIRED] Room Key no longer valid A button appears to generate a new Room Key (Host) or enter a new one (Guest) This prevents a Room Key from remaining "listening" indefinitely on the public broker. 🔐 Message Cryptographic Model Every message is individually encrypted before being sent over the DataChannel: 1. Generate a random 12-byte IV (crypto.getRandomValues) 2. ciphertext = AES-GCM-Encrypt(key, IV, plaintext) 3. payload = IV || ciphertext (concatenated, IV in plaintext at the front) 4. Send payload as a Uint8Array via conn.send() On receipt: 1. Extract the first 12 bytes as the IV 2. The rest is the ciphertext (includes the 16-byte GCM authentication tag at the end) 3. plaintext = AES-GCM-Decrypt(key, IV, ciphertext) Security properties guaranteed by AES-GCM: Confidentiality — nobody without the key can read the content Integrity/authenticity — any tampering with the packet in transit causes decryption to fail ([ERR: DECRYPTION_FAILED]), rather than silently producing corrupted output What this scheme does NOT cover: Forward secrecy across sessions — if the same Room Key were reused across multiple sessions (not the normal flow, which generates a new one every time), all those sessions would share the same derived key Peer identity authentication — anyone who knows the Room Key can connect; there is no cryptographic verification of "who" is on the other end beyond possession of the shared key 💾 Data Persistence (Client-Side) The "PANIC: PURGE SESSION" button explicitly forces: Closure of the PeerConnection/DataConnection Zeroing of the encryption key in memory Wiping of all UI fields and the displayed message history 👁️ What External Infrastructure Can See (Metadata) Key point to understand: encryption protects content, not connection metadata. Recommended mitigation (outside the code): use a trustworthy VPN (e.g. Mullvad, with an anonymously created account) on both devices, to avoid exposing real IP addresses to these third-party services. The project displays an explicit warning to this effect on the splash screen. 🎯 Attack Surface and Known Limitations 🛠️ Full Technology Stack 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 library over native WebRTC), loaded from a public CDN (unpkg.com) NAT Traversal: WebRTC ICE (STUN/TURN) — 18 endpoints configured in total Hosting: GitHub Pages (static, automatic HTTPS) Browser requirements: WebRTC support, Web Crypto API, ES6+ — requires a secure context (HTTPS); does not work from file:// or content:// 📝 Changelog of Major Versions v1 — Native WebRTC with manual SDP exchange (copy/paste offer/answer) v2 — Migrated to PeerJS, automatic connection based on the Room Key, removed manual SDP fields v3 — Added dedicated Metered.ca TURN + multiple public STUN/TURN fallbacks v4 — Detailed ICE diagnostics (candidate logging, connection states) — fixed a bug that overwrote PeerJS's internal event handlers v5 — Timeout extended to 120s, Room Key expiry system with manual regeneration, VPN warning on splash screen, TURN credential obfuscation 💚 Support the Project Hard-Chat is 100% free, open-source, and maintained by the Hardlint Cybersecurity Team. We don't run ads and we don't sell data. If you believe in our mission and want to help us fund our future self-hosted infrastructure (custom STUN/TURN servers), consider supporting us! Solana (SOL) donation address: GSsqZCtDC7rf53U6gC5cJ4weAYYT9g7twxz9t15mfRDV 📜 License and Disclaimer This software is provided "as is", without warranties of any kind. The developers are not responsible for any improper or illegal use of this tool. Users are solely responsible for complying with applicable laws in their jurisdiction. This document is provided for informational and technical documentation purposes only. It does not constitute legal advice regarding regulatory compliance, privacy, or liability for use. Hardlint Cybersecurity Team