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
- Ban History Gaps
- 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.2 includes breaking changes that require database migration.
What's Changed
v0.3.0-beta.2 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.2:
# Linux (amd64)
wget https://github.com/Cod-e-Codes/marchat/releases/download/v0.3.0-beta.2/marchat-v0.3.0-beta.2-linux-amd64.zip
unzip marchat-v0.3.0-beta.2-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.2/marchat-v0.3.0-beta.2-darwin-amd64.zip
unzip marchat-v0.3.0-beta.2-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.2/marchat-v0.3.0-beta.2-android-arm64.zip
unzip marchat-v0.3.0-beta.2-android-arm64.zip
chmod +x marchat-server marchat-client
Docker Installation
Pull from Docker Hub:
# Latest release
docker pull codecodesxyz/marchat:v0.3.0-beta.2
# 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.2
Using Docker Compose:
# docker-compose.yml
version: '3.8'
services:
marchat:
image: codecodesxyz/marchat:v0.3.0-beta.2
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 |
MARCHAT_BAN_GAPS_HISTORY |
No | false |
Enable ban history gaps (prevents banned users from seeing messages during ban periods) |
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.
Ban History Gaps
The ban history gaps feature prevents banned users from seeing messages that were sent during their ban periods. This creates a more effective moderation experience by ensuring users cannot access conversation history from when they were excluded from the chat.
How It Works
When enabled, the system:
- Tracks ban events in a dedicated
ban_historytable - Records ban/unban timestamps with admin attribution
- Filters message history for users with ban records
- Maintains performance by only filtering for users who have been banned
Enabling Ban History Gaps
Set the environment variable to enable this feature:
# Enable ban history gaps
export MARCHAT_BAN_GAPS_HISTORY=true
# Start server with feature enabled
./marchat-server
Behavior Examples
With Ban History Gaps Enabled:
- User gets banned → cannot see new messages
- User gets unbanned → reconnects and sees only messages sent after their unban
- Messages sent during ban period are permanently hidden from that user
With Ban History Gaps Disabled (default):
- User gets banned → cannot see new messages
- User gets unbanned → reconnects and sees all messages (including those sent during ban)
- Standard behavior maintained for backward compatibility
Performance Considerations
- Minimal impact on users who have never been banned
- Database queries only run for users with ban history
- Automatic cleanup of expired ban records
- Indexed queries for efficient ban period lookups
Database Schema
The feature adds a new ban_history table:
CREATE TABLE ban_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL,
banned_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
unbanned_at DATETIME,
banned_by TEXT NOT NULL
);
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 |
[!NOTE] Ban History Gaps: When
MARCHAT_BAN_GAPS_HISTORY=trueis enabled, banned users cannot see messages sent during their ban periods. This creates a more effective moderation experience.
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 |
| Ban history gaps not working | Ensure MARCHAT_BAN_GAPS_HISTORY=true is set and database has ban_history table |
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

