Node.js · WebSockets · SQLite · MIT License

Real-time messaging
built for the terminal

A lightweight, modular terminal chat platform powered by WebSockets, featuring a rich TUI, user authentication, room management, and persistent message history.

Rooms • Direct Messages • Authentication • Chat History • Live Presence

WS
Protocol
TUI
Interface
SQLite
Persistence
argon2
Auth Hashing

Features

A modular, handler-based WebSocket server with a rich blessed terminal interface on the client.

⚡

Real-Time Messaging

Low-latency bi-directional communication over WebSockets. Messages are broadcast instantly to all users in a room.

🔐

Secure Authentication

User registration and login backed by argon2 password hashing. Sessions are tracked in-memory on the server.

🏠

Room Management

Create, list, and join chat rooms. A general room is pre-created on startup. Rooms persist until the server restarts.

💬

Direct Messages

Send private messages directly to any online user with the /dm command, bypassing the current room channel.

🗄️

Persistent History

Messages are stored in a local SQLite database via the built-in node:sqlite module. Fetch history with /history [n].

🖥️

Terminal UI

A rich, interactive client built with blessed — featuring a scrollable chat log, live status bar, and a focused input box.

Architecture

A clean, handler-based server with in-memory state and SQLite-backed persistence.

                  ┌─────────────────┐
  Client A (TUI)  │   Wire Server   │
  ──────────────► │  WebSocket :5051│◄── Client B (TUI)
                  │                 │
                  │  ┌───────────┐  │
                  │  │  Router   │  │
                  │  └─────┬─────┘  │
                  │ handleAuth      │
                  │ handleMessage   │
                  │ handleCmd       │
                  │  ┌───────────┐  │
                  │  │  SQLite   │  │
                  │  │ chat.db   │  │
                  │  └───────────┘  │
                  └─────────────────┘

Project Structure

wire-chat/
├── client/
│   ├── client.js          # TUI client (blessed + WebSocket)
│   └── modules/
│       └── cmdAction.js
├── server/
│   ├── server.js          # WebSocket server, routing
│   └── modules/
│       ├── handlers/
│       │   ├── handleAuth.js     # register / login
│       │   ├── handleMessage.js  # broadcast messages
│       │   └── handleCmd.js      # /join, /dm, /rooms ...
│       └── services/
│           ├── broadcast.js      # room broadcasting
│           └── database.js       # SQLite operations
└── shared/
    ├── protocol.js        # packet factory functions
    ├── validator.js       # packet schema validation
    └── commands.js        # command constants

Quick Start

Get WireChat running locally in under a minute.

01

Clone the repository

# Clone from GitHub
git clone https://github.com/lokeshkrio/wire-chat.git
cd wire-chat
02

Install dependencies

# Install server packages
cd server && npm install

# Install client packages
cd ../client && npm install
03

Start the server

cd server
node server.js
# → Connected to the SQLite database.
# → Wire server running
04

Launch a client

# In a new terminal window
cd client
node client.js

# Then register and login:
/register alice mysecretpass
/login alice mysecretpass

Commands

All commands are entered in the client's input box, prefixed with /.

Command Description
/register <user> <pass>Create a new account
/login <user> <pass>Authenticate and join the server
/join <room>Join or switch to a room
/create-room <name> [desc]Create a new room with an optional description
/roomsList all available rooms and their user count
/dm <user> <message>Send a private direct message
/history [limit]Fetch recent messages from the current room (default: 50)
/onlineList all currently connected users
/quitQuit the application
/helpDisplay the available command list

Packet Protocol

WireChat uses a simple JSON packet protocol over WebSockets.

Message
{
  "type": "message",
  "content": "Hello everyone"
}
Direct Message
{
  "type": "command",
  "command": "dm",
  "target": "Alice",
  "content": "Hi!"
}
Join Room
{
  "type": "command",
  "command": "join",
  "target": "general"
}
System Broadcast
{
  "type": "system",
  "content": "Alice joined general"
}