A web GUI and HTTP API gateway for smstools — the Linux SMS server that uses USB/serial modems.
Browse SMS folders, send messages, and monitor delivery in real-time through a modern web interface. Built with FastAPI + Svelte, deployed securely via Cloudflare Tunnel.
POST /send-sms with MD5 signature verification/var/spool/sms/outgoing/, smsd handles delivery| Component | Technology |
|---|---|
| Backend | FastAPI + Uvicorn |
| Frontend | Svelte 5 + Vite |
| Auth | JWT (python-jose) |
| Build | Bun |
| Deploy | systemd + Cloudflare Tunnel |
| CI/CD | GitHub Actions (self-hosted runner) |
| Python Deps | uv |
# Docker
docker compose up --build
# → http://localhost:8000
# Login with ADMIN_KEY from passkey.conf.example
POST /send-sms
Content-Type: application/x-www-form-urlencoded
| Name | Required | Description |
|---|---|---|
sdt |
Yes | Phone number |
noidungtinnhan |
Yes | SMS message content |
hash |
Yes | MD5 signature |
MD5("{sdt}&{noidungtinnhan}&{SECRET_KEY}")
curl -X POST https://sms.sonpython.com/send-sms \
-d "sdt=0901234567" \
-d "noidungtinnhan=Test SMS" \
-d "hash=$(echo -n '0901234567&Test SMS&YOUR_SECRET_KEY' | md5sum | cut -d' ' -f1)"
// Success
{"status": "OK", "file": "sms_1734539200_0901234567.sms"}
// Error
{"detail": "INVALID_HASH"} // 403
[smstools/smsd] ←→ /var/spool/sms/{incoming,outgoing,sent,failed,checked}
↕
[SMS Gateway] ←→ Web GUI (Svelte)
↕
HTTP API (/send-sms)
↕
Cloudflare Tunnel → Internet
The gateway reads/writes smstools spool directories. smsd daemon handles actual modem communication.
# 1. Clone to VM
git clone <repo> /opt/sms-api
# 2. Run pre-deploy setup (installs all deps)
sudo bash predeploy.sh michaelphan
# 3. Configure
nano /opt/sms-api/passkey.conf
# SECRET_KEY=<random-64-char>
# ADMIN_KEY=<random-64-char>
# 4. Setup Cloudflare Tunnel (see docs/deployment-guide.md)
# 5. Push to main → auto deploy
See docs/deployment-guide.md for full setup.
passkey.conf (never commit):
SECRET_KEY=your-secret-for-md5-and-jwt
ADMIN_KEY=your-admin-login-key
SMS_BASE_DIR=/var/spool/sms
passkey.conf)MIT