Prepare QF100 pilot and Debian app deploy

This commit is contained in:
Wira Basalamah
2026-06-04 11:20:16 +07:00
parent 648e77cee9
commit 8a2e202606
17 changed files with 1135 additions and 216 deletions

View File

@ -1,251 +1,316 @@
# Codex Handoff - QRIS Soundbox Platform
Tanggal update: 2026-05-29, Asia/Jakarta.
Tanggal update: 2026-06-03, Asia/Jakarta.
Dokumen ini adalah snapshot kerja terakhir untuk melanjutkan project tanpa perlu membaca ulang seluruh chat.
## Status Terakhir
- Estimasi MVP / early pilot: 92-94%.
- Estimasi production-ready penuh: 82-85%.
- Platform sudah bukan prototype docs-only. Backend, UI operasional, migration, smoke test, rate limiting, audit logging, async export, runbook, dan script deployment sudah tersedia.
- Fokus terakhir yang selesai: rate limiting + security polish, login audit, admin audit UI real-data, placeholder nav cleanup, dan runbook/checklist produksi.
- Worktree kemungkinan masih dirty karena banyak perubahan aktif. Jangan revert perubahan yang tidak eksplisit diminta.
- Fokus hari ini bergeser dari production readiness umum ke integrasi device soundbox QF100.
- Folder SDK lokal `QF100-60s-l511-SecondApp-260107/` ditemukan dan dianalisis. Folder ini sengaja tidak dimasukkan git.
- Backend sekarang sudah punya adapter awal untuk firmware QF100 sample:
- config server vendor-compatible;
- payload MQTT payment success format QF100;
- smoke script backend untuk static + dynamic QF100.
- Target milestone berikutnya: dua soundbox real, satu static dan satu dynamic, bisa ambil config dari backend, connect MQTT, lalu static payment test bunyi dan tercatat di dashboard.
- Worktree aktif/dirty karena perubahan backend QF100 dan update handoff. Jangan revert perubahan yang tidak eksplisit diminta.
## Verifikasi Terakhir
- `npm run typecheck`: pass.
- `npm run db:migrate`: pass dan idempotent sampai migration `003_export_job_storage.sql`.
- `npm audit --json`: pass, 0 vulnerability.
- `npm run ui:qa`: pass setelah cleanup placeholder navigation.
- `npm run smoke:e2e`: pass setelah rate limiting dan login audit.
- Quick rate limit test: pass. Login admin salah pertama menghasilkan `401` dengan `RateLimit-Remaining: 0`, request berikutnya menghasilkan `429 RATE_LIMITED`.
- Quick login audit test: pass. Event `admin.login.success`, `admin.login.failed`, `merchant.login.success`, dan `merchant.login.failed` tercatat.
- Quick audit UI API test: pass. `GET /admin/audit-logs?action_contains=.login.&limit=10` mengembalikan event login.
- Production-like env check dummy: pass via `npm run deploy:check-env`, hanya warning opsional untuk `MQTT_SUBSCRIBE`.
- Staging/load/MQTT real sebelumnya sudah pernah diverifikasi: load level 2 1610 requests 0 errors, MQTT broker `mqtts://mqtt.iptek.co:8883` publish/subscribe OK.
- `npm run typecheck`: pass setelah perubahan QF100.
- `npm install`: sudah dijalankan untuk memasang dependency lokal; perubahan lockfile accidental sudah dibalik.
- `npm run smoke:qf100`: script sudah dibuat, belum dijalankan end-to-end di DB/server lokal pada turn ini.
- Verifikasi lama yang masih relevan:
- `npm run db:migrate`: sebelumnya pass dan idempotent sampai migration `003_export_job_storage.sql`.
- `npm audit --json`: sebelumnya pass, 0 vulnerability.
- `npm run ui:qa`: sebelumnya pass.
- `npm run smoke:e2e`: sebelumnya pass.
- Real MQTT smoke sebelumnya pernah pass terhadap `mqtts://broker.bizone.id:8883`.
## Implementasi Selesai
## SDK QF100 Yang Ditemukan
### 1. Auth, RBAC, dan Security
- Folder lokal: `QF100-60s-l511-SecondApp-260107/`.
- Sudah ditambahkan ke `.gitignore`:
- `QF100-60s-l511-SecondApp-260107/`
- Struktur penting:
- `docs/Config Server.docx`: kontrak config server vendor.
- `docs/Cloud Speaker API Spec V2.8.7.pdf`: API spec cloud speaker.
- `app/source/MainApp/globalDefine.h`: device model, demo mode, `CONFIG_ADDR`.
- `app/source/MainApp/demo.c`: alur config server, MQTT connect/subscribe, parse payment payload.
- `app/source/MainApp/demo.h`: MQTT TLS/QoS/clean session/cert config.
- `app/source/MainApp/main.c`: boot flow dan monitor network/MQTT.
- `app/inc/MercuryMqtt.h`: header MQTT SDK.
- `app/release/user_app.bin`: firmware build existing.
- Admin session login tersedia lewat `/admin/login`, `/admin/logout`, `/admin/me`.
- Merchant session login tersedia lewat `/merchant/login`, `/merchant/logout`, `/merchant/me`.
- Legacy dev auth bisa dimatikan via env dan production check memblokir konfigurasi yang tidak aman.
- Admin dan merchant bootstrap script tersedia:
- `scripts/create-admin-user.mjs`
- `scripts/create-merchant-user.mjs`
- Password policy bootstrap diperketat:
- minimal 14 karakter;
- wajib lowercase, uppercase, angka, dan simbol;
- menolak kata mudah ditebak seperti product/default/password/admin/merchant/qris/soundbox.
- Rate limiting middleware baru:
- [src/shared/middleware/rateLimit.ts](/home/wira/work/codex/qris-soundbox-platform/src/shared/middleware/rateLimit.ts)
- dipasang ke `/admin/login`, `/merchant/login`, admin write routes, `/device`, dan `/integrations`.
- Env security baru:
- `TRUST_PROXY`
- `JSON_BODY_LIMIT`
- `RATE_LIMIT_ENABLED`
- `RATE_LIMIT_AUTH_WINDOW_MS`
- `RATE_LIMIT_AUTH_MAX`
- `RATE_LIMIT_ADMIN_WRITE_WINDOW_MS`
- `RATE_LIMIT_ADMIN_WRITE_MAX`
- `RATE_LIMIT_WRITE_WINDOW_MS`
- `RATE_LIMIT_WRITE_MAX`
- Error code baru `RATE_LIMITED` di [src/shared/errors/index.ts](/home/wira/work/codex/qris-soundbox-platform/src/shared/errors/index.ts).
## Kesimpulan SDK QF100
### 2. Audit, Monitoring, dan Logging
- SDK ini memungkinkan develop/patch aplikasi firmware sendiri, tetapi bentuknya embedded C firmware app, bukan SDK backend.
- Strategi dipilih: firmware hanya hardcode URL config server backend kita, bukan hardcode broker MQTT.
- Firmware sample boot lalu call `CONFIG_ADDR` ke endpoint vendor `/speaker/dev-config`.
- Request config berisi field seperti:
- `dev-model`
- `item-number`
- `dev-sn`
- `fw-version`
- `fw-build`
- `app-config-version`
- `imei`
- `imsi`
- `iccid`
- Response yang firmware cari memiliki blok:
- `mqtt.broker-ip`
- `mqtt.broker-port`
- `mqtt.client-id`
- `mqtt.user-name`
- `mqtt.password`
- `mqtt.subscribe-topic`
- `mqtt.keep-alive`
- Payment success payload yang firmware sample bisa bunyikan:
```json
{
"header": {
"category": 1
},
"data": {
"pay-amount": 15000
}
}
```
- `MQTT_TLS_ENABLE` di SDK sample masih `0`. Jika broker production memakai `mqtts://...:8883`, firmware perlu patch TLS atau pilot perlu listener non-TLS terbatas.
- Audit logging login admin:
- `admin.login.success`
- `admin.login.failed`
- Audit logging login merchant:
- `merchant.login.success`
- `merchant.login.failed`
- `auditLogStore` mendukung `actor_type: merchant`.
- Filter audit baru `action_contains` tersedia di:
- [src/shared/store/auditLogStore.ts](/home/wira/work/codex/qris-soundbox-platform/src/shared/store/auditLogStore.ts)
- [src/routes/admin.ts](/home/wira/work/codex/qris-soundbox-platform/src/routes/admin.ts)
- Admin audit UI sudah memakai real API, bukan mock:
- [ui/admin-system-audit-logs/index.html](/home/wira/work/codex/qris-soundbox-platform/ui/admin-system-audit-logs/index.html)
- Audit UI memiliki filter action/entity/date/search, preset login events, KPI count, dan drawer JSON detail.
- Observability/health sebelumnya sudah tersedia:
- `/health`
- `/health/ready`
- `/admin/observability/summary`
- `/admin/observability/dead-letter-replays`
- `/admin/observability/mqtt-status`
## Implementasi QF100 Backend Hari Ini
### 3. MQTT dan Device Operations
### 1. Config Server Vendor-Compatible
- MQTT worker dan policy production sudah diperketat.
- Wildcard subscribe default production dicegah oleh env check.
- MQTT ACL tooling tersedia:
- `scripts/check-mqtt-acl.mjs`
- `scripts/smoke-mqtt-acl.mjs`
- `scripts/provision-mqtt-device.mjs`
- Package scripts:
- `npm run mqtt:provision-device`
- `npm run mqtt:check-acl`
- `npm run smoke:mqtt-acl`
- `npm run smoke:mqtt-real`
- Real MQTT smoke pernah pass dengan broker `mqtts://mqtt.iptek.co:8883`.
- Route baru:
- `GET /speaker/dev-config`
- File:
- `src/routes/speaker.ts`
- mounted di `src/app.ts`
- Perilaku:
- menerima query/body vendor;
- lookup device memakai `dev-sn` -> `devices.serial_number`;
- jika device aktif, update:
- `vendor`
- `model`
- `communication_mode`
- `last_seen_at`
- `firmware_version`
- mencatat heartbeat `state: config_pull`;
- balas JSON vendor top-level, bukan wrapper internal `successResponse`.
- Error vendor style:
- `1001`: `dev-sn` kosong;
- `1002`: device tidak terdaftar atau inactive;
- `1003`: broker MQTT belum dikonfigurasi.
### 4. Settlement, Reconciliation, dan Finance Ops
### 2. Env QF100
- Settlement batch, merchant settlement history, reconciliation management, adjustment approval, dan device technical detail UI sudah tersedia.
- Admin reconciliation UI sudah menggunakan async export flow dan export history.
- Key UI pages:
- [ui/admin-reconciliation-management/index.html](/home/wira/work/codex/qris-soundbox-platform/ui/admin-reconciliation-management/index.html)
- [ui/settlement-batch-management/index.html](/home/wira/work/codex/qris-soundbox-platform/ui/settlement-batch-management/index.html)
- [ui/merchant-settlement-history/index.html](/home/wira/work/codex/qris-soundbox-platform/ui/merchant-settlement-history/index.html)
- [ui/device-technical-detail/index.html](/home/wira/work/codex/qris-soundbox-platform/ui/device-technical-detail/index.html)
- Placeholder `href="#"` sudah dibersihkan dari UI yang masuk QA.
- Env baru di `src/config/env.ts` dan `.env.example`:
- `QF100_MQTT_BROKER_HOST`
- `QF100_MQTT_BROKER_PORT`
- `QF100_MQTT_USERNAME`
- `QF100_MQTT_PASSWORD`
- `QF100_MQTT_KEEP_ALIVE_SECONDS`
- Jika host/port QF100 kosong, endpoint mencoba parse dari `MQTT_BROKER_URL`.
- Catatan penting: password MQTT yang dikirim ke QF100 diambil dari `QF100_MQTT_PASSWORD` atau fallback `MQTT_PASSWORD`. Sistem saat ini menyimpan credential device sebagai fingerprint, jadi plaintext per-device tidak bisa dibaca ulang dari DB.
### 5. Async Export dan Storage
### 3. Payload MQTT QF100
- Async export job sudah tersedia untuk settlement adjustment export.
- Export job worker:
- [src/shared/services/exportJobWorker.ts](/home/wira/work/codex/qris-soundbox-platform/src/shared/services/exportJobWorker.ts)
- Export job store:
- [src/shared/store/exportJobStore.ts](/home/wira/work/codex/qris-soundbox-platform/src/shared/store/exportJobStore.ts)
- Migration:
- [migrations/002_export_jobs.sql](/home/wira/work/codex/qris-soundbox-platform/migrations/002_export_jobs.sql)
- [migrations/003_export_job_storage.sql](/home/wira/work/codex/qris-soundbox-platform/migrations/003_export_job_storage.sql)
- Export file storage memakai `EXPORT_STORAGE_DIR`, dengan metadata path/size/expiry.
- Export retention cleanup tersedia via worker.
- Admin endpoints:
- `POST /admin/exports/settlement-adjustments`
- `GET /admin/exports`
- `GET /admin/exports/:jobId`
- `GET /admin/exports/:jobId/download`
- File:
- `src/shared/services/mqttPublisher.ts`
- `src/shared/orchestrators/notificationOrchestrator.ts`
- Topic QF100:
- `devices/{deviceId}/downlink/qf100`
- Adapter memilih format QF100 jika:
- `device.model` mengandung `QF100`; atau
- `capability_profile_json.mqtt_payload_profile`, `protocol_profile`, atau `vendor_protocol` bernilai `qf100`.
- Downlink payment sekarang juga dicatat ke `mqtt_messages`, sehingga dashboard device detail bisa melihat topic/payload yang dikirim.
### 6. Deployment, Backup, Restore, dan Runbook
### 4. Device Store dan Schema
- Production env checker diperketat:
- [scripts/check-production-env.mjs](/home/wira/work/codex/qris-soundbox-platform/scripts/check-production-env.mjs)
- Backup/restore tooling:
- [scripts/backup-production.mjs](/home/wira/work/codex/qris-soundbox-platform/scripts/backup-production.mjs)
- [scripts/restore-plan.mjs](/home/wira/work/codex/qris-soundbox-platform/scripts/restore-plan.mjs)
- [scripts/restore-drill-validate.mjs](/home/wira/work/codex/qris-soundbox-platform/scripts/restore-drill-validate.mjs)
- Load testing/report tooling:
- [scripts/load-test.mjs](/home/wira/work/codex/qris-soundbox-platform/scripts/load-test.mjs)
- [scripts/run-staging-load-report.mjs](/home/wira/work/codex/qris-soundbox-platform/scripts/run-staging-load-report.mjs)
- Operational docs baru:
- [OPERATIONAL_RUNBOOK.md](/home/wira/work/codex/qris-soundbox-platform/OPERATIONAL_RUNBOOK.md)
- [PILOT_EXECUTION_CHECKLIST.md](/home/wira/work/codex/qris-soundbox-platform/PILOT_EXECUTION_CHECKLIST.md)
- [EXPORT_STORAGE_READINESS.md](/home/wira/work/codex/qris-soundbox-platform/EXPORT_STORAGE_READINESS.md)
- README dan deployment readiness docs sudah direferensikan ke runbook/checklist tersebut.
- Lookup baru:
- `getDeviceBySerialNumber(serialNumber)` di `src/shared/store/deviceStore.ts`.
- Schema bootstrap menambahkan index:
- `idx_devices_serial_number`.
## Endpoint Penting
### 5. Smoke QF100
- Script baru:
- `scripts/smoke-qf100-adapter.mjs`
- Package script baru:
- `npm run smoke:qf100`
- Script ini:
- create merchant/outlet/terminal/device static;
- create merchant/outlet/terminal/device dynamic MQTT;
- set `model: QF100` dan `capability_profile_json.mqtt_payload_profile: qf100`;
- hit `/speaker/dev-config` untuk dua SN;
- validasi MQTT config vendor;
- trigger static QRIS paid callback;
- validasi `mqtt_messages` downlink QF100 payload `category: 1`;
- trigger backend dynamic QR MQTT flow untuk device dynamic.
- Untuk memakai SN real:
```bash
QF100_STATIC_SN=<sn-static> \
QF100_DYNAMIC_SN=<sn-dynamic> \
BASE_URL=http://127.0.0.1:3000 \
npm run smoke:qf100
```
## Device Flow Yang Disepakati
### Static Soundbox
1. Device boot.
2. Firmware call backend `/speaker/dev-config`.
3. Backend balas MQTT config.
4. Device connect MQTT dan subscribe `devices/{deviceId}/downlink/qf100`.
5. QRIS callback paid masuk backend.
6. Backend publish payload QF100 `category: 1`.
7. Device bunyi nominal.
8. Dashboard melihat transaction, notification, mqtt message, last seen/config pull.
### Dynamic Soundbox
- Prinsip: trigger tetap via MQTT, bukan polling terus-menerus.
- HTTP polling dynamic QR dianggap tidak cocok untuk skala 20 ribu device karena `gap=30s` berarti sekitar 40 ribu request/menit.
- Desain lanjutan:
1. Backend/admin/merchant membuat dynamic QR request.
2. Backend publish MQTT command ke device.
3. Device menampilkan QR langsung atau HTTP fetch QR detail dari URL.
4. Payment callback tetap jadi source of truth.
5. Payment success tetap via MQTT `category: 1`.
- Firmware dynamic handler belum dipatch. Perlu tambah handler misalnya `category == 10` di `demo.c`.
## Dashboard Yang Perlu Disiapkan Berikutnya
Prioritas UI/dashboard untuk test dua soundbox real:
1. Device detail QF100 panel:
- SN;
- model/profile;
- config pull terakhir;
- broker host/port yang dikirim;
- client-id;
- subscribe-topic;
- keepalive.
2. Test payment button:
- nominal quick actions, misalnya Rp1.000 dan Rp15.000;
- create dummy tx/callback internal;
- tampilkan notification dan MQTT payload result.
3. MQTT timeline:
- direction;
- topic;
- message_type;
- payload JSON;
- publish_status/reason;
- timestamp.
4. Dynamic QR panel:
- input nominal;
- create dynamic QR;
- tampilkan QR payload/status;
- nanti `Send to Soundbox` via MQTT command setelah firmware handler siap.
5. Ops summary:
- total soundbox;
- online/stale/offline;
- config pull terbaru;
- payment notification sent/failed;
- dynamic QR active/paid/expired.
## Endpoint Penting Saat Ini
- Health:
- `GET /health`
- `GET /health/ready`
- `GET /health/deep`
- QF100 vendor config:
- `GET /speaker/dev-config`
- Admin auth/session:
- `POST /admin/login`
- `POST /admin/logout`
- `GET /admin/me`
- Admin audit/observability:
- `GET /admin/audit-logs`
- Admin device:
- `POST /admin/devices`
- `GET /admin/devices`
- `GET /admin/devices/{id}`
- `POST /admin/devices/{id}/credentials/rotate`
- `POST /admin/devices/{id}/bind`
- `POST /admin/devices/{id}/unbind`
- `GET /admin/devices/{id}/mqtt-messages`
- `GET /admin/devices/{id}/notifications`
- Device API:
- `POST /device/heartbeat`
- `POST /device/transactions/dynamic-qr`
- `POST /device/mqtt/uplink/dynamic-qr/request`
- `GET /device/config`
- `POST /device/config/ack`
- Integrations:
- `POST /integrations/qris/callback`
- Observability:
- `GET /admin/observability/summary`
- `GET /admin/observability/dead-letter-replays`
- `GET /admin/observability/mqtt-status`
- Admin export:
- `POST /admin/exports/settlement-adjustments`
- `GET /admin/exports`
- `GET /admin/exports/:jobId`
- `GET /admin/exports/:jobId/download`
- Merchant auth/session:
- `POST /merchant/login`
- `POST /merchant/logout`
- `GET /merchant/me`
- Device and integration routes remain rate-limited for write-heavy paths:
- `/device`
- `/integrations`
## Package Scripts Penting
- `npm run typecheck`
- `npm run db:migrate`
- `npm run smoke:qf100`
- `npm run smoke:e2e`
- `npm run smoke:mqtt-real`
- `npm run smoke:mqtt-acl`
- `npm run ui:qa`
- `npm run deploy:check-env`
- `npm run load:test`
- `npm run load:test:staging`
- `npm run backup:production`
- `npm run restore:plan`
- `npm run restore:validate`
- `npm run admin:create-user`
- `npm run merchant:create-user`
- `npm run mqtt:provision-device`
- `npm run mqtt:check-acl`
- `npm run smoke:mqtt-acl`
- `npm run smoke:mqtt-real`
- `npm run admin:create-user`
- `npm run merchant:create-user`
## File Kunci yang Sering Disentuh
## File Kunci Yang Sering Disentuh
- App bootstrap: [src/app.ts](/home/wira/work/codex/qris-soundbox-platform/src/app.ts)
- Env config: [src/config/env.ts](/home/wira/work/codex/qris-soundbox-platform/src/config/env.ts)
- Admin routes: [src/routes/admin.ts](/home/wira/work/codex/qris-soundbox-platform/src/routes/admin.ts)
- Merchant routes: [src/routes/merchant.ts](/home/wira/work/codex/qris-soundbox-platform/src/routes/merchant.ts)
- Audit store: [src/shared/store/auditLogStore.ts](/home/wira/work/codex/qris-soundbox-platform/src/shared/store/auditLogStore.ts)
- Export worker: [src/shared/services/exportJobWorker.ts](/home/wira/work/codex/qris-soundbox-platform/src/shared/services/exportJobWorker.ts)
- Export store: [src/shared/store/exportJobStore.ts](/home/wira/work/codex/qris-soundbox-platform/src/shared/store/exportJobStore.ts)
- Rate limit middleware: [src/shared/middleware/rateLimit.ts](/home/wira/work/codex/qris-soundbox-platform/src/shared/middleware/rateLimit.ts)
- UI QA script: [scripts/ui-qa-check.mjs](/home/wira/work/codex/qris-soundbox-platform/scripts/ui-qa-check.mjs)
- Admin audit UI: [ui/admin-system-audit-logs/index.html](/home/wira/work/codex/qris-soundbox-platform/ui/admin-system-audit-logs/index.html)
- Env sample: [.env.example](/home/wira/work/codex/qris-soundbox-platform/.env.example)
## Decision Log Ringkas
- D-026 sampai D-049: dasar auth, merchant/admin flows, migration, UI awal, dan smoke testing.
- D-050 sampai D-059: production hardening awal, MQTT policy, finance/reconciliation UI, settlement flows.
- D-060 sampai D-069: merchant auth productionization, DB migration idempotent, monitoring/logging, load test, async export.
- D-070 sampai D-074: export storage/history, MQTT ACL, backup/restore, staging load report.
- D-075 sampai D-080: rate limiting/security polish, login audit, audit UI real data, UI QA cleanup, runbook/checklist produksi.
Rujukan utama: [DECISIONS_LOG.md](/home/wira/work/codex/qris-soundbox-platform/DECISIONS_LOG.md).
- App bootstrap: `src/app.ts`
- Env config: `src/config/env.ts`
- QF100 config route: `src/routes/speaker.ts`
- Device route: `src/routes/device.ts`
- Admin route: `src/routes/admin.ts`
- MQTT publisher: `src/shared/services/mqttPublisher.ts`
- MQTT subscriber: `src/shared/services/mqttSubscriber.ts`
- Notification orchestrator: `src/shared/orchestrators/notificationOrchestrator.ts`
- Device store: `src/shared/store/deviceStore.ts`
- MQTT message store: `src/shared/store/mqttMessageStore.ts`
- Heartbeat store: `src/shared/store/heartbeatStore.ts`
- Schema bootstrap: `src/shared/db/pool.ts`
- QF100 smoke: `scripts/smoke-qf100-adapter.mjs`
- Env sample: `.env.example`
## Sisa Gap Utama
1. Eksekusi staging nyata dari checklist:
- deploy dengan env final;
- jalankan `deploy:check-env`, migration, smoke, UI QA, load report;
- simpan artefak hasil staging.
2. Pilot real device:
- provisioning device real;
- validasi MQTT ACL per device;
- transaksi QRIS test end-to-end;
- validasi soundbox delivery dan dead-letter handling.
3. Restore drill nyata:
- backup production/staging;
- restore ke database disposable;
- jalankan `restore:validate`;
- dokumentasikan RTO/RPO aktual.
4. Export storage production topology:
- pastikan `EXPORT_STORAGE_DIR` durable, absolute, writable, dan di-backup;
- jika multi-node, perlu shared filesystem/object storage strategy.
5. Manual visual QA:
- buka halaman admin utama di browser;
- cek layout mobile/desktop;
- cek login/session expiry state;
- cek empty/error/loading state.
6. Operational readiness:
- isi PIC, escalation contact, broker credential, backup location, dan pilot merchant list di runbook/checklist.
## Prioritas Lanjutan Disarankan
1. Jalankan full staging rehearsal dari `PILOT_EXECUTION_CHECKLIST.md`.
2. Lakukan manual visual QA admin UI dengan browser.
3. Jalankan restore drill sungguhan pada database disposable.
4. Finalisasi export storage production strategy.
5. Siapkan pilot real merchant/device dan rekam hasilnya di runbook.
1. Jalankan `npm run smoke:qf100` terhadap backend + DB lokal/staging.
2. Register dua device real:
- static SN -> device static;
- dynamic SN -> device dynamic.
3. Patch firmware QF100:
- `CONFIG_ADDR` ke backend kita;
- TLS setting sesuai broker.
4. Test static device real:
- config pull;
- MQTT connect;
- payment success bunyi;
- dashboard melihat downlink.
5. Patch firmware dynamic:
- tambah handler command dynamic QR, kemungkinan `category == 10`;
- pilih direct QR payload vs HTTP fetch detail.
6. Siapkan dashboard QF100 detail/test panel.
7. Putuskan credential strategy production:
- shared pilot password saat ini cukup untuk lab;
- production sebaiknya credential per-device yang bisa diprovisioning aman.
8. Tetap perlu staging rehearsal, restore drill, export storage strategy, dan manual visual QA dari handoff lama.
## Catatan Penting
- Jangan hidupkan legacy auth di production.
- Jangan gunakan wildcard MQTT subscribe di production kecuali sedang maintenance terkontrol.
- `EXPORT_STORAGE_DIR` harus absolute path dan durable untuk production.
- Rate limiting sekarang aktif secara default jika `RATE_LIMIT_ENABLED=true`; hati-hati saat smoke test berulang pada login endpoint.
- Folder SDK QF100 adalah artefak lokal dan jangan dimasukkan git.
- Jangan hardcode broker credential production di firmware.
- Firmware cukup hardcode config server URL; broker/topic/credential dikirim dari backend.
- Jangan gunakan wildcard MQTT subscribe di production kecuali maintenance terkontrol.
- Jika memakai MQTT TLS (`mqtts://...:8883`), firmware QF100 perlu `MQTT_TLS_ENABLE=1` dan sertifikat yang sesuai.
- Rate limiting aktif default; `/speaker` memakai limiter device.
- `CODEX_HANDOFF.md` ini adalah snapshot operasional terbaru; untuk detail historis keputusan, baca `DECISIONS_LOG.md`.