Skip to content

Ram-sah19/Nexchat

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

10 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ” NexChat β€” End-to-End Encrypted Chat Application

A production-ready, fully End-to-End Encrypted (E2EE) real-time chat system built with Python (Flask), Java (WebSocket), and vanilla JavaScript β€” powered by ECDH + AES-GCM cryptography via the browser's native WebCrypto API. Features real-time voice & video calling (WebRTC), call history, typing indicators, unread badges, message delivery ticks, and a complete friend system β€” all following a clean MVC architecture.

GitHub Python Java MongoDB WebRTC


πŸ“‹ Table of Contents


🌐 Overview

NexChat is a fully E2EE chat application where:

  • No one β€” not even the server β€” can read your messages.
  • All encryption and decryption happens exclusively on the client (browser).
  • The server only stores and relays opaque ciphertext it cannot decode.
  • Authentication uses industry-standard JWT (HMAC-SHA256) tokens.
  • Passwords are stored using bcrypt with salting.
  • Users must be friends before they can chat or call β€” enforced at both the frontend and Java server level.
  • Live real-time voice & video calls via WebRTC β€” peer-to-peer media, Java handles signalling only.
  • Live real-time notifications: typing indicator, unread badges, message ticks, friend events.
  • The client follows a strict MVC pattern β€” state, DOM, and logic are fully separated.

✨ Features

Feature Description
πŸ” E2EE Messaging ECDH P-256 key exchange + AES-GCM 256-bit encryption in the browser
πŸ“ž Voice Calls Real-time P2P audio via WebRTC (STUN negotiation)
πŸ“Ή Video Calls Real-time P2P video with local PiP + full-screen remote
πŸ“‹ Call History WhatsApp-style inline call log (Outgoing / Incoming / Missed + duration)
βœ“βœ“ Message Ticks Single βœ“ sent β†’ Double βœ“βœ“ grey delivered β†’ Double βœ“βœ“ blue seen
βŒ› Message Timestamps HH:MM shown on every message bubble
πŸ’¬ Typing Indicator Animated 3-dot bounce in chat + mini dots in sidebar
πŸ”΄ Unread Badge Accent-coloured count badge on friend in sidebar
πŸ‘₯ Friend System Add / accept / reject / unfriend with live WS notifications
🌐 Online Status Green/grey dot on every user β€” updated on connect/disconnect
πŸ”‘ Cross-Device Keys Private key encrypted with PBKDF2 and backed up to server
πŸ—οΈ MVC Architecture Strict model / view / controller separation (13 files, no framework)

