# Architecture ## Overview pulse-lets-go — балансировщик телефонной нагрузки на базе FreeSWITCH + NATS. Агенты PBX отправляют метрики в NATS каждые 5 секунд. Балансировщик вычисляет weighted score готовности каждой ноды, кэширует лучшую и отдаёт её FreeSWITCH через `/api/route` для следующего звонка. ## Data Flow ``` ┌─────────────────────┐ ESL/AMI ┌──────────────────────┐ │ PBX-01 │──────────▶│ pulse-lets-go-agent │ │ (FS or Asterisk) │ collect │ (on each PBX node) │ ├─────────────────────┤ ├──────────────────────┤ │ PBX-02 │──────────▶│ pulse-lets-go-agent │ │ (FS or Asterisk) │ ├──────────────────────┤ ├─────────────────────┤ ├──────────────────────┤ │ PBX-N │──────────▶│ pulse-lets-go-agent │ │ (FS or Asterisk) │ └─────────┬────────────┘ └─────────────────────┘ │ pulse.metrics. │ every 5s ▼ ┌──────────────┐ │ NATS Server │ │ nats://:4222 │ └──────┬───────┘ │ ▼ ┌──────────────────────┐ │ pulse-lets-go │ │ │ │ ┌────────────────┐ │ │ │ NATS Sub │ │ │ │ (pulse.metrics)│ │ │ └────────┬───────┘ │ │ ▼ │ │ ┌────────────────┐ │ │ │ Engine │ │ │ │ (scoring) │──│──▶ /api/route │ └────────┬───────┘ │ ↓ │ ▼ │ FreeSWITCH bridge │ ┌────────────────┐ │ sip:pbx-03:5060 │ │ REST API │──│──▶ /api/nodes │ │ (JWT auth) │ │ /api/trunks │ └────────┬───────┘ │ /api/users │ ▼ │ │ ┌────────────────┐ │ │ │ WebSocket │──│──▶ SvelteKit UI │ └────────────────┘ │ (ws://:8080/ws/metrics) │ ┌────────────────┐ │ │ │ ESL Client │──│──▶ FreeSWITCH ESL │ │ (gateways) │ │ :8021 │ └────────────────┘ │ │ ┌────────────────┐ │ │ │ Monitoring │──│──▶ /api/monitoring/prometheus │ │ (API-key) │ │ /api/monitoring/zabbix │ └────────────────┘ │ └──────────────────────┘ ``` ## Components | Component | Role | |-----------|------| | **pulse-lets-go** | Central balancer: NATS subscriber, scoring engine, REST API, WebSocket hub, ESL client | | **pulse-lets-go-agent** | Per-PBX metrics collector (FreeSWITCH ESL / Asterisk AMI) | | **NATS** | Message bus — `pulse.metrics.` subject | | **FreeSWITCH** | Softswitch — uses `route.lua` + `mod_curl` to query `/api/route` | | **SvelteKit UI** | Dashboard with real-time metrics via WebSocket | ## Tech Stack | Component | Technology | |-----------|-----------| | Backend | Go 1.26, stdlib `net/http` + `http.ServeMux` | | Frontend | SvelteKit 2 + Tailwind CSS 4 + Lucide | | Auth | JWT (golang-jwt/v5, HS256) + bcrypt | | Message bus | NATS (nats.go) | | Storage | JSON files (`data/`) | | ESL | Raw TCP (no external libraries) | | Deployment | Single binary + systemd | ## Data Flow Summary 1. **Agent** collects metrics from PBX (ESL/AMI + /proc) every 5s 2. **Agent** publishes `pulse.metrics.` to NATS 3. **Balancer** NATS subscriber receives metrics, updates in-memory node state 4. **Scoring engine** recalculates scores, caches best node 5. **FreeSWITCH** dialplan calls `/api/route` via `mod_curl` 6. **Balancer** returns best node's `sip_gateway` (O(1) from cache) 7. **FreeSWITCH** bridges the call to the returned gateway ## Project Structure ``` pulse-lets-go/ ├── cmd/ │ ├── pulse-lets-go/ # Main entry point │ ├── pulse-lets-go-agent/ # Metrics collector agent (FS / Asterisk) │ ├── emulator/ # Metrics emulator for testing │ └── siptest/ # SIP tester for e2e checks ├── internal/ │ ├── api/ # HTTP handlers (auth, nodes, trunks, users, monitoring, ws, route) │ ├── config/ # JSON config manager (atomic save, thread-safe) │ ├── engine/ # Scoring engine + router (sync.RWMutex, best-node cache) │ ├── esl/ # FreeSWITCH ESL client (raw TCP) │ ├── ami/ # Asterisk AMI client (raw TCP) │ ├── log/ # ASCII metrics logger with rotation │ ├── models/ # Data types (NodeMetric, NodeState, Trunk, User, ...) │ └── nats/ # NATS subscriber ├── contrib/ # route.lua, agent configs ├── deploy/ # systemd unit ├── web/ # SvelteKit frontend ├── data/ # Runtime JSON files (gitignored) └── docs/ # Documentation ```