A lean 1-to-1 messenger built in Go with embedded frontend, WebSocket real-time chat, and SQLite storage.
- Single self-contained binary (frontend embedded)
- WebSocket messaging
- SQLite DB
- File uploads with configurable limit
- PWA frontend (RTL/Farsi ready)
- Backend: Go (Gin), WebSocket hub, JWT auth
- DB: SQLite file (default
/var/lib/payambar/payambar.dbin production) - Storage: Local uploads (default
/var/lib/payambar/uploads) - Frontend: Vuejs PWA served by the binary
Requirements: Debian/Ubuntu (systemd), curl, python3, tar, (unzip if the release is a zip), systemd.
Supported release targets: linux-amd64, linux-arm64.
curl -fsSL https://raw.githubusercontent.com/4xmen/payambar/main/install.sh | sudo bash -s -- --installUpgrade to the latest release:
curl -fsSL https://raw.githubusercontent.com/4xmen/payambar/main/install.sh | sudo bash -s -- --update--install creates everything from scratch:
- Fetches the latest release asset from GitHub and installs the
payambarbinary to/opt/payambar. - Creates system user
payambar, data dir/var/lib/payambar(uploads at/var/lib/payambar/uploads), env file/etc/payambar/payambar.env. - Seeds an empty SQLite DB file, installs a systemd unit, enables the service, and starts/restarts it.
- Default port:
8080. ChangePORTorJWT_SECRETin/etc/payambar/payambar.envthensudo systemctl restart payambar.
--update only replaces the binary and restarts the service:
- Your
.envconfig (/etc/payambar/payambar.env) and systemd unit are preserved — custom ports, secrets, and service settings are not overwritten.
Common commands:
sudo systemctl status payambar
sudo journalctl -u payambar -fmkdir payambar && cd payambar
curl -O https://raw.githubusercontent.com/4xmen/payambar/main/docker-compose.yml
cat > .env <<'EOF'
JWT_SECRET=change-me
# Optional:
# CORS_ORIGINS=https://yourdomain.com
# STUN_SERVERS=stun:stun.l.google.com:19302
EOF
docker-compose up -dOpen http://<server-ip>:8080.
Prereqs: Go 1.25+, Make, SQLite dev headers.
make build-all # builds frontend + backend → bin/payambar
PORT=8080 DATABASE_PATH=./data/payambar.db JWT_SECRET=dev-key ./bin/payambarEnvironment variables (used by the binary and the installer-generated env file):
| Variable | Default | Purpose |
|---|---|---|
PORT |
8080 | HTTP/WebSocket port |
ENVIRONMENT |
production | production or development |
DATABASE_PATH |
/var/lib/payambar/payambar.db (installer) | SQLite file path |
FILE_STORAGE_PATH |
/var/lib/payambar/uploads | Upload directory |
JWT_SECRET |
(randomly generated by installer) | Sign/verify JWT tokens |
CORS_ORIGINS |
* | Allowed origins |
MAX_UPLOAD_SIZE |
10485760 | Max upload bytes (10MB) |
STUN_SERVERS |
stun:stun.l.google.com:19302 | WebRTC STUN list |
TURN_SERVER, TURN_USERNAME, TURN_PASSWORD |
(empty) | Optional TURN (voice) — keep empty to disable |
VAPID_PUBLIC_KEY |
(generated by installer) | Web Push VAPID public key |
VAPID_PRIVATE_KEY |
(generated by installer) | Web Push VAPID private key |
PAYAMBAR_ENV_FILE |
(empty) | Optional explicit env-file path for CLI/server startup |
For CLI usage, config is resolved in this order:
- Process environment variables
PAYAMBAR_ENV_FILE(if set)/etc/payambar/payambar.env(installer default).envin current working directory
The binary supports operational CLI commands in addition to running the server:
payambar # start HTTP/WebSocket server
payambar status # print app/database/storage statistics
payambar status --json # same stats as JSONExample with local build output:
./bin/payambar status
./bin/payambar status --jsoncmd/payambar/ # Main entrypoint (embeds static assets)
frontend/ # PWA source
bin/ # Built binaries (make build-all)
data/ # Local dev data (SQLite/uploads)
install.sh # Linux installer (systemd)
docker-compose.yml # Container deployment
sudo systemctl status payambar
sudo systemctl restart payambar
sudo journalctl -u payambar -f- App listens on
PORT(default 8080). Expose/forward this port. - If enabling voice/TURN later, also open 3478 TCP/UDP and 49152-49252 UDP.
make devbuilds frontend and runs backend with local SQLite at./data/payambar.db.- Tests:
make test - Format:
make fmt
Payambar supports text-message E2EE v1 (files/calls are not part of E2EE).
- Per-device ECDH P-256 keypair generated in browser.
- Private key is optionally backed up to server encrypted with AES-GCM using a PBKDF2-SHA256 key derived from the login password (150k iterations). Password/derived key are never stored.
- Public key is published via
POST /api/keys/devices; the owner can restore viaGET /api/keys/devices/self. - Send flow: if recipient key exists → encrypt; if missing → warn once and send plaintext so messages stay readable (backward compatible).
make dev- Register/login two users in separate browsers.
- Send messages; confirm encrypted rows in DB:
sqlite3 ./data/payambar.db "select id,encrypted,e2ee_v,alg,length(ciphertext),length(content) from messages order by id desc limit 5;"encrypted=1, e2ee_v=1, alg= AES-256-GCM, content empty when encrypted.
If you log in on a new device with the same password, the private key is restored and old encrypted messages decrypt without manual export/import.
- Service not up:
sudo journalctl -u payambar -n 50 - DB locked: SQLite allows one writer; retry or migrate to Postgres for scale.
- WebSocket blocked: Ensure port 8080 open and CDN/proxy allows WebSockets.
MIT License
Payambar supports Web Push notifications for new messages when the user is offline.
- The installer auto-generates VAPID keys. For manual setup, generate keys and add to your env file:
# Generate with openssl: openssl ecparam -genkey -name prime256v1 -noout -out vapid_private.pem # Then extract keys (or use an online VAPID key generator)
- Users toggle push notifications on/off in their profile modal ("اعلان پیام جدید").
- Push only works on HTTPS or
localhost. make devincludes test VAPID keys for local development.
- See
docs/e2ee-text-v1.mdfor the proposed text-only E2EE v1 protocol and rollout plan.