Skip to content

leejaew/template-secure-chat

Repository files navigation

SecureChat RT

Node.js License Socket.IO Remix on Replit

A production-ready, forkable starter template for real-time encrypted group chat. Built for teams and developers who need a self-hosted messaging baseline with strong security defaults and zero frontend framework dependencies.

Remix this project on Replit — fork and deploy in one click, no local setup required. New to Replit? Create a free account.

Key features

  • End-to-end encryption — Messages are encrypted in the browser with AES-256-GCM before being sent. The server never sees plaintext. Each room has its own key, delivered to the client via ECDH P-256 key exchange.
  • Room keys encrypted at rest — The server stores room keys wrapped with AES-256-GCM using a key derived from MASTER_ENCRYPTION_KEY via PBKDF2-SHA512 (600,000 iterations, per-key random salt).
  • Replit OIDC authentication — Session-based login using Replit's built-in OpenID Connect provider with PKCE. No client credentials required when deployed on Replit. Sessions stored in PostgreSQL and expire after 24 hours.
  • Public and private rooms — Anyone can join public rooms. Private rooms require an invite link created by the room creator. Invite links have configurable expiry (default 24 hours) and a maximum number of uses (default 10).
  • WebRTC signaling relay — The server relays SDP offers, answers, and ICE candidates between peers for establishing direct connections. The server acts as a blind relay and cannot inspect WebRTC traffic.
  • Real-time lobby and presence — A lobby panel shows all currently connected users with live updates. Per-room member lists update in real time. Presence is multi-device aware: a user only goes offline when all of their sockets disconnect.
  • Client-side shadow-ban profanity filter — Runs in the sender's browser before encryption. Flagged messages are echoed locally so the sender believes delivery occurred, but the encrypted payload is never transmitted. The server cannot perform this check because it never sees plaintext.
  • Structured rate limiting — Separate rate limiters for room joins, message sends, history requests, invite creation, and invite use. Limits are applied per user ID or IP depending on the operation.

Tech stack

Layer Technology
Runtime Node.js ≥ 20
HTTP server Express 4
Real-time transport Socket.IO 4
Database PostgreSQL (via pg)
Session storage connect-pg-simple
Authentication Replit OIDC via openid-client
Encryption (server) Node.js crypto — AES-256-GCM, PBKDF2-SHA512
Encryption (client) Web Crypto API — AES-256-GCM, ECDH P-256
Input validation Zod
Security headers Helmet
Logging Pino (JSON); pino-pretty for local development
Frontend Vanilla HTML / CSS / JavaScript — no framework

Prerequisites

  • Node.js v20 or higher
  • PostgreSQL 14 or higher
  • A Replit account (required for the built-in OIDC provider)
  • pnpm v8 or higher (this project is a pnpm workspace)

Installation and setup

  1. Fork or clone this repository:

    git clone https://github.com/leejaew/template-secure-chat.git
    cd template-secure-chat
  2. Install dependencies from the workspace root:

    pnpm install
  3. Copy the example environment file and fill in all values:

    cp artifacts/securechat/.env.example artifacts/securechat/.env
  4. Create the database schema. Connect to your PostgreSQL instance and run:

    psql $DATABASE_URL -f artifacts/securechat/src/db/schema.sql
  5. Generate the required secrets and add them to .env:

    # SESSION_SECRET — at least 32 characters
    openssl rand -base64 48
    
    # MASTER_ENCRYPTION_KEY — exactly 64 hex characters
    openssl rand -hex 32

    Important: MASTER_ENCRYPTION_KEY must remain stable across restarts. Changing it after rooms have been created makes all stored room keys unrecoverable and breaks message history.

Running the project

pnpm --filter @workspace/securechat run dev

The server starts on the port specified by PORT (default 3000). Open http://localhost:3000 in a browser. A Pino log line like {"level":30,"msg":"Server listening","port":3000} confirms a successful start.

Environment variables

Variable Description Required
NODE_ENV Set to production to enable HSTS, secure cookies, and suppress stack traces. Optional (defaults to development)
PORT Port the HTTP server listens on. Optional (defaults to 3000)
APP_URL Full public URL of this deployment — e.g. https://your-app.replit.app. Used as the OIDC callback base and for generating invite links. Required
REPLIT_OIDC_ISSUER OIDC discovery endpoint. Change only if using a custom provider. Optional (defaults to https://replit.com/oidc)
SESSION_SECRET Signs and verifies session cookies. Must be at least 32 characters. Generate with openssl rand -base64 48. Required
DATABASE_URL PostgreSQL connection string — e.g. postgresql://user:password@localhost:5432/securechat. Required
MASTER_ENCRYPTION_KEY 64-character hex string used to wrap room keys at rest. Generate with openssl rand -hex 32. Never change this after rooms exist. Required

Architecture notes for contributors

Why the server never filters messages: All message content is encrypted client-side before transmission. The server stores and forwards ciphertext only. The profanity filter in public/js/profanity.js therefore runs in the sender's browser before the message is encrypted — this is the only point in the pipeline where plaintext is accessible.

Key exchange flow: When a client joins a room, it generates an ephemeral ECDH P-256 key pair and sends the public key to the server. The server generates its own ephemeral pair, derives a shared secret, uses it to wrap the room's AES-256-GCM key, and sends the wrapped key back alongside its public key. The client unwraps the room key locally. The raw room key never travels over the network.

Room key storage: Room keys are wrapped with AES-256-GCM using a key derived from MASTER_ENCRYPTION_KEY via PBKDF2-SHA512 with 600,000 iterations and a per-key random 32-byte salt. The encrypted key, IV, auth tag, and salt are stored in PostgreSQL. The plaintext room key exists in server memory only during a key exchange operation.

License

MIT

About

A starter template for a real-time encrypted group chat app with Socket.IO, AES-256-GCM E2E encryption, Replit Auth, WebRTC signalling, and PostgreSQL persistence.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors