# MQTT Broker Mosquitto on Debian 13

Panduan operasional untuk menyiapkan broker MQTT awal platform QRIS Soundbox di Debian 13 dengan subdomain `mqtt.iptek.co`.

Keputusan arsitektur terkait:
- `D-026`: broker MQTT sungguhan ditunda sampai infrastruktur siap; simulator/outbox tetap dipakai selama transisi.
- `D-027`: broker awal memakai Mosquitto, dengan kontrak topic dan adapter backend tetap migration-ready ke EMQX/managed MQTT.

## Target Setup

- Broker: Eclipse Mosquitto.
- Domain: `mqtt.iptek.co`.
- MQTT TLS publik: `8883/tcp`.
- MQTT local-only: `1883/tcp` pada `127.0.0.1`.
- TLS: Let's Encrypt.
- Auth: username/password.
- Authorization: ACL topic per user/device.
- Anonymous access: disabled.

## DNS dan Paket

Pastikan DNS `mqtt.iptek.co` sudah mengarah ke public IP server.

```bash
dig +short mqtt.iptek.co
curl -4 ifconfig.me
```

Install paket:

```bash
sudo apt update
sudo apt install -y mosquitto mosquitto-clients certbot ufw
sudo systemctl enable --now mosquitto
```

## Firewall

```bash
sudo ufw default deny incoming
sudo ufw default allow outgoing

sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 8883/tcp

sudo ufw enable
sudo ufw status verbose
```

Jangan buka `1883/tcp` ke internet. Listener `1883` hanya untuk localhost/internal test.

## Sertifikat TLS

Ambil sertifikat Let's Encrypt:

```bash
sudo certbot certonly --standalone -d mqtt.iptek.co
```

Copy sertifikat ke lokasi yang bisa dibaca Mosquitto:

```bash
sudo install -d -o root -g mosquitto -m 750 /etc/mosquitto/certs

sudo install -o root -g mosquitto -m 640 \
  /etc/letsencrypt/live/mqtt.iptek.co/fullchain.pem \
  /etc/mosquitto/certs/fullchain.pem

sudo install -o root -g mosquitto -m 640 \
  /etc/letsencrypt/live/mqtt.iptek.co/privkey.pem \
  /etc/mosquitto/certs/privkey.pem
```

Buat renewal hook:

```bash
sudo nano /etc/letsencrypt/renewal-hooks/deploy/mosquitto-cert-copy.sh
```

Isi:

```bash
#!/usr/bin/env bash
set -euo pipefail

DOMAIN="mqtt.iptek.co"

install -o root -g mosquitto -m 640 \
  "/etc/letsencrypt/live/${DOMAIN}/fullchain.pem" \
  /etc/mosquitto/certs/fullchain.pem

install -o root -g mosquitto -m 640 \
  "/etc/letsencrypt/live/${DOMAIN}/privkey.pem" \
  /etc/mosquitto/certs/privkey.pem

systemctl reload mosquitto
```

Aktifkan:

```bash
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/mosquitto-cert-copy.sh
```

## User dan Password

Buat user backend dan device test:

```bash
sudo mosquitto_passwd -c /etc/mosquitto/passwd qris-backend
sudo mosquitto_passwd /etc/mosquitto/passwd DEVICE_UUID_FROM_PLATFORM
sudo chown root:mosquitto /etc/mosquitto/passwd
sudo chmod 640 /etc/mosquitto/passwd
```

Rekomendasi:
- `qris-backend` dipakai backend platform.
- Username device memakai `device_id` UUID dari platform, supaya ACL `pattern` bisa mengikat topic `devices/{deviceId}/...` ke user/device.

## ACL Topic

Buat file ACL:

```bash
sudo nano /etc/mosquitto/acl
```

Isi awal:

```conf
user qris-backend
topic readwrite devices/#

pattern write devices/%u/uplink/#
pattern read devices/%u/downlink/#
pattern write devices/%u/heartbeat
```

Permission:

```bash
sudo chown root:mosquitto /etc/mosquitto/acl
sudo chmod 640 /etc/mosquitto/acl
```

## Konfigurasi Mosquitto

Buat file:

```bash
sudo nano /etc/mosquitto/conf.d/qris.conf
```

Isi minimal:

```conf
per_listener_settings true

listener 8883 0.0.0.0
protocol mqtt
allow_anonymous false
password_file /etc/mosquitto/passwd
acl_file /etc/mosquitto/acl
certfile /etc/mosquitto/certs/fullchain.pem
keyfile /etc/mosquitto/certs/privkey.pem

listener 1883 127.0.0.1
protocol mqtt
allow_anonymous false
password_file /etc/mosquitto/passwd
acl_file /etc/mosquitto/acl
```

