README
¶
marchat
A lightweight terminal chat with separate server and client binaries, real-time messaging over WebSockets, optional end-to-end encryption, and a flexible plugin ecosystem. Built for developers who prefer the command line and want reliable, self-hosted group chat with minimal operational overhead.

Table of Contents
- Overview
- Features
- Breaking Changes
- Installation & Setup
- Quick Start
- Configuration
- TLS Support
- Usage
- Security
- Troubleshooting
- Roadmap
- Getting Help
- Contributing
- Appreciation
Overview
marchat started as a fun weekend project for father-son coding sessions and has since evolved into a lightweight, self-hosted terminal chat application designed specifically for developers who love the command line. It currently runs with a local SQLite database and real-time messaging over WebSockets, with planned support for PostgreSQL and MySQL to enable greater scalability and flexibility.
Key Benefits:
- Self-hosted: No external services required
- Cross-platform: Runs on Linux, macOS, and Windows
- Secure: Optional E2E encryption with X25519/ChaCha20-Poly1305
- Extensible: Plugin ecosystem for custom functionality
- Lightweight: Minimal resource usage, perfect for servers
Features
| Feature | Description |
|---|---|
| Terminal UI | Beautiful TUI built with Bubble Tea |
| Real-time Chat | Fast WebSocket-based messaging with a lightweight SQLite backend |
| Plugin System | Install and manage plugins via :store and :plugin commands |
| E2E Encryption | Optional X25519 key exchange with ChaCha20-Poly1305 |
| File Sharing | Send files up to 1MB with :sendfile |
| Admin Controls | User management, bans, and database operations with improved ban/unban experience |
| Themes | Choose from patriot, retro, or modern themes |
| Docker Support | Containerized deployment with security features |
| Enhanced User Experience | Improved message history persistence after moderation actions |
| Cross-Platform File Sharing | Theme Switching |
|---|---|
![]() |
![]() |
marchat running on Android via Termux, demonstrating file transfer through reverse proxy and real-time theme switching
Breaking Changes
[!IMPORTANT] Database Schema Migration Required
v0.3.0-beta.1 includes breaking changes that require database migration.
What's Changed
v0.3.0-beta.1 introduces per-user message state tracking to fix the "frozen message history" bug where banned/unbanned users could only see messages from before their ban. This enhancement requires database schema changes that will affect existing installations.
Required Actions
Before building from source:
-
Backup your database:
cp ./config/marchat.db ./config/marchat.db.backup -
Build and run - migration happens automatically during server startup
-
Verify migration - check server logs for migration messages
Migration Details
- New table:
user_message_statefor tracking per-user message history - Schema update:
messagestable getsmessage_idcolumn - Automatic migration: Existing messages get
message_id = id - Performance: New indexes added for efficient queries
- Duration: Typically under 30 seconds for most installations
Rollback Procedure
If migration fails or you need to downgrade:
# Stop the server
# Restore from backup
cp ./config/marchat.db.backup ./config/marchat.db
# Restart with previous version
Benefits After Migration
- Improved user experience: Banned/unbanned users see complete message history
- Better moderation: Clean slate for users after ban/unban cycles
- Enhanced performance: Optimized queries with new indexes
- Future-proof: Foundation for advanced message tracking features
Installation & Setup
Binary Installation
Download pre-built binaries for v0.3.0-beta.1:
# Linux (amd64)
wget https://github.com/Cod-e-Codes/marchat/releases/download/v0.3.0-beta.1/marchat-v0.3.0-beta.1-linux-amd64.zip
unzip marchat-v0.3.0-beta.1-linux-amd64.zip
chmod +x marchat-server marchat-client
# macOS (amd64)
wget https://github.com/Cod-e-Codes/marchat/releases/download/v0.3.0-beta.1/marchat-v0.3.0-beta.1-darwin-amd64.zip
unzip marchat-v0.3.0-beta.1-darwin-amd64.zip
chmod +x marchat-server marchat-client
# Windows
# Download from GitHub releases page, extract the ZIP,
# and run marchat-server.exe and marchat-client.exe from PowerShell or CMD.
# Android/Termux (arm64)
pkg install wget unzip
wget https://github.com/Cod-e-Codes/marchat/releases/download/v0.3.0-beta.1/marchat-v0.3.0-beta.1-linux-arm64.zip
unzip marchat-v0.3.0-beta.1-linux-arm64.zip
chmod +x marchat-server marchat-client
# Android/Termux (armv7)
pkg install wget unzip
wget https://github.com/Cod-e-Codes/marchat/releases/download/v0.3.0-beta.1/marchat-v0.3.0-beta.1-linux-armv7.zip
unzip marchat-v0.3.0-beta.1-linux-armv7.zip
chmod +x marchat-server marchat-client
Docker Installation
Pull from Docker Hub:
# Latest release
docker pull codecodesxyz/marchat:v0.3.0-beta.1
# Run with environment variables
docker run -d \
-p 8080:8080 \
-e MARCHAT_ADMIN_KEY=$(openssl rand -hex 32) \
-e MARCHAT_USERS=admin1,admin2 \
codecodesxyz/marchat:v0.3.0-beta.1
Using Docker Compose:
# docker-compose.yml
version: '3.8'
services:
marchat:
image: codecodesxyz/marchat:v0.3.0-beta.1
ports:
- "8080:8080"
environment:
- MARCHAT_ADMIN_KEY=${MARCHAT_ADMIN_KEY}
- MARCHAT_USERS=${MARCHAT_USERS}
volumes:
- ./config:/marchat/config
Docker/Unraid Deployment Notes
[!NOTE] SQLite Database Permissions: SQLite requires write permissions on both the database file and its directory. Incorrect permissions may cause runtime errors on Docker/Unraid setups.
Automatic Fix: The Docker image now automatically creates the complete directory structure (
/marchat/server/) with all necessary subdirectories (config, db, data, plugins) and sets proper ownership at startup. This resolves permission issues that previously required manual intervention.Manual Fix (if needed): Create the required directories and ensure proper ownership:
mkdir -p ./config/db chown -R 1000:1000 ./config/db chmod 775 ./config/dbThe container user (UID 1000) must match the ownership of these folders/files.
Source Installation
Prerequisites:
- Go 1.23+ (download)
- For Linux clipboard support:
sudo apt install xclip(Ubuntu/Debian) orsudo yum install xclip(RHEL/CentOS)
Build from source:
git clone https://github.com/Cod-e-Codes/marchat.git
cd marchat
go mod tidy
go build -o marchat-server ./cmd/server
go build -o marchat-client ./client
chmod +x marchat-server marchat-client
Quick Start
1. (Recommended) Generate Secure Admin Key
For security, generate a strong random key to use as your admin key. This step is recommended but you can set any non-empty string as the admin key.
openssl rand -hex 32
2. Start Server
# Set environment variables
export MARCHAT_ADMIN_KEY="your-generated-key"
export MARCHAT_USERS="admin1,admin2"
# Start server
./marchat-server
3. Connect Client
# Connect as admin
./marchat-client --username admin1 --admin --admin-key your-generated-key --server ws://localhost:8080/ws
# Connect as regular user
./marchat-client --username user1 --server ws://localhost:8080/ws
Configuration
Environment Variables
| Variable | Required | Default | Description |
|---|---|---|---|
MARCHAT_ADMIN_KEY |
Yes | - | Admin authentication key |
MARCHAT_USERS |
Yes | - | Comma-separated admin usernames |
MARCHAT_PORT |
No | 8080 |
Server port |
MARCHAT_DB_PATH |
No | ./config/marchat.db |
Database file path |
MARCHAT_LOG_LEVEL |
No | info |
Log level (debug, info, warn, error) |
MARCHAT_CONFIG_DIR |
No | Auto-detected | Custom config directory |
MARCHAT_TLS_CERT_FILE |
No | - | Path to TLS certificate file |
MARCHAT_TLS_KEY_FILE |
No | - | Path to TLS private key file |
Configuration File
Create config.json for client configuration:
{
"username": "your-username",
"server_url": "ws://localhost:8080/ws",
"theme": "patriot",
"twenty_four_hour": true
}
TLS Support
TLS (Transport Layer Security) enables secure WebSocket connections using wss:// instead of ws://. This is essential for production deployments and when exposing the server over the internet.
When to Use TLS
- Public deployments: When the server is accessible from the internet
- Production environments: For enhanced security and privacy
- Corporate networks: When required by security policies
- HTTPS reverse proxies: When behind nginx, traefik, or similar
Enabling TLS
TLS is optional but recommended for secure deployments. To enable TLS:
- Obtain SSL/TLS certificates (self-signed for testing, CA-signed for production)
- Set environment variables:
export MARCHAT_TLS_CERT_FILE="/path/to/cert.pem" export MARCHAT_TLS_KEY_FILE="/path/to/key.pem" - Start the server - it will automatically detect TLS configuration
Example Configuration
With TLS (recommended for production):
# Generate self-signed certificate for testing
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes
# Set environment variables
export MARCHAT_ADMIN_KEY="your-secure-key"
export MARCHAT_USERS="admin1,admin2"
export MARCHAT_TLS_CERT_FILE="./cert.pem"
export MARCHAT_TLS_KEY_FILE="./key.pem"
# Start server (will show wss:// in banner)
./marchat-server
Without TLS (development/testing):
# No TLS certificates set
export MARCHAT_ADMIN_KEY="your-secure-key"
export MARCHAT_USERS="admin1,admin2"
# Start server (will show ws:// in banner)
./marchat-server
Client Connection
The client connection URL automatically reflects the server's TLS status:
- TLS enabled: Connect to
wss://host:port/ws - TLS disabled: Connect to
ws://host:port/ws
The server banner displays the correct WebSocket URL scheme based on TLS configuration.
Usage
Basic Commands
| Command | Description | Example |
|---|---|---|
:theme <name> |
Switch theme | :theme patriot |
:time |
Toggle 12/24-hour format | :time |
:clear |
Clear chat buffer | :clear |
:sendfile <path> |
Send file (<1MB) | :sendfile document.txt |
:savefile <name> |
Save received file | :savefile received.txt |
Plugin Commands
| Command | Description | Admin Only |
|---|---|---|
:store |
Open plugin store | No |
:plugin list |
List installed plugins | No |
:plugin install <name> |
Install plugin | No |
:plugin uninstall <name> |
Uninstall plugin | Yes |
Admin Commands
| Command | Description | Example |
|---|---|---|
:cleardb |
Wipe server database | :cleardb |
:kick <username> |
Disconnect user | :kick user1 |
:ban <username> |
Ban user for 24h with improved user experience after unban | :ban user1 |
:unban <username> |
Remove user ban with clean message history restoration | :unban user1 |
Connect as admin:
./marchat-client --username admin1 --admin --admin-key your-key --server ws://localhost:8080/ws
E2E Encryption Commands
| Command | Description | Example |
|---|---|---|
:showkey |
Display public key | :showkey |
:addkey <user> <key> |
Add user's public key | :addkey alice <base64-key> |
Enable E2E encryption:
./marchat-client --e2e --keystore-passphrase your-passphrase --username alice
Security
Critical Security Warnings
[!WARNING] Change default admin key immediately The default admin key
changemeis insecure. Generate a secure key:
openssl rand -hex 32
Security Best Practices
-
Generate Secure Keys:
# Admin key openssl rand -hex 32 # JWT secret (optional) openssl rand -base64 32 -
Secure File Permissions:
# Secure database file chmod 600 ./config/marchat.db # Secure config directory chmod 700 ./config -
Production Deployment:
- Use
wss://for secure WebSocket connections - Implement reverse proxy (nginx/traefik)
- Restrict server access to trusted networks
- Use Docker secrets for sensitive environment variables
- Use
E2E Encryption
When enabled, E2E encryption provides:
- Forward Secrecy: Unique session keys per conversation
- Server Privacy: Server cannot read encrypted messages
- Key Management: Local encrypted keystore with passphrase protection
Troubleshooting
Common Issues
| Issue | Solution |
|---|---|
| Connection failed | Verify server URL uses ws:// or wss:// |
| TLS certificate errors | Ensure certificate and key files are readable and valid |
| Admin commands not working | Ensure --admin flag and correct --admin-key |
| Clipboard not working (Linux) | Install xclip: sudo apt install xclip |
| Permission denied (Docker) | Rebuild with correct UID/GID: docker-compose build --build-arg USER_ID=$(id -u) |
| Port already in use | Change port: export MARCHAT_PORT=8081 |
| Database migration fails | Ensure proper database file permissions and backup before building from source |
| Message history missing after update | Expected behavior - user message states reset for improved ban/unban experience |
| Server fails to start after source build | Check database permissions and consider manual schema migration |
Network Connectivity
Local Network:
# Ensure server binds to all interfaces
export MARCHAT_PORT=8080
./marchat-server
Roadmap
See the project roadmap for planned features, performance enhancements, and future development goals.
Getting Help
- GitHub Issues: Report bugs
- GitHub Discussions: Ask questions
- Documentation: Plugin Ecosystem
- Security: Security Policy
Contributing
We welcome contributions! See the contribution guidelines for:
- Development setup
- Code style guidelines
- Pull request process
Quick Start for Contributors:
git clone https://github.com/Cod-e-Codes/marchat.git
cd marchat
go mod tidy
go test ./...
Appreciation
Special thanks to these wonderful communities and bloggers for featuring and supporting marchat:
- Self-Host Weekly by Ethan Sholly
- mtkblogs.com by Reggie
License: MIT License
Commercial Support: Contact cod.e.codes.dev@gmail.com

