A few months ago I got tired of giving my team's conversations to third-party servers. So I built XtermChat — a self-hosted chat system that runs entirely in the terminal.
Since then, it has grown into something I didn't originally plan: a full client-server chat system with a terminal TUI, a web admin panel, and a bot that monitors your VPS and sends alerts directly into your chat rooms.
This post is about what it became.
What XtermChat Is Now
At its core, XtermChat is still simple: a Python Flask server, a SQLite database, and a CLI client. But it now ships with three ways to use it.
1. Terminal TUI — the original interface. Run xtc start:chat @general and you get a full terminal UI with three panels, dual-focus mode, emoji shortcuts, link detection, and clipboard copy.
2. Web Admin Panel — run xtc start:web and open localhost:5000. Full admin panel with dashboard, room management, chat with link previews (OG image, title, description), and bot management.
3. Bot System — run xtc start:bot and a setup wizard walks you through configuring a monitoring bot that runs as a background process on your VPS.
The Bot System
This is the part I'm most excited about. The idea is simple: instead of setting up a separate alerting tool, XtermChat already has rooms and messaging — so why not use that infrastructure for server monitoring too?
$ xtc start:bot
┌──────────────────────────────────────┐
│ XTERMCHAT · BOT SETUP │
└──────────────────────────────────────┘
▸ SELECT ROOM
1. @general OPEN by KETUT
2. @alerts OPEN by KETUT
▸ Bot name: MONITOR
▸ SELECT BOT TASKS
1. Resource Monitor CPU, RAM, Disk usage
2. Process Monitor Watch if a service dies
3. Uptime Watchdog Ping a URL every N minutes
4. Port Checker Verify ports are open
5. SSL Cert Checker Alert before cert expires
6. Log Watcher Tail a log for keywords
...
▸ Select tasks: 1,3
✓ Config saved (bot_id: 1)
✓ Bot started on server (PID: 24891)
The bot runs as a background process on the VPS. It reads its config from the database, checks at the configured interval, and sends alerts as normal messages to any room you choose.
An alert looks like this in your chat:
⚠️ RESOURCE ALERT on srv1260384 [15 Mar 2026 08:30:00]
CPU 85% (limit 80%)
RAM 91% (limit 85%)
Because it's just a message in a room, you can see it from the terminal TUI, the web admin, or any client connected to the server.
The Architecture
The whole thing is two repos.
xtc-server — lives on your VPS:
xtc-server/
├── server.py Flask app, all API + bot routes
├── db.py SQLite init (users, rooms, messages, bots)
├── room.py Room CRUD, bcrypt passwords
├── connection.py Message save/retrieval
└── bot_runner.py Bot background process
xtc-client — lives on your machine:
xtc-client/
├── xtc.py CLI entry point, command dispatcher
├── utils.py Hardware UUID identity
├── commands/
│ ├── chat.py Terminal TUI
│ ├── bot.py Bot setup wizard
│ ├── bot_stop.py Stop/list bots
│ ├── listBots.py List all registered bots
│ └── deleteBot.py Delete bots
└── web/
├── app.py Flask web server (port 5000)
└── html/ 5 admin pages
The client sends a POST /bot/start to the server, which spawns bot_runner.py as a background process. The bot reads its config from the database and starts monitoring. Stop it with xtc stop:bot 1 which sends POST /bot/kill — the server kills the PID and updates the database.
The Web Admin
The web admin surprised me with how useful it turned out. I built it for team members who don't use the terminal, but I ended up using it myself because the chat bubble UI with link previews is genuinely nicer for long conversations.
The chat page fetches Open Graph metadata for any URL shared in a message — image, title, description, favicon. It's cached per session so the same URL isn't fetched twice.
One thing worth noting: the web admin uses a 5-digit PIN you choose at login, while the CLI uses a hardware UUID auto-generated from your machine. Both work with the same server and the same rooms. The difference is that CLI identity is device-bound (if you reinstall your OS, your PIN changes), while web identity is portable.
What I Learned
SQLite is underrated. The entire server state — users, rooms, messages, bot configs — lives in one file. Backup is cp xtc.db backup.db. For a team of 2–50 people on a $5 VPS, it's more than enough.
REST over WebSocket was the right call. Polling every 2 seconds is fine for this use case and dramatically simpler to implement, debug, and extend. The bot just makes HTTP requests too — same pattern everywhere.
Hardware UUID as identity is surprisingly clean. No passwords to remember. Your machine is your identity. The tradeoff is that switching devices requires an admin to reset your entry, but for a small team that's a non-issue.
Try It
- Server: github.com/dnysaz/xtc-server
- Client: github.com/dnysaz/xtc-client
- Docs: xtermchat.vercel.app
Setup takes about 10 minutes. The docs cover everything from server setup to HTTPS to the full bot task reference.
If you run a small team, manage a homelab, or just want your chat data to stay on your own infrastructure — give it a try. And if you find something broken or have a feature idea, issues and pull requests are very welcome.
Built with Python · Runs on a $5 VPS · MIT License
Top comments (0)