Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

13 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

DownpourChat β€” MERN Real-Time Encrypted Chat

A full-stack real-time chat application with AES-256 end-to-end encryption, anonymous authentication, and transient encrypted messaging.

πŸ”— Live Demo: https://downpourchat.netlify.app
πŸ–₯️ Server: https://downpourchat-server.onrender.com
πŸ“¦ Repo: https://github.com/Ankit-2039/DownpourChat


Tech Stack

Layer Technology
Frontend React 18, Vite, React Router v6
Backend Node.js, Express.js
Database MongoDB Atlas, Mongoose
Real-time Socket.IO v4
Encryption Web Crypto API (AES-256-CBC, PBKDF2)
Sessions express-session + connect-mongo
Hosting Netlify (client), Render (server)

Features

  • πŸ”’ AES-256 E2E Encryption β€” server never sees plaintext
  • πŸ‘€ Anonymous authentication β€” no login, no registration
  • ⚑ Real-time messaging via Socket.IO WebSockets
  • ✍️ Typing indicators with multi-user support
  • πŸ“œ Chat transcripts β€” persistent encrypted history
  • πŸ’Ύ Export chat β€” download decrypted transcript as JSON on leave
  • ⏳ Transient rooms β€” auto-expire after 24 hours

Project Structure

root/
β”œβ”€β”€ start.bat              # Windows: double-click to launch everything
β”œβ”€β”€ netlify.toml           # Netlify SPA routing config
β”œβ”€β”€ package.json           # Root: concurrently runs server + client
β”œβ”€β”€ server/
β”‚   β”œβ”€β”€ config/db.js
β”‚   β”œβ”€β”€ models/            # Room.js, Message.js
β”‚   β”œβ”€β”€ routes/            # roomRoutes.js, transcriptRoutes.js
β”‚   β”œβ”€β”€ socket/            # socketHandler.js
β”‚   β”œβ”€β”€ middleware/        # sessionMiddleware.js
β”‚   β”œβ”€β”€ app.js
β”‚   β”œβ”€β”€ server.js
β”‚   └── .env.example
└── client/
    β”œβ”€β”€ src/
    β”‚   β”œβ”€β”€ crypto/        # cryptoUtils.js (PBKDF2 + AES-256)
    β”‚   β”œβ”€β”€ socket/        # socketClient.js
    β”‚   β”œβ”€β”€ context/       # ChatContext.jsx
    β”‚   β”œβ”€β”€ hooks/         # useSocket.js, useEncryption.js
    β”‚   β”œβ”€β”€ components/    # JoinRoom, ChatWindow, MessageList, MessageInput, TypingIndicator
    β”‚   β”œβ”€β”€ pages/         # Home.jsx, Chat.jsx
    β”‚   β”œβ”€β”€ App.jsx
    β”‚   └── index.css
    β”œβ”€β”€ index.html
    β”œβ”€β”€ vite.config.js
    └── .env.example

Prerequisites

  • Node.js >= 18
  • MongoDB running locally, or a MongoDB Atlas URI

Local Setup

1. Clone the repo

git clone https://github.com/Ankit-2039/DownpourChat.git
cd DownpourChat

2. Configure environment variables

Server β€” server/.env:

PORT=5000
MONGO_URI=mongodb://127.0.0.1:27017/encrypted-chat
SESSION_SECRET=replace_with_a_strong_random_string
CLIENT_ORIGIN=http://localhost:5173
NODE_ENV=development

Client β€” client/.env:

VITE_SERVER_URL=http://localhost:5000

3. Start the app

Windows β€” double-click start.bat

Or from terminal:

npm install     # installs concurrently at root
npm run dev     # starts both server + client
Service URL
Client http://localhost:5173
Server http://localhost:5000

Deployment

Service Platform URL
Client Netlify https://downpourchat.netlify.app
Server Render https://downpourchat-server.onrender.com
Database MongoDB Atlas Managed cloud

Production env variables

Render (server):

MONGO_URI=<your Atlas URI>
SESSION_SECRET=<strong random string>
CLIENT_ORIGIN=https://downpourchat.netlify.app
NODE_ENV=production