πŸ—οΈ Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        CLIENT (Browser β€” MVC)                         β”‚
β”‚                                                                        β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚  auth + friend REST  β”‚       β”‚  chat + social + call WebSocket  β”‚  β”‚
β”‚  β”‚  (FriendController)  β”‚       β”‚  (SocketController)              β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
              β”‚ HTTP (REST)                         β”‚ WebSocket (ws://)
              β–Ό                                     β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Python / Flask        β”‚       β”‚   Java WebSocket Server             β”‚
β”‚   (Port 5000)           β”‚       β”‚   (Port 5001)                       β”‚
β”‚                         β”‚       β”‚                                     β”‚
β”‚  /auth/register         β”‚       β”‚  JWT verification on connect        β”‚
β”‚  /auth/login            β”‚       β”‚  E2EE message relay                 β”‚
β”‚  /friends/status        β”‚       β”‚  Friend guard (areFriends check)    β”‚
β”‚  /friends/request       β”‚       β”‚  WebRTC signalling relay            β”‚
β”‚  /friends/accept        β”‚       β”‚  Typing indicator relay             β”‚
β”‚  /friends/reject        β”‚       β”‚  Message delivery/seen relay        β”‚
β”‚  /friends/remove        β”‚       β”‚  Call history recording             β”‚
β”‚  Serves client files    β”‚       β”‚  Social event relay                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
               β”‚                                  β”‚
               β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                  β”‚
               β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
               β”‚              MongoDB                      β”‚
               β”‚            (Port 27017)                   β”‚
               β”‚  Collections:                             β”‚
               β”‚  β€’ users           (auth + keys)          β”‚
               β”‚  β€’ messages        (encrypted blobs)      β”‚
               β”‚  β€’ friend_requests (social graph)         β”‚
               β”‚  β€’ calls           (call history log)     β”‚
               β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
Layer Technology Port Role
Auth + Friend API Python / Flask 5000 Register, Login, JWT, Friend CRUD
Chat + Relay Java / WebSocket 5001 E2EE relay, WebRTC signalling, ticks, typing
Database MongoDB 27017 Users, encrypted messages, friendships, calls
Client Vanilla JS / WebCrypto (served by Flask) MVC app β€” Crypto, UI, WS relay

πŸ“ Frontend MVC Structure

The entire client is split across 13 files following strict MVC separation.

client/
  index.html                      ← HTML shell (no logic)
  style.css                       ← Warm glassmorphism design system
  crypto.js                       ← Global WebCrypto helpers (ECDH, AES-GCM, PBKDF2)
  app.js                          ← Entry point: instantiate + wire (β‰ˆ70 lines)

  models/                         ── PURE STATE, no DOM, no fetch, no WebSocket
    AuthModel.js                  ← token, username, isLoggedIn, authHeader getter
    UserModel.js                  ← friends[], pendingSent[], unreadCounts{}, typingUsers set
    ChatModel.js                  ← activeChatUser, sharedKeys{}, ECDH keypair, reqId map

  views/                          ── DOM ONLY, no business logic
    AuthView.js                   ← login/register form, showChat/showAuth transitions
    SidebarView.js                ← renderFriendsList (with badges + typing dots), renderAllUsers
    ChatView.js                   ← appendMessage (with ticks + timestamps), updateTicks, typing
    ToastView.js                  ← show(message, type) with rAF fade animation
    CallView.js                   ← Incoming banner, active call overlay, PiP, controls

  controllers/                    ── ORCHESTRATION: use models + views + APIs
    SocketController.js           ← WebSocket lifecycle, send(), event dispatcher
    FriendController.js           ← 5 REST actions, WS relay, 5s polling safety net
    ChatController.js             ← initCrypto, key exchange, selectUser, sendMessage, ticks
    AuthController.js             ← login, register, logout, sidebar collapse
    CallController.js             ← WebRTC lifecycle: offer/answer/ICE, media, hang-up

Dependency Graph

        AuthController
        β”Œβ”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
        β–Ό      β–Ό      β–Ό             β”‚
  SocketCtrl  ChatCtrl  FriendCtrl  β”‚
      β”‚    β—„β”€β”€β”€β”€β”€β”€β”˜   β—„β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
      β”‚                  CallCtrl ◄── wired into SocketCtrl
      β”‚         └── share: AuthModel, UserModel, ChatModel
      β”‚         └── share: AuthView, SidebarView, ChatView, ToastView, CallView
      β–Ό
  (WebSocket β†’ Java Server)

πŸ“ž WebRTC Calling

Voice and video calls are peer-to-peer β€” only signalling messages (offer/answer/ICE) go through the Java server. Media streams directly between browsers.

Call Signal Flow

Caller (Browser A)          Java WS Server           Callee (Browser B)
      β”‚                          β”‚                          β”‚
      │── call_offer ──────────► β”‚ ── call_offer ─────────► β”‚
      β”‚                          β”‚                (banner shows + ring)
      │◄─────────────── call_answer ◄───────────────────────│
      │── call_ice ─────────────►│ ── call_ice ────────────►│
      │◄──────────────────── call_ice ◄─────────────────────│
      │◄══════════════ WebRTC P2P media stream ═════════════│
      │── call_ended ───────────►│ ── call_ended ──────────►│

Call History β€” MongoDB calls Collection

{
  "caller":    "alice",
  "callee":    "bob",
  "type":      "voice",
  "status":    "completed",
  "startedAt": 1714234567890,
  "answeredAt": 1714234572000,
  "endedAt":   1714234692890,
  "duration":  120
}
Status Meaning
ringing Offer sent, callee hasn't answered
in_progress Call answered by callee
completed Call ended after being answered (duration recorded)
missed Ended before callee answered

Call UI

  • Incoming banner β€” slides in from top with ring pulse animation + caller name
  • Active overlay β€” dark full-screen with remote video (full) + local PiP (bottom-right)
  • Controls β€” Mute πŸŽ™οΈ / Camera πŸ“· / End call πŸ“΅ (glassmorphic frosted bar)
  • Inline call record β€” appears in chat immediately after the call ends (no reload needed)

βœ“βœ“ Message Status Ticks

Every sent message shows a WhatsApp-style tick that upgrades in real time:

Tick Colour Meaning
βœ“ single Grey Sent β€” message reached the server
βœ“βœ“ double Grey Delivered β€” recipient's browser received it
βœ“βœ“ double Blue πŸ”΅ Seen β€” recipient opened the chat

How it works

Alice sends msg          β†’ βœ“  grey  (single)
Bob receives it online   β†’ Java relays message_delivered β†’ Alice's βœ“βœ“ grey
Bob opens the chat       β†’ Java relays message_seen      β†’ Alice's βœ“βœ“ BLUE (+ pop animation)
  • History messages load with double grey ticks (already delivered).
  • Ticks upgrade without page reload via WS events.

🀝 Friend System

Flow Overview

Step 1 β€” Alice clicks "+ Add" on Bob
  β”œβ”€ Frontend β†’ POST /friends/request    (Python saves status: "pending")
  └─ Java relays: new_friend_request β†’ Bob (live notification panel)

Step 2 β€” Bob clicks "Accept"
  β”œβ”€ Frontend β†’ POST /friends/accept     (Python sets status: "accepted")
  └─ Java fires friendship_activated to BOTH simultaneously βœ…

Step 3 β€” Chatting
  └─ Java ChatServer.areFriends() checks MongoDB before every message
       βœ… Friends   β†’ message relayed
       ❌ Not friends β†’ { type: "error" } blocked live

Step 4 β€” Unfriend
  β”œβ”€ Frontend β†’ POST /friends/remove     (Python deletes document)
  └─ Java relays: friend_removed β†’ Bob (UI closes chat instantly) βœ…

Safety Net: Every 5 seconds FriendController polls /friends/status
  └─ If state drifted (missed WS event), auto-syncs and re-renders silently

MongoDB Schema β€” friend_requests

{
  "_id":      "ObjectId(...)",
  "sender":   "alice",
  "receiver": "bob",
  "status":   "pending"
}

Status transitions: pending β†’ accepted | rejected On unfriend: document is deleted (not soft-deleted).


πŸ” Cryptographic Algorithms

1. ECDH P-256 β€” Key Exchange

const ecdhParams = { name: "ECDH", namedCurve: "P-256" };
const keyPair = await crypto.subtle.generateKey(ecdhParams, true, ["deriveKey", "deriveBits"]);
  • Shared secret: Alice_priv + Bob_pub = Bob_priv + Alice_pub β†’ becomes the AES-GCM key.

2. AES-GCM 256-bit β€” Message Encryption

const iv         = crypto.getRandomValues(new Uint8Array(12)); // fresh per message
const ciphertext = await crypto.subtle.encrypt({ name: "AES-GCM", iv }, sharedKey, encoded);
  • Fresh 12-byte random IV per message β€” prevents IV-reuse attacks.

3. HMAC-SHA256 β€” JWT Auth

token = jwt.encode({ 'username': ..., 'exp': now + 10h }, JWT_SECRET, algorithm='HS256')

4. bcrypt β€” Password Hashing

hashed = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8')

5. PBKDF2 + AES-GCM β€” Private Key Backup (Cross-Device)

const aesKey = await crypto.subtle.deriveKey(
  { name: "PBKDF2", salt: enc.encode(username + "_salt"), iterations: 100000, hash: "SHA-256" },
  passwordKey,
  { name: "AES-GCM", length: 256 }, false, ["encrypt", "decrypt"]
);
  • Private key encrypted client-side before upload β€” server cannot decrypt it.

πŸ”„ Full Message Encryption Flow

[Alice β€” Browser]               [Java Server]              [Bob β€” Browser]
       β”‚                              β”‚                            β”‚
       │── login (REST) ────────────►│                            β”‚
       │◄─ JWT + encrypted priv key ─│                            β”‚
       │── ChatController.initCrypto()                            β”‚
       β”‚   restore ECDH keys from localStorage / server           β”‚
       β”‚                              β”‚                            β”‚
       │── WS connect ?token=JWT ───►│                            β”‚
       │── join { publicKey } ──────►│── store Alice_pub in DB   β”‚
       β”‚                              │◄── Bob does the same ─────│
       β”‚                              β”‚                            β”‚
       │── request_public_key(Bob) ──►│                           β”‚
       │◄─ Bob_pub ──────────────────│                            β”‚
       │── deriveSharedKey(Alice_priv, Bob_pub) β†’ aesKey          β”‚
       │── encryptMessage(aesKey, "Hello") β†’ { ciphertext, iv }   β”‚
       │── send_message ────────────►│── areFriends? βœ…           β”‚
       β”‚                              │── save to MongoDB          β”‚
       β”‚                              │── relay to Bob ───────────►│
       β”‚                              β”‚       decryptMessage(aesKey, ciphertext, iv)
       β”‚                              β”‚                   β†’ "Hello" βœ…
       β”‚                              │◄── message_seen ──────────│ (Bob opens chat)
       │◄─ message_seen (from: bob) ─│                            β”‚
       β”‚  updateTicks("bob", "seen") β†’ βœ“βœ“ BLUE                    β”‚

πŸ› οΈ Tech Stack

Component Technology
Auth + Friend API Python 3, Flask, Flask-CORS, PyMongo, PyJWT, bcrypt
Chat + Relay Java 11, Java-WebSocket (TooTallNate), auth0 java-jwt, MongoDB Java Driver
Real-Time Calling WebRTC (RTCPeerConnection, getUserMedia), STUN: stun.l.google.com:19302
Database MongoDB 6+
Client Architecture Vanilla JS MVC (13 files, no framework)
Cryptography Browser WebCrypto API (crypto.subtle)
UI Warm Glassmorphism (Inter font, Phosphor Icons)
Build Apache Maven + maven-shade-plugin

βœ… Prerequisites

Tool Version
Python 3.8+
Java 11+
Apache Maven 3.6+
MongoDB 6+ running on localhost:27017

πŸ“¦ Installation & Setup

1. Python Server

cd server-python
pip install -r requirements.txt

.env file (create in server-python/):

MONGO_URI=mongodb://localhost:27017/securechat
JWT_SECRET=your-secret-key-here

2. Java Server

cd server-java
mvn clean package -DskipTests

⚠️ Always rebuild and restart after editing ChatServer.java β€” the running JAR will not pick up source changes automatically. Use the shaded JAR: target/server-java-1.0-SNAPSHOT-shaded.jar


πŸš€ Running the App

Step 1 β€” MongoDB

mongosh

Step 2 β€” Recommended Indexes (one-time setup)

use securechat
db.friend_requests.createIndex({ sender: 1, receiver: 1 })
db.friend_requests.createIndex({ receiver: 1, status: 1 })
db.messages.createIndex({ sender: 1, receiver: 1, timestamp: 1 })
db.calls.createIndex({ caller: 1, callee: 1, startedAt: 1 })

Step 3 β€” Python Server (Terminal 1)

cd server-python
python app.py
# β†’ http://localhost:5000

Step 4 β€” Java WebSocket Server (Terminal 2)

cd server-java
java -jar target\server-java-1.0-SNAPSHOT-shaded.jar
# β†’ ws://localhost:5001

Step 5 β€” Open the App

Navigate to http://localhost:5000


πŸ—„οΈ MongoDB Collections

users

{
  "username": "alice",
  "password": "<bcrypt hash>",
  "publicKey": { "<ECDH P-256 JWK>" },
  "encryptedPrivateKey": { "<PBKDF2-encrypted JWK>" }
}

messages

{
  "sender": "alice",
  "receiver": "bob",
  "ciphertext": [12, 34, ...],
  "iv": [56, 78, ...],
  "timestamp": 1714234567890
}

friend_requests

{
  "sender": "alice",
  "receiver": "bob",
  "status": "pending | accepted | rejected"
}

calls

{
  "caller": "alice",
  "callee": "bob",
  "type": "voice | video",
  "status": "ringing | in_progress | completed | missed",
  "startedAt": 1714234567890,
  "answeredAt": 1714234572000,
  "endedAt": 1714234692890,
  "duration": 120
}

πŸ“‘ API Reference

Auth (Python β€” Port 5000)

Method Endpoint Body Response
POST /auth/register { username, password, publicKey, encryptedPrivateKey } { msg }
POST /auth/login { username, password } { token, username, publicKey, encryptedPrivateKey }

Friend System (Python β€” Port 5000, JWT Required)

Method Endpoint Body Description
GET /friends/status β€” Returns friends[], pending_sent[], pending_received[]
POST /friends/request { receiver } Send a friend request
POST /friends/accept { sender } Accept a pending request
POST /friends/reject { sender } Decline a pending request
POST /friends/remove { target } Unfriend β€” deletes from MongoDB

πŸ”Œ WebSocket Events (Java β€” Port 5001)

Connect: ws://localhost:5001/?token=<JWT>

Client β†’ Server

Type Payload Description
join { publicKey: JWK } Register session + public key
request_public_key { receiver } Fetch ECDH public key
send_message { receiver, ciphertext[], iv[] } Send encrypted message
fetch_history { withUser } Retrieve chat history
fetch_call_history { withUser } Retrieve call log
typing { to, isTyping } Typing indicator relay
message_delivered { to } Notify sender: message received
message_seen { to } Notify sender: message seen
call_offer { to, offer, withVideo } WebRTC offer (friends-only)
call_answer { to, answer } WebRTC answer
call_ice { to, candidate } ICE candidate
call_ended { to } Hang-up / decline
friend_request_sent { to } Relay: ping receiver
friend_request_accepted { to } Relay: fire to both users
friend_removed_notify { to } Relay: ping target

Server β†’ Client

Type Payload Triggered by
user_list ["alice", ...] Any connect/disconnect
online_users ["alice", ...] Any connect/disconnect
public_key_response { publicKey } request_public_key
receive_message { sender, ciphertext[], iv[], timestamp } send_message
history_response [{ sender, ciphertext[], iv[], timestamp }] fetch_history
call_history_response [{ caller, callee, type, status, duration, startedAt }] fetch_call_history
typing { from, isTyping } Typing relay
message_delivered { from } Delivery receipt relay
message_seen { from } Seen receipt relay
call_offer { from, offer, withVideo } WebRTC signalling
call_answer { from, answer } WebRTC signalling
call_ice { from, candidate } WebRTC signalling
call_ended { from } Hang-up / cancel
new_friend_request { from } πŸ”” Live request alert
friendship_activated { with } βœ… Both UIs add each other
friend_removed { from } ❌ Live unfriend notification
error { message } Friend guard block or offline peer

πŸ›‘οΈ Security Design Decisions

Design Choice Reason
ECDH over RSA Smaller keys, faster operations, equivalent security
AES-GCM over AES-CBC AEAD β€” encryption + authentication in one primitive; prevents tampering
Fresh IV per message Prevents IV-reuse attacks that break AES-GCM security entirely
PBKDF2 private key backup PBKDF2(password) β†’ AES-GCM wraps private key β€” server never holds plaintext key
bcrypt with gensalt() Salted, computationally expensive β€” resists rainbow tables and brute force
JWT expiry (10 hours) Limits token exposure window if intercepted
Browser WebCrypto API Native, audited, hardware-accelerated β€” no third-party crypto library needed
Friend guard in Java (server-side) Cannot be bypassed by the client β€” friendship verified in MongoDB before every relay
WebRTC P2P for calls Media never touches the server β€” no call recording possible by the server
STUN for NAT traversal Uses Google's public STUN servers; add TURN for symmetric NAT environments
5s polling safety net Eventual-consistency guarantee β€” catches missed WS relay events silently
MVC separation State lives only in Models β€” Views never mutate state; zero spaghetti logic

⚠️ Known Limitations

  • No Perfect Forward Secrecy (PFS): Keys are persisted. A compromised private key could decrypt stored ciphertext. Production would use ephemeral keys per session.
  • TOFU model only: Key fingerprints are displayed but not enforced β€” no out-of-band verification flow.
  • TURN server not included: WebRTC calls work on LAN/same-network. For calls across the internet (symmetric NAT), a TURN relay server must be added to CallController._iceConfig.
  • No group calls or group chats: One-to-one only.
  • No message deletion or editing.
  • Single server: No horizontal scaling for the Java WebSocket server.
  • Offline notifications: Users who are offline when a friend request or message is sent receive it only after next login (the 5s poll catches it immediately on reconnect). Call history still saves to MongoDB regardless.

πŸ“ License

This project is for educational and demonstration purposes.


Built with ❀️ by Ram-sah19

NexChat β€” Encrypted Β· Social Β· Real-Time Β· WebRTC Β· MVC

About

Developed a feature-rich messaging platform inspired by modern communication standards, implementing real-time data synchronization and end-to-end encryption

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors