digital-gateway-notifier
Notification gateway: turning Kafka alarms into WhatsApp and Telegram messages
A service that consumes alarm events from Kafka and delivers them to the people who actually act on them, with recipient management, per-user channel preferences, and an audit trail written back to Kafka.
- Role
- Solo project. Kafka consumer, channel integrations, backend API, dashboard, and container topology.
- Stack
- Python
- FastAPI
- confluent-kafka
- PostgreSQL
- React
- Docker Compose
- Kafka: alarm topic
- Worker consumer
- Route by priority
- Telegram + WhatsApp
- Kafka: notification logs
Read left to right: each stage feeds the next. The prose sections below describe the same pipeline in full.
Problem
- Alarms already reach Kafka, but the people who must act on them do not read Kafka topics. They read WhatsApp and Telegram.
- A raw topic subscription also has no notion of who should be notified for which class of alarm, or through which channel.
Constraints
- Delivery targets are third-party services, so the gateway has to tolerate a channel that is down or not yet authenticated.
- Recipients and channel preferences change independently of the alarm stream, so they belong in a database rather than in configuration files.
- Alarm traffic is bursty, and notification calls are slow relative to message consumption, so the consumer must not stall behind them.
- The dashboard and the consumer both need health signals, since a silently dead consumer looks identical to a quiet plant.
Architecture and data flow
- Inbound: a worker process consumes alarm messages from the alarm topic in its own consumer group.
- Routing: recipients are selected by priority level and by the channel each user enabled, read from PostgreSQL.
- Delivery: Telegram goes through the bot integration, and WhatsApp goes through a WAHA HTTP API instance rather than the official Business API.
- State: PostgreSQL holds recipients, Telegram chat registrations, and preferences, with tables created on startup by the database manager.
- Control plane: a FastAPI backend exposes JWT-authenticated endpoints, and a React dashboard edits recipients and preferences.
- Audit: every delivery result is published to a notification-logs topic, so history is available to the dashboard from the same substrate as the alarms.
- Supervision: a watchdog thread checks consumer health and forces the process to exit if the consumer thread dies, so the container restarts instead of idling.
Engineering decisions
- The asyncio event loop runs in a dedicated background thread, and the Telegram bot stays on the main thread because it needs the signal handlers.
- The watchdog deliberately opts for a hard process exit over in-place recovery, trading a container restart for the certainty that a wedged consumer cannot silently pass as healthy.
- Channel integrations sit behind a notifier package, so adding a channel does not touch the consumer.
- Notification history is published back to Kafka rather than written only to the database, keeping one transport in play for both ingestion and observability.
- Kafka authentication is optional and switches to SASL PLAIN only when credentials are present, so local development needs no broker auth.
- The GSM modem path is kept separate from the network notifiers, because its failure modes are physical rather than protocol-level.
Testing and verification
- Honest gap: the Python side has no test suite. The only test artifact in the repository is the Create React App default test file.
- There is no CI workflow, so nothing runs automatically on push.
- What the repository does make verifiable is structure and intent: the consumer wiring, the routing by priority, the channel adapters, the database manager, and the Docker Compose topology.
What is proven today
- A working multi-channel gateway with recipient management, per-user channel preferences, and a delivery audit trail.
- Containerized end to end, covering the backend, dashboard, worker, and WhatsApp service.
- What is not claimed: no throughput, reliability, or uptime figures. The README relies on marketing language that nothing in the repository measures, so it is not repeated here.
Source and references
Every claim on this page is traceable to the public repository, its tests, or its build output. Unverifiable numbers are deliberately omitted.