Netlify (client):

VITE_SERVER_URL=https://downpourchat-server.onrender.com

Note: Render free tier spins down after 15 min inactivity. First request may take ~30s to cold start.


How It Works

End-to-End Encryption

User A                         Server                        User B
  |                               |                              |
  |  passphrase + roomId          |                              |
  |──► PBKDF2 ──► AES-256 Key     |         passphrase + roomId  |
  |   (in memory only)            |    AES-256 Key ◄── PBKDF2 ◄──|
  |                               |         (in memory only)     |
  | encrypt(plaintext) ──────────► store ciphertext ────────────►|
  |                               |   (server sees NO plaintext) |
  |                               |                   decrypt()  |
  |                               |               plaintext βœ“    |
  1. Key Derivation β€” Client derives AES-256 key via PBKDF2(passphrase, roomId, 100000 iterations, SHA-256). Key never leaves the browser.
  2. Encrypt before send β€” Messages are encrypted client-side before emitting via Socket.IO. Server only receives { ciphertext, iv }.
  3. Server is blind β€” Server stores and relays only ciphertext. No plaintext field exists in the database.
  4. Decrypt on receive β€” Incoming ciphertext is decrypted immediately using the in-memory key. Wrong passphrase renders [decryption failed].
  5. Transcript β€” Chat history is fetched from the REST API and decrypted client-side on mount.

Anonymous Authentication

  • On first request, attachAnonId middleware assigns a uuid v4 as req.session.anonId
  • Stored in MongoDB via connect-mongo, shared with Socket.IO
  • No login, no registration, no PII stored

Room Lifecycle

  • Rooms use UUID v4 identifiers with a 24-hour TTL β€” auto-expire in MongoDB
  • Joining validates room existence before key derivation proceeds

Transcript Export

  • On leave, users are prompted to download chat as JSON
  • File is decrypted client-side before export β€” server never involved
  • System messages excluded from export

API Reference

REST

Method Endpoint Description
POST /api/rooms/create Create a new room, returns roomId
POST /api/rooms/join Validate room exists
GET /api/transcript/:roomId Fetch last 100 ciphertexts
GET /health Health check

Socket.IO Events

Client β†’ Server

Event Payload Description
message:send { ciphertext, iv } Send encrypted message
typing:start β€” User started typing
typing:stop β€” User stopped typing

Server β†’ Client

Event Payload Description
message:receive { _id, username, ciphertext, iv, createdAt } Broadcast encrypted msg
typing:update { typingUsers: string[] } Current typers in room
user:joined { username } User joined notification
user:left { username } User left notification

Security Notes

  • Passphrase strength directly determines encryption strength
  • Page refresh clears the in-memory CryptoKey β€” users must re-enter passphrase (intentional)
  • Usernames stored as plaintext metadata only β€” not message content
  • Sessions use httpOnly cookies with secure flag in production
  • Room IDs are UUID v4 β€” share only via trusted channels

Environment Variables

Server

Variable Required Description
PORT No Server port (default: 5000)
MONGO_URI Yes MongoDB connection string
SESSION_SECRET Yes Secret for signing session cookies
CLIENT_ORIGIN No CORS origin (default: localhost:5173)
NODE_ENV No development or production

Client

Variable Required Description
VITE_SERVER_URL No Backend URL (default: localhost:5000)

Dependencies

Root

Package Purpose
concurrently Run server + client together

Server

Package Purpose
express HTTP server & routing
mongoose MongoDB ODM
socket.io WebSocket server
express-session Session management
connect-mongo MongoDB session store
cors Cross-origin requests
uuid Room ID + anonId generation
dotenv Environment variable loading

Client

Package Purpose
react UI framework
react-router-dom Client-side routing
socket.io-client WebSocket client
uuid Client-side anonId generation

No external crypto library β€” encryption uses the native Web Crypto API built into all modern browsers.

About

A full-stack real-time encrypted chat application built with the MERN stack, featuring AES-256 end-to-end encryption, anonymous session-based authentication, and transient messaging. Messages are encrypted client-side using the Web Crypto API and transmitted over Socket.IO, ensuring the server never accesses plaintext data.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages