An anonymous, encrypted real-time chat platform styled like an old-school command terminal. Create secret rooms, share invite codes, pick a handle, and communicate with end-to-end style encryption. The server never sees your messages.
Traditional chat apps require accounts, store messages in plaintext, and track user data. Ghost Terminal flips that — no sign-ups, no accounts, no plaintext storage. Just an invite code and a handle. Messages are encrypted on your device before they ever leave the browser.
Built as a capstone project demonstrating Singleton, Repository, and Service Layer architectural patterns with real-time WebSocket communication and client-side encryption.
| Layer | Technology |
|---|---|
| Backend | Express.js (Node.js) |
| Database | MongoDB (Atlas) |
| Real-Time | Socket.io |
| Frontend | React (Vite) |
| Encryption | AES-256-GCM (Web Crypto API) |
| Security | Helmet, CORS, express-rate-limit |
| Validation | express-validator |
| Hashing | bcryptjs |
Client (React + Vite)
│
├── REST API (axios)
│
└── WebSocket (Socket.io-client)
│
▼
Express.js Server
│
Routes → Controllers → Services → Repositories → MongoDB
│
Socket.io Handler ──→ Services → Repositories → MongoDB
- Singleton — Single MongoDB connection instance shared across the app (
config/db.js) - Repository — All database operations isolated in repository classes. No other layer touches MongoDB directly.
- Service Layer — All business logic lives in services. Controllers are thin request/response handlers.
- Create encrypted chat rooms with auto-generated 6-character invite codes
- Join rooms via invite code — no account required
- Real-time messaging via Socket.io
- AES-256-GCM encryption — messages encrypted client-side, stored encrypted, decrypted client-side
- Message history loaded and decrypted on room join
- Room expiry with auto-deletion (TTL index)
- Optional room password (bcrypt hashed)
- Typing indicators and online user list
- Copy invite code to clipboard
- Matrix/hacker terminal UI — scanlines, glitch text, screen flicker, neon glow
- Node.js (v18+)
- MongoDB (local or Atlas)
git clone <repo-url>
cd ghost-terminalcd server
cp .env.example .envEdit .env with your MongoDB connection string:
PORT=3001
MONGODB_URI=mongodb+srv://<username>:<password>@<cluster>.mongodb.net/?retryWrites=true&w=majority
DB_NAME=ghost_terminal
CORS_ORIGIN=http://localhost:5173
NODE_ENV=development
Install and run:
npm install
npm run devcd clientThe .env file should contain:
VITE_API_URL=http://localhost:3001/api
VITE_SOCKET_URL=http://localhost:3001
Install and run:
npm install
npm run devThe app will be available at http://localhost:5173.
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/rooms |
Create a new room |
| POST | /api/rooms/join |
Join a room via invite code |
| GET | /api/rooms/:id |
Get room info and participants |
| DELETE | /api/rooms/:id |
Delete a room (creator only) |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/messages/:roomId |
Get message history (paginated) |
| DELETE | /api/messages/:roomId |
Purge all messages in a room |
Create Room:
POST /api/rooms
{
"roomName": "Operation Midnight",
"creatorHandle": "neo",
"password": "optional",
"expiresInHours": 24
}Join Room:
POST /api/rooms/join
{
"inviteCode": "X7K9M2",
"handle": "trinity",
"password": "optional"
}| Event | Payload | Description |
|---|---|---|
join_room |
{ roomId, handle } |
Enter a chat room |
leave_room |
{ roomId, handle } |
Leave a chat room |
send_message |
{ roomId, handle, encryptedContent } |
Send encrypted message |
typing |
{ roomId, handle } |
Typing indicator on |
stop_typing |
{ roomId, handle } |
Typing indicator off |
| Event | Payload | Description |
|---|---|---|
new_message |
{ _id, handle, encryptedContent, timestamp } |
New message in room |
user_joined |
{ handle, participants } |
User entered room |
user_left |
{ handle, participants } |
User left room |
user_typing |
{ handle } |
Show typing status |
user_stop_typing |
{ handle } |
Hide typing status |
Messages are encrypted using AES-256-GCM via the browser's Web Crypto API.
- An encryption key is derived from
inviteCode + roomIdusing PBKDF2 (SHA-256, 100,000 iterations) - Each message is encrypted with a random 12-byte IV
- The IV and ciphertext are combined and base64 encoded
- The server stores and transmits only the base64 ciphertext
- Receiving clients derive the same key and decrypt locally
The server never has access to plaintext messages or encryption keys.
ghost-terminal/
├── client/ # React frontend (Vite)
│ ├── src/
│ │ ├── components/
│ │ │ ├── Terminal.jsx # Main terminal wrapper
│ │ │ ├── ChatRoom.jsx # Active chat view
│ │ │ ├── JoinRoom.jsx # Create/join room screen
│ │ │ ├── MessageList.jsx # Message display
│ │ │ ├── MessageInput.jsx # Terminal-style input
│ │ │ ├── RoomHeader.jsx # Room info bar
│ │ │ ├── Scanlines.jsx # CRT overlay effect
│ │ │ └── GlitchText.jsx # Glitch animation
│ │ ├── hooks/
│ │ │ ├── useSocket.js # Socket.io connection
│ │ │ └── useEncryption.js # Encrypt/decrypt hook
│ │ ├── utils/
│ │ │ ├── crypto.js # AES-256-GCM helpers
│ │ │ └── api.js # REST API wrapper
│ │ └── styles/
│ │ └── terminal.css # Terminal theme
│ └── package.json
│
├── server/ # Express.js backend
│ ├── src/
│ │ ├── config/
│ │ │ ├── db.js # Singleton DB connection
│ │ │ ├── env.js # Environment config
│ │ │ └── socket.js # Socket.io setup
│ │ ├── controllers/ # Request/response handlers
│ │ ├── services/ # Business logic
│ │ ├── repositories/ # MongoDB operations
│ │ ├── routes/ # API route definitions
│ │ ├── middleware/ # Error handler, validation, rate limit
│ │ ├── validators/ # Input validation schemas
│ │ ├── socket/
│ │ │ └── chatHandler.js # Real-time event handlers
│ │ └── utils/ # AppError, code generator
│ ├── server.js # Entry point
│ └── package.json
│
├── .gitignore
└── README.md
- Encryption key is derived from the invite code — anyone with the code can decrypt messages
- No persistent user authentication — handles are per-session
- No message editing or deletion for individual messages
- Room creator verification for deletion relies on the handle string
- No file/image sharing support
- Matrix rain canvas background animation
- Message self-destruct (auto-delete after read)
- Room theme options (amber, blue, classic green)
- Export chat as
.txtterminal log - Bot commands (
/clear,/users,/info) - Sound effects on message send (toggleable)
- End-to-end encryption with public/private key pairs instead of shared secret