Catatan Debian:
- Jangan set ulang `persistence`, `persistence_location`, `log_dest`, atau `log_type` di `conf.d/qris.conf` jika sudah ada di `/etc/mosquitto/mosquitto.conf`.
- Jika muncul error `Duplicate persistence_location value`, hapus `persistence` dan `persistence_location` dari `qris.conf`.
- Jika muncul error `Duplicate "log_dest file" value`, hapus blok logging dari `qris.conf`.

Test config:

```bash
sudo mosquitto -c /etc/mosquitto/mosquitto.conf -v
```

Jika tidak ada error, tekan `Ctrl+C`, lalu restart service:

```bash
sudo systemctl restart mosquitto
sudo systemctl status mosquitto --no-pager
```

Pastikan listener terbuka:

```bash
sudo ss -lntp | grep mosquitto
```

Expected:
- `0.0.0.0:8883`
- `127.0.0.1:1883`

## Test Publish Subscribe

Terminal 1, subscribe sebagai backend:

```bash
mosquitto_sub \
  -h mqtt.iptek.co \
  -p 8883 \
  -u qris-backend \
  -P 'PASSWORD_BACKEND' \
  -t 'devices/DEVICE_UUID_FROM_PLATFORM/uplink/#' \
  -v
```

Terminal 2, publish sebagai device:

```bash
mosquitto_pub \
  -h mqtt.iptek.co \
  -p 8883 \
  -u DEVICE_UUID_FROM_PLATFORM \
  -P 'PASSWORD_DEVICE' \
  -t 'devices/DEVICE_UUID_FROM_PLATFORM/uplink/dynamic-qr/request' \
  -m '{"request_id":"test-001","amount":10000}'
```

Test ACL negatif:

```bash
mosquitto_pub \
  -h mqtt.iptek.co \
  -p 8883 \
  -u DEVICE_UUID_FROM_PLATFORM \
  -P 'PASSWORD_DEVICE' \
  -t 'devices/OTHER_DEVICE_UUID/uplink/dynamic-qr/request' \
  -m '{}'
```

Pesan ke topic device lain harus ditolak atau tidak sampai ke subscriber.

## Monitoring

```bash
sudo journalctl -u mosquitto -f
sudo tail -f /var/log/mosquitto/mosquitto.log
sudo ss -lntp | grep mosquitto
```

Test renewal TLS:

```bash
sudo certbot renew --dry-run
```

## Environment Backend Nanti

Saat adapter broker sungguhan dipasang ke platform:

```env
MQTT_PUBLISH_MODE=broker
MQTT_BROKER_URL=mqtts://mqtt.iptek.co:8883
MQTT_USERNAME=qris-backend
MQTT_PASSWORD=...
MQTT_CLIENT_ID=qris-platform-backend
MQTT_CONNECT_TIMEOUT_MS=5000
MQTT_TLS=true
```

Topic kontrak yang harus dipertahankan:

```text
devices/{deviceId}/uplink/dynamic-qr/request
devices/{deviceId}/downlink/dynamic-qr/response
devices/{deviceId}/downlink/payment/success
devices/{deviceId}/downlink/config/push
devices/{deviceId}/uplink/config/ack
devices/{deviceId}/heartbeat
```

## Provisioning Credential Device

Credential MQTT device dibuat dari aplikasi, lalu password satu kali tersebut dimasukkan ke Mosquitto. Jalankan backend platform lebih dulu, lalu rotate credential:

```bash
ADMIN_TOKEN=admin-dev-token \
npm run mqtt:provision-device -- \
  --base-url http://127.0.0.1:3000 \
  --device-id DEVICE_UUID_FROM_PLATFORM
```

Output berisi:
- `mqtt_username`: sama dengan `device_id`.
- `mqtt_password`: secret satu kali untuk dimasukkan ke broker dan device.
- `mosquitto_commands`: perintah yang bisa dijalankan di server broker.

Jika script dijalankan langsung di host broker:

```bash
ADMIN_TOKEN=admin-dev-token \
npm run mqtt:provision-device -- \
  --base-url http://127.0.0.1:3000 \
  --device-id DEVICE_UUID_FROM_PLATFORM \
  --apply-local
```

Setelah password dimasukkan, reload Mosquitto:

```bash
sudo systemctl reload mosquitto
```

Catatan penting:
- Platform hanya menyimpan fingerprint secret, bukan plaintext password.
- Jika password hilang, lakukan rotate ulang dan update device + broker.
- ACL `pattern` bergantung pada username yang sama dengan `device_id`; jangan mengganti username device menjadi `device_code` kecuali kontrak topic/ACL ikut diubah.

## Migration Ready Notes

- Jangan mengunci business logic di fitur Mosquitto-specific.
- Credential device tetap dikelola domain platform.
- Topic tetap `devices/{deviceId}/...`.
- `mqtt_messages` tetap menjadi outbox/trace awal di aplikasi.
- Benchmark sebelum milestone 20k, 50k, dan 100k connected device.
