Zero-Knowledge End-to-End Encrypted Private Messenger
Engineered with Apple iMessage Liquid Glass aesthetics, native W3C Web Cryptography, and real-time WebSockets.
- Overview
- Zero-Knowledge Architecture
- Key Features
- Cryptographic Specification
- System Architecture
- Project Directory Structure
- Getting Started
- Configuration & Environment
- Database Persistence Options
- API & Realtime Reference
- Threat Model & Security Matrix
- Automated Verification Suite
- Mobile Installation (PWA)
- Production Deployment
- License
freeChat is an open-source, Zero-Knowledge End-to-End Encrypted (E2EE) messaging platform built to combine uncompromising mathematical privacy with the refined design language of Apple iMessage Liquid Glass.
Unlike traditional web messengers that rely on third-party JavaScript crypto libraries or server-side decryption, freeChat executes all cryptographic operations directly inside the browser using the hardware-accelerated W3C Web Cryptography API (window.crypto.subtle).
- The Server & Database Are Untrusted: The backend and database only ever receive ciphertext, public keys, and cryptographic initialization vectors (IVs).
- Zero Plaintext Transmission: Messages, master keys, and private keys never leave the client device unencrypted.
- Data at Rest Protection: Even in the event of a full server infrastructure compromise or database breach, stored messages cannot be decrypted by an attacker.
- Hardware-Accelerated Web Crypto: Native browser
ECDH (P-256)ephemeral key agreement combined withAES-256-GCMmessage encryption. - Zero-Knowledge Key Derivation: User master keys derived via
PBKDF2-HMAC-SHA256(100,000 iterations) with CSPRNG per-user salts. - Anti-Pass-the-Hash Verifiers: Client authentication tokens are hashed server-side with HMAC-SHA256 (
v2$) and compared using timing-safe equality checks. - In-Memory Key Hardening: Private keys are loaded as non-extractable (
extractable: false), preventing memory dumping via cross-site scripting (XSS). - Safety Numbers & Fingerprints: Signal/WhatsApp standard 60-digit symmetric safety numbers and 16-character hex fingerprints to detect Man-in-the-Middle (MITM) attacks.
- Frosted Glass Panels: Deep blur backdrop filters (
backdrop-filter: blur(25px)), translucent floating headers, and multi-layered specular borders. - Authentic iMessage Bubbles: Vibrant iOS blue sender bubbles with directional tails, adaptive typography, and smooth spring physics.
- Dynamic Themes: Seamless light and dark mode with reactive ambient background mesh lighting and iOS status bar tinting.
- Interactive Feedback: Real-time typing capsule (
...), online presence indicators, and animated message delivery states.
- Realtime WebSockets: Low-latency Socket.IO delivery with JWT handshake authentication and room-level authorization checks.
- Dual-Mode Database Adapter: Instant offline development via the built-in local JSON file store (
database/local_db.json), with seamless one-switch migration to Supabase PostgreSQL. - Installable PWA: Service-worker caching and web app manifest for a full-screen, standalone app experience on iOS and Android.
- 100% Free Cloud Stack: Permanently deployable on Render Free Tier (Node.js) paired with Supabase Free Tier (PostgreSQL).
| Component | Standard / Algorithm | Configuration | Security Purpose |
|---|---|---|---|
| Key Agreement | ECDH (NIST P-256 / secp256r1) | W3C Web Crypto P-256 curve |
Derives shared 256-bit symmetric secrets between participants without transmitting keys over the wire |
| Message Cipher | AES-256-GCM | 256-bit key, 96-bit (12-byte) random IV | Authenticated encryption providing confidentiality and integrity for chat payloads |
| Master Key (KEK) | PBKDF2-HMAC-SHA256 | 100,000 iterations, 16-byte CSPRNG salt | Derives the Key Encryption Key used to encrypt and decrypt the user's private key backup |
| Auth Verifier | PBKDF2 + Server HMAC-SHA256 | 100,000 iterations, :auth domain separation |
Proves password knowledge to server without sending the raw password or master key |
| Client Key Vault | AES-256-GCM + IndexedDB | Session vault key in sessionStorage |
Encrypts private keys at rest on the client; synced across tabs via BroadcastChannel |
| Runtime Hardening | Non-Extractable CryptoKey |
extractable: false |
Prevents JavaScript runtime extraction of raw private key coordinates (d) |
| Identity Verification | SHA-512 Canonical Key Digest | 60 decimal digits (12 Γ 5 blocks) + 16-char hex | Symmetrically computed safety number to verify contact keys and defeat MITM attacks |
-
Key Agreement: When Alice initiates a chat with Bob, Alice's browser retrieves Bob's public ECDH key. Using Bob's public key and Alice's private key, the browser computes a shared secret:
Shared Secret K = ECDH(Alice_PrivateKey, Bob_PublicKey) -
Authenticated Encryption: For every message, a cryptographically secure 12-byte initialization vector (
IV) is generated viawindow.crypto.getRandomValues(). The plaintext is encrypted usingAES-256-GCM:Ciphertext C, Tag T = AES-256-GCM-Encrypt(K, IV, Plaintext) -
Transmission: Alice transmits
{ conversationId, ciphertext: C, iv: IV }through Socket.IO. The server stores and relays only the ciphertext and IV. -
Decryption: Bob computes the identical shared secret
$K = \text{ECDH}(\text{Bob_PrivateKey}, \text{Alice_PublicKey})$ and decrypts the payload in browser memory.
When freeChat imports the user's private key into browser memory, it specifies { extractable: false }. The browser's native C++ crypto implementation prohibits any JavaScript code from calling crypto.subtle.exportKey() on that key object. Even in the event of an injected script or malicious extension, the raw private key cannot be exfiltrated from memory.
To ensure that no rogue server or proxy has substituted a contact's public key:
- Both users' public keys are canonically formatted (sorted coordinates
crv,kty,x,y). - The keys are sorted lexicographically and hashed with
SHA-512. - The output is formatted into 12 blocks of 5 decimal digits (60 digits total) matching the Signal / WhatsApp standard.
- If both parties compare safety numbers and confirm they match, active MITM eavesdropping is mathematically impossible.
- If a contact's key changes in the database, freeChat revokes the verified checkmark and presents an immediate Security Alert Banner.
flowchart LR
subgraph ClientA["Client A (Browser / PWA)"]
UI_A["iMessage UI"] --> WebCrypto_A["Web Crypto API\n(ECDH + AES-256-GCM)"]
WebCrypto_A --> Vault_A[("IndexedDB Vault\n(AES-GCM Encrypted)")]
end
subgraph Infrastructure["freeChat Application Server (Untrusted)"]
Server["Node.js / Express Gateway\n& Socket.IO Realtime"]
DBAdapter{"Universal DB Adapter"}
LocalStore[("Local JSON File\ndatabase/local_db.json")]
CloudStore[("Supabase Cloud\nPostgreSQL")]
Server --> DBAdapter
DBAdapter -->|Offline / Default| LocalStore
DBAdapter -->|Configured| CloudStore
end
subgraph ClientB["Client B (Browser / PWA)"]
WebCrypto_B["Web Crypto API\n(ECDH + AES-256-GCM)"] --> UI_B["iMessage UI"]
WebCrypto_B --> Vault_B[("IndexedDB Vault\n(AES-GCM Encrypted)")]
end
WebCrypto_A == "Ciphertext + IV (E2EE)" ==> Server
Server == "Ciphertext + IV (E2EE)" ==> WebCrypto_B
freeChat/
βββ database/
β βββ schema.sql # PostgreSQL production schema & RLS policies
β βββ local_db.json # Local JSON store (auto-generated for offline mode)
βββ public/ # Frontend SPA client
β βββ css/
β β βββ variables.css # Design tokens, color palette, spring curves
β β βββ glass.css # Glassmorphism, backdrop blurs, modal surfaces
β β βββ imessage.css # Apple iMessage chat bubbles, tails, typing pills
β β βββ main.css # Layouts, responsive breakpoints, resets
β βββ js/
β β βββ api.js # Authenticated REST API wrapper
β β βββ app.js # Application state machine & DOM orchestrator
β β βββ crypto.js # Web Crypto E2EE engine, vault store, safety numbers
β β βββ socket.js # Socket.IO connection manager & event dispatcher
β β βββ ui.js # UI rendering, toasts, and DOM sanitation
β βββ favicon.svg # Application lock emblem
β βββ index.html # Main Single Page Application shell
β βββ manifest.json # Progressive Web App manifest
β βββ sw.js # Service worker for offline shell caching
βββ server/ # Backend application server
β βββ config/
β β βββ db.js # Universal DB adapter (Supabase PostgreSQL / Local JSON)
β βββ middleware/
β β βββ auth.js # JWT verification, security headers, rate limiting
β βββ routes/
β β βββ auth.js # Registration, pre-login, login, user profile
β β βββ chat.js # Search, conversation management, message history
β βββ server.js # Express app entry, HTTP server, Socket.IO router
βββ test/
β βββ verify.js # Automated 15-suite verification & security test runner
βββ .env.example # Environment variables template
βββ package.json # Project dependencies & npm scripts
βββ README.md # Technical architecture manual
- Node.js >=
18.0.0(supports native Web Crypto and ES modules) - npm >=
8.0.0
-
Clone the repository:
git clone https://github.com/your-username/freeChat.git cd freeChat -
Install dependencies:
npm install
-
Start the application:
npm start
For auto-reloading development mode:
npm run dev
-
Open
http://localhost:3000in your browser.
π‘ Immediate Offline Evaluation: freeChat initializes its built-in local JSON database (
database/local_db.json) automatically when no external database is configured. You can start chatting and testing immediately without creating accounts or setting up cloud services.
To configure production secrets or cloud persistence, create a .env file in the project root:
cp .env.example .env| Variable | Required | Default Value | Description |
|---|---|---|---|
PORT |
Optional | 3000 |
HTTP and WebSocket listening port |
JWT_SECRET |
Recommended | Built-in dev key | Secret for signing session tokens (must be a strong 64-character key in production) |
SUPABASE_URL |
Optional | Empty (Local mode) | Full Supabase project URL (https://your-project-id.supabase.co) |
SUPABASE_KEY |
Optional | Empty (Local mode) | Supabase service_role secret API key (required for server-side RLS operations) |
ALLOWED_ORIGINS |
Optional | Auto / Empty | Comma-separated CORS whitelist (leave empty for automatic same-origin and cloud detection) |
NODE_ENV |
Optional | development |
Environment mode (development or production) |
When SUPABASE_URL is omitted, the universal database adapter (server/config/db.js) uses local file storage:
- Stores records in
database/local_db.json. - Implements concurrency-safe atomic file writes with
.tmpstaging andfs.renameSync. - Automatically generates timestamped recovery backups if malformed JSON is encountered.
For scalable, multi-device cloud storage:
- Create a project at Supabase.com.
- In the Supabase Dashboard, open the SQL Editor and run the contents of
database/schema.sql. - Copy your Project URL and service_role secret key from Project Settings $\rightarrow$ API.
- Update your
.envfile:SUPABASE_URL=https://your-project-id.supabase.co SUPABASE_KEY=your-supabase-service-role-secret-key
- Restart the server (
npm start). freeChat will automatically connect to Supabase PostgreSQL.
| Method | Endpoint | Auth | Description |
|---|---|---|---|
POST |
/api/auth/register |
Public | Registers a new account with encrypted key backup and auth verifier |
POST |
/api/auth/pre-login |
Public | Returns key derivation salt (anti-enumeration protected) |
POST |
/api/auth/login |
Public | Validates zero-knowledge auth verifier and issues session JWT |
GET |
/api/auth/me |
Bearer | Validates active session token and returns user profile |
GET |
/api/auth/user/:username |
Bearer | Returns public identity and public key for a contact |
GET |
/api/chat/users/search?q= |
Bearer | Searches registered users by username |
GET |
/api/chat/conversations |
Bearer | Lists all active conversations for the authenticated user |
POST |
/api/chat/conversations/direct |
Bearer | Initializes or retrieves a 1-on-1 direct conversation |
GET |
/api/chat/conversations/:id/messages |
Bearer | Retrieves encrypted message history (enforces participant membership) |
GET |
/api/health |
Public | System liveness probe and active database engine indicator |
| Event Name | Direction | Description |
|---|---|---|
connection |
Client |
Authenticates connection using handshake token (auth: { token }) |
online_users_list |
Server |
Transmits array of currently active user IDs upon connect |
user_status_change |
Server |
Broadcasts real-time online/offline presence changes |
join_conversation |
Client |
Joins conversation room conv:id (server validates participant membership) |
typing_start / typing_stop
|
Client |
Emits typing state; throttled server-side to prevent room flooding |
user_typing |
Server |
Relays typing indicator capsule to conversation participants |
send_message |
Client |
Sends encrypted payload; server enforces sender ID from JWT token |
new_message |
Server |
Dispatches ciphertext and IV to all conversation participants |
conversation_updated |
Server |
Notifies recipient's personal room (user:id) to refresh sidebar snippet |
| Attack Vector | Threat Scenario | freeChat Defense & Mitigation |
|---|---|---|
| Compromised Database | Attacker obtains full read access to database storage | Mathematically Protected. Database contains only AES-256-GCM ciphertext, public keys, and encrypted private key backups. No unencrypted messages, master keys, or passwords ever touch disk. |
| Malicious / Rogue Server | Server attempts to substitute Bob's public key (MITM) | Detected by Safety Numbers. Symmetrically derived 60-digit safety numbers will mismatch. Stored contact records trigger an immediate visual key-change warning banner. |
| Network Eavesdropping | Attacker intercepts traffic on unencrypted Wi-Fi | Transport + Payload Encryption. HTTPS/WSS encrypts transit; Web Crypto AES-256-GCM ensures message payloads remain unbreakable ciphertext even if TLS terminates at an intermediate proxy. |
| DOM Injection / XSS | Malicious script attempts to steal in-memory keys | Non-Extractable Keys. In-memory private keys are marked extractable: false. The browser's native crypto implementation refuses to export raw key coordinates. |
| Username Enumeration | Attacker scans /pre-login to harvest active accounts |
Anti-Enumeration Defense. The endpoint computes and returns a deterministic pseudorandom salt for non-existent users with identical HTTP status (200 OK) and latency. |
| Replay / Pass-the-Hash | Attacker captures auth verifier to impersonate user | Server HMAC Protection. Auth verifiers are hashed server-side with HMAC-SHA256 (v2$). The stored database hash cannot be replayed as a client login token. |
freeChat includes an end-to-end automated verification suite covering cryptography, authorization boundaries, concurrency, and network security.
Run the test suite:
npm test- Web Crypto Engine: Complete ECDH P-256 key agreement & AES-256-GCM encryption roundtrip.
- Zero-Knowledge Key Backup: PBKDF2 derivation, client-side encryption, and cross-device recovery.
- Database & Schema: User/conversation CRUD operations, participant indexing, and message persistence.
- Username Validation: Strict character whitelisting (3β30 chars: lowercase, numbers,
_,-,.). - IDOR / BOLA Defense: Strict object-level authorization blocking non-participant conversation access.
- Socket Security: Handshake authentication, room access verification, and sender identity spoofing prevention.
- Anti-Enumeration: Pre-login deterministic pseudorandom salts and zero unauthenticated key exposure.
- Auth Verifier Hardening: PBKDF2 (
:auth) derivation, server HMAC hashing, and timing-safe equality. - Safety Numbers & MITM Defense: Symmetric 60-digit number computation and key-substitution alerting.
- Vault Security: Encrypted client storage at rest and non-extractable in-memory keys (
extractable: false). - HTTP Security Headers: Strict Content Security Policy (CSP), HSTS, and X-Frame-Options clickjacking protection.
- Rate Limiting: Brute-force throttling on authentication endpoints and IP anti-spoofing verification.
- PostgreSQL RLS: Verification of Row Level Security policies for multi-tenant database isolation.
- Atomic Local Storage: Concurrency-safe atomic writes and automated corruption backup preservation.
- Deep Hardening: Strict registration schema validation and real-time typing flood throttling.
freeChat functions as a standalone Progressive Web App with zero third-party app store requirements:
-
iOS (Safari): Open the URL
$\rightarrow$ tap Share$\rightarrow$ tap Add to Home Screen$\rightarrow$ tap Add. -
Android (Chrome): Open the URL
$\rightarrow$ tap the menu ($\vdots$ )$\rightarrow$ tap Install App / Add to Home Screen.
When launched from the home screen, freeChat runs full-screen with native iOS safe-area adaptation, status bar tinting, and no browser chrome.
freeChat includes a pre-configured render.yaml infrastructure specification:
- Push your repository to GitHub:
git add . git commit -m "Production-ready freeChat" git push origin main
- Log in to Render.com and navigate to Blueprints
$\rightarrow$ New Blueprint Instance. - Select your repository. Render automatically reads
render.yaml, configures the Node service, sets up zero-downtime healthchecks (/api/health), and generates a cryptographically secureJWT_SECRET. - Enter your
SUPABASE_URLandSUPABASE_KEY(service_role key), then click Apply.
- Connect your GitHub repository to your cloud platform.
- Configure build & start commands:
- Build Command:
npm install - Start Command:
npm start - Healthcheck Path:
/api/health
- Build Command:
- Set the following Environment Variables:
NODE_ENV:productionTRUST_PROXY:trueJWT_SECRET: (A random 64-character string)SUPABASE_URL: (Your Supabase project URL, e.g.https://xyz.supabase.co)SUPABASE_KEY: (IMPORTANT: Your Supabase service_role key to operate with RLS)ALLOWED_ORIGINS: (Your custom domain, e.g.https://mychat.com)
freeChat includes an optimized, non-root Alpine container:
# 1. Build the Docker image
docker build -t freechat .
# 2. Run the container
docker run -d \
--name freechat \
-p 3000:3000 \
-e NODE_ENV=production \
-e JWT_SECRET="your-strong-random-secret-key-at-least-32-chars" \
-e SUPABASE_URL="https://your-project.supabase.co" \
-e SUPABASE_KEY="your-supabase-service-role-secret-key" \
freechatVerify container health:
docker inspect --format='{{json .State.Health.Status}}' freechatThis project is open-source and licensed under the MIT License.
Built with β€οΈ for privacy, freedom of speech, and craftsmanship.