This is a continuation to my HLD document this expands more into the LLD side of things I will not be using Class components that is why it is excluded here
Low-Level Design (LLD) for E2EE Messaging App
Key Focus Areas
- Frontend Components
- Backend Architecture
- Database Schema
- API Design
- WebSocket Events
- Message Encryption/Decryption
1. Frontend Components
a) Component Breakdown
Your React app can be broken down into the following core components:
-
Authentication:
-
LoginForm
(Handles login requests). -
RegisterForm
(Handles new user registration and public key generation).
-
-
Dashboard:
-
ChatList
(Displays active conversations or rooms). -
UserSearch
(Allows searching for users to start new chats). -
RoomList
(Displays a list of chat rooms).
-
-
Messaging:
-
MessageWindow
(Displays chat history and active conversation). -
MessageInput
(Text input field for sending messages). -
MessageBubble
(Individual message component with sender/receiver styling).
-
-
Utilities:
-
WebSocketProvider
(Manages WebSocket connection and events). -
EncryptionHelper
(Handles encryption/decryption on the client side).
-
b) Frontend Flows
-
User Login:
- Send
email
andpassword
to the backend to get a JWT token. - Store the private key locally for decrypting messages.
- Send
-
Send Message:
- Retrieve the recipient’s public key (from backend API).
- Encrypt the message using the recipient’s public key.
- Send the encrypted message over WebSocket.
-
Receive Message:
- Decrypt messages using the private key stored in
localStorage
.
- Decrypt messages using the private key stored in
2. Backend Architecture
Key Modules:
-
Authentication:
- User registration and login APIs (with JWT for authentication).
- Public key storage in the database.
-
Message Handling:
- WebSocket server for real-time communication.
- REST APIs for fetching old messages.
-
Room Management:
- APIs to create/join rooms.
- WebSocket events to notify participants of new messages.
-
Redis Cache:
- Store online user sessions and active WebSocket connections.
3. Database Schema
Below is the schema design to handle user data, encrypted messages, and chat rooms.
Users Table
CREATE TABLE users (
user_id SERIAL PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
public_key TEXT NOT NULL, -- User's public RSA key
created_at TIMESTAMP DEFAULT NOW()
);
Messages Table
CREATE TABLE messages (
message_id SERIAL PRIMARY KEY,
sender_id INTEGER REFERENCES users(user_id),
recipient_id INTEGER REFERENCES users(user_id), -- NULL if it's a room message
room_id INTEGER REFERENCES rooms(room_id), -- NULL if it's a direct message
encrypted_message TEXT NOT NULL, -- Base64-encoded encrypted message
timestamp TIMESTAMP DEFAULT NOW()
);
Rooms Table
CREATE TABLE rooms (
room_id SERIAL PRIMARY KEY,
room_name VARCHAR(100) NOT NULL,
created_by INTEGER REFERENCES users(user_id),
created_at TIMESTAMP DEFAULT NOW()
);
Room Members Table
CREATE TABLE room_members (
room_member_id SERIAL PRIMARY KEY,
room_id INTEGER REFERENCES rooms(room_id) ON DELETE CASCADE,
user_id INTEGER REFERENCES users(user_id) ON DELETE CASCADE,
joined_at TIMESTAMP DEFAULT NOW()
);
4. API Design
The APIs will facilitate frontend-backend communication for login, message storage, and user management.
Authentication APIs
1.POST /register
-
Request:
{ "username": "JohnDoe", "email": "john@example.com", "password": "securePass123", "publicKey": "BASE64_PUBLIC_KEY" }
-
Response:
{ "message": "User registered successfully" }
2.POST /login
-
Request:
{ "email": "john@example.com", "password": "securePass123" }
-
Response:
{ "token": "JWT_TOKEN", "userId": 123 }
Messaging APIs
1.POST /fetchMessages
-
Request:
{ "userId": 123, "chatWith": 456 }
-
Response:
[ { "messageId": 1, "encryptedMessage": "BASE64_MESSAGE", "timestamp": "2024-12-31T12:00:00Z" } ]
2.POST /fetchRoomMessages
-
Request:
{ "roomId": 789 }
-
Response:
[ { "messageId": 1, "encryptedMessage": "BASE64_MESSAGE", "timestamp": "2024-12-31T12:00:00Z" } ]
5. WebSocket Events
WebSocket Client Events
1.sendMessage:
{ "recipientId": 456, "encryptedMessage": "BASE64_ENCRYPTED_MESSAGE" }
2.joinRoom:
{ "roomId": 789 }
3.typing:
{ "roomId": 789, "isTyping": true }
WebSocket Server Events
1.receiveMessage:
{ "senderId": 123, "encryptedMessage": "BASE64_ENCRYPTED_MESSAGE" }
2.roomNotification:
{ "roomId": 789, "message": "JohnDoe has joined the room." }
6. Message Encryption/Decryption
Frontend: Encrypt Message
Use the recipient’s public key to encrypt the message:
const crypto = require('crypto');
function encryptMessage(message, publicKey) {
return crypto.publicEncrypt(publicKey, Buffer.from(message)).toString('base64');
}
Frontend: Decrypt Message
Use the user’s private key to decrypt incoming messages:
function decryptMessage(encryptedMessage, privateKey) {
return crypto.privateDecrypt(privateKey, Buffer.from(encryptedMessage, 'base64')).toString();
}
Backend: Store Encrypted Messages
The backend stores the encrypted message directly in the database, ensuring it cannot read user data.
Conclusion
The Low-Level Design outlined here provides a detailed roadmap for implementing a secure, real-time messaging system with end-to-end encryption (E2EE). It defines the necessary components, such as database schemas, API endpoints, WebSocket events, and encryption logic, while maintaining a focus on scalability and security.
This design is extensible, allowing for future features like file sharing, advanced group chat functionalities, and multimedia integration. By following this LLD, developers can seamlessly implement the features while adhering to best practices in security and real-time communication.
With the foundation provided by this LLD, the next steps involve translating these concepts into robust, production-ready code. Feedback and collaboration will ensure that the system evolves to meet user needs while maintaining its core principles of security and performance.
Top comments (0)