···11# Netdata Zulip Bot - Development Instructions
2233-This repository implements a Zulip bot that receives incoming webhook notifications from Netdata Cloud and posts the resulting notifications to a Zulip topic.
33+This repository implements a production-ready Zulip bot that receives incoming webhook notifications from Netdata Cloud and posts the resulting notifications to a Zulip topic.
4455## Core Requirements
66
+21
LICENSE.md
···11+MIT License
22+33+Copyright (c) 2025 Anil Madhavapeddy
44+55+Permission is hereby granted, free of charge, to any person obtaining a copy
66+of this software and associated documentation files (the "Software"), to deal
77+in the Software without restriction, including without limitation the rights
88+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
99+copies of the Software, and to permit persons to whom the Software is
1010+furnished to do so, subject to the following conditions:
1111+1212+The above copyright notice and this permission notice shall be included in all
1313+copies or substantial portions of the Software.
1414+1515+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1616+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1717+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1818+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1919+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2020+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2121+SOFTWARE.
+104-103
README.md
···11# Netdata Zulip Bot
2233-*100% vibe coded, use at your peril*
44-55-A webhook service that receives notifications from Netdata Cloud and forwards them to Zulip channels. Features HTTPS with Let's Encrypt certificates and mutual TLS authentication for secure communication with Netdata Cloud.
33+A production-ready webhook service that receives notifications from Netdata Cloud and forwards them to Zulip channels. Designed to run behind a reverse proxy (like Caddy) that handles HTTPS and mutual TLS authentication.
6475## Features
8699-- 🔐 **Automated SSL Certificates**: Built-in Let's Encrypt integration with automatic renewal
1010-- 🤝 **Mutual TLS**: Secure authentication with Netdata Cloud
77+- 🔗 **Reverse Proxy Ready**: HTTP service designed to run behind Caddy/nginx
88+- 🤝 **Mutual TLS Support**: When configured with reverse proxy
119- 📊 **Rich Formatting**: Beautiful Zulip messages with emojis and markdown
1210- 🏷️ **Topic Organization**: Automatic topic routing by severity level
1311- 📝 **Structured Logging**: JSON-structured logs for monitoring
1412- ⚡ **High Performance**: FastAPI-based webhook endpoint
1515-- 🚀 **Standalone**: No external dependencies like certbot required
1313+- 🔧 **Flexible Configuration**: Support for .zuliprc files or environment variables
1414+- ✅ **Webhook Verification**: Built-in Netdata challenge/response handling
16151716## Quick Start
1817···2120```bash
2221# Using uv (recommended)
2322uv sync
2424-2525-# Or using pip
2626-pip install -e .
2723```
28242925### 2. Create Configuration
30263127```bash
3228# Generate sample configuration files
3333-netdata-zulip-bot --create-config
2929+uv run netdata-zulip-bot --create-config
34303531# Copy and customize
3632cp .zuliprc.sample ~/.zuliprc
3333+cp .env.sample .env
3734```
38353936### 3. Configure Zulip Settings
···4845stream=netdata-alerts
4946```
50475151-### 4. Set Server Environment Variables
4848+### 4. Configure Environment Variables
4949+5050+Edit `.env` file or set environment variables:
52515352```bash
5454-export SERVER_DOMAIN=your-webhook-domain.com
5555-export SERVER_PORT=8443
5656-export SERVER_ENABLE_MTLS=true
5353+# Server configuration (HTTP only)
5454+export SERVER_HOST=0.0.0.0
5555+export SERVER_PORT=8080
57565858-# For automated SSL certificates (recommended)
5959-export SERVER_AUTO_CERT=true
6060-export SERVER_CERT_EMAIL=admin@example.com
6161-# Use staging for testing (optional)
6262-export SERVER_CERT_STAGING=false
5757+# Required: Netdata webhook challenge secret
5858+export SERVER_CHALLENGE_SECRET=your-challenge-secret-here
5959+6060+# Optional: Override Zulip stream
6161+export ZULIP_STREAM=netdata-alerts
6362```
64636564### 5. Run the Service
66656766```bash
6868-# With automated SSL certificates
6969-netdata-zulip-bot
6767+# Start the HTTP service
6868+uv run netdata-zulip-bot
70697171-# The bot will automatically:
7272-# 1. Obtain SSL certificates from Let's Encrypt
7373-# 2. Start the HTTPS server
7474-# 3. Renew certificates before expiration
7070+# Or with custom configuration
7171+uv run netdata-zulip-bot --zuliprc /path/to/.zuliprc
7272+7373+# The service runs on HTTP (default: localhost:8080)
7474+# Use a reverse proxy like Caddy for HTTPS and mutual TLS
7575```
76767777## Configuration
···101101export ZULIP_STREAM=netdata-alerts
102102```
103103104104-Use `--env-config` flag to use environment variables instead of zuliprc.
104104+Use the `--env-config` flag to use environment variables instead of zuliprc:
105105+106106+```bash
107107+uv run netdata-zulip-bot --env-config
108108+```
105109106110### Server Configuration
107111108112Set these environment variables:
109113110110-- `SERVER_DOMAIN`: Your public domain (required)
111114- `SERVER_HOST`: Bind address (default: `0.0.0.0`)
112112-- `SERVER_PORT`: HTTPS port (default: `8443`)
113113-- `SERVER_ENABLE_MTLS`: Enable mutual TLS (default: `true`)
115115+- `SERVER_PORT`: HTTP port (default: `8080`)
116116+- `SERVER_CHALLENGE_SECRET`: Netdata webhook challenge secret (required)
114117115115-#### Automated SSL Configuration (Recommended)
118118+### Reverse Proxy Setup
116119117117-- `SERVER_AUTO_CERT`: Enable automatic certificate management (default: `false`)
118118-- `SERVER_CERT_EMAIL`: Email for Let's Encrypt account (required when auto_cert is true)
119119-- `SERVER_CERT_PATH`: Directory for storing certificates (default: `./certs`)
120120-- `SERVER_CERT_STAGING`: Use Let's Encrypt staging server for testing (default: `false`)
121121-- `SERVER_ACME_PORT`: Port for ACME HTTP-01 challenge (default: `80`)
120120+The bot is designed to run behind a reverse proxy that handles HTTPS and mutual TLS:
122121123123-#### Manual SSL Configuration
122122+#### Using Caddy (Recommended)
124123125125-If not using automated certificates:
126126-- `SERVER_CERT_PATH`: Path to certificate directory
127127-- Place `fullchain.pem` and `privkey.pem` in `{SERVER_CERT_PATH}/{SERVER_DOMAIN}/`
124124+1. Update `Caddyfile` with your domain name
125125+2. Place Netdata CA certificate in `netdata-ca.pem`
126126+3. Run both services:
127127+128128+```bash
129129+# Start the bot
130130+uv run netdata-zulip-bot &
131131+132132+# Start Caddy
133133+caddy run --config Caddyfile
134134+```
135135+136136+#### Using Docker Compose
137137+138138+```bash
139139+docker-compose up -d
140140+```
128141129142## Message Format
130143···146159**Time:** 2024-01-15 14:30:00 UTC
147160148161**Details:** CPU usage has exceeded 90% for 5 minutes
162162+149163**Summary:** Critical alert: High CPU usage detected
150164151165[View Alert](https://app.netdata.cloud/spaces/...)
···171185172186### Systemd Service
173187174174-Create `/etc/systemd/system/netdata-zulip-bot.service`:
188188+See `examples/netdata-zulip-bot.service` for a complete systemd service configuration.
175189176176-```ini
177177-[Unit]
178178-Description=Netdata Zulip Bot
179179-After=network.target
190190+### Automated Setup
180191181181-[Service]
182182-Type=simple
183183-User=netdata-bot
184184-WorkingDirectory=/opt/netdata-zulip-bot
185185-Environment=SERVER_DOMAIN=your-domain.com
186186-ExecStart=/opt/netdata-zulip-bot/venv/bin/netdata-zulip-bot
187187-Restart=always
188188-RestartSec=5
192192+Use the provided setup script:
189193190190-[Install]
191191-WantedBy=multi-user.target
192192-```
193193-194194-Enable and start:
195194```bash
196196-sudo systemctl enable netdata-zulip-bot
197197-sudo systemctl start netdata-zulip-bot
195195+sudo ./scripts/setup.sh --domain your-domain.com --email admin@example.com
198196```
199197200198### Docker
201199202202-```dockerfile
203203-FROM python:3.11-slim
200200+The included `Dockerfile` and `docker-compose.yml` provide a complete setup with Caddy reverse proxy:
204201205205-WORKDIR /app
206206-COPY . .
207207-RUN pip install -e .
208208-209209-EXPOSE 8443
210210-211211-CMD ["netdata-zulip-bot"]
202202+```bash
203203+docker-compose up -d
212204```
213205214206## Security
215207216216-### SSL Certificate Management
208208+### Architecture
217209218218-The bot includes fully automated SSL certificate management:
210210+The bot uses a security-focused architecture:
219211220220-1. **Automatic Provisioning**: Obtains certificates from Let's Encrypt on first run
221221-2. **Automatic Renewal**: Checks daily and renews certificates 30 days before expiration
222222-3. **Zero Downtime**: Certificate renewal happens in the background
223223-4. **ACME HTTP-01 Challenge**: Built-in challenge server (requires port 80 access)
212212+1. **HTTP Backend**: Simple HTTP service with no direct internet exposure
213213+2. **Reverse Proxy**: Caddy handles HTTPS, certificates, and client authentication
214214+3. **Mutual TLS**: Client certificate validation at the reverse proxy level
224215225225-### Mutual TLS Authentication
216216+### Webhook Security
226217227227-The service supports mutual TLS to authenticate Netdata Cloud webhooks:
218218+- **Challenge/Response**: Built-in Netdata webhook verification using HMAC-SHA256
219219+- **Payload Validation**: Strict payload parsing and validation
220220+- **Request Logging**: Comprehensive logging of all webhook requests
221221+- **Error Handling**: Secure error responses without information disclosure
228222229229-1. **Server Certificate**: Automatically managed via built-in ACME client
230230-2. **Client Verification**: Validates Netdata's client certificate
231231-3. **CA Certificate**: Built-in Netdata CA certificate for client validation
223223+### SSL Certificate Management
232224233233-### Webhook Endpoint Security
225225+SSL certificates are managed by the reverse proxy (Caddy):
234226235235-- HTTPS-only communication
236236-- Request logging and monitoring
237237-- Payload validation and sanitization
238238-- Error handling without information disclosure
227227+1. **Automatic Provisioning**: Caddy obtains Let's Encrypt certificates
228228+2. **Automatic Renewal**: Built-in certificate renewal
229229+3. **Mutual TLS**: Client certificate validation using Netdata CA certificate
239230240231## Monitoring
241232···255246### Health Check
256247257248```bash
258258-curl -k https://your-domain.com:8443/health
249249+# Direct HTTP check (backend service)
250250+curl http://localhost:8080/health
251251+252252+# Through reverse proxy
253253+curl https://your-domain.com/health
259254```
260255261256Response:
···271266### Running Tests
272267273268```bash
274274-pytest
269269+uv run python -m pytest tests/ -v
275270```
276271277272### Code Formatting
278273279274```bash
280280-black .
281281-ruff check .
275275+uv run black .
276276+uv run ruff check .
282277```
283278284279### Local Development
285280286286-For development, you can disable HTTPS and mTLS:
281281+For development, you can run the HTTP service directly:
287282288283```bash
289289-export SERVER_ENABLE_MTLS=false
290290-# Use HTTP for testing (not recommended for production)
284284+# Set required environment variables
285285+export SERVER_CHALLENGE_SECRET=test-secret
286286+287287+# Run the service
288288+uv run netdata-zulip-bot
289289+290290+# Test webhook endpoint
291291+curl -X POST http://localhost:8080/webhook/netdata?crc_token=test123
291292```
292293293294## Troubleshooting
294295295296### Common Issues
296297297297-1. **Certificate Issues**
298298- - For automated certs: Ensure port 80 is accessible for ACME challenges
299299- - Domain must point to your server's IP address
300300- - Check `SERVER_CERT_EMAIL` is set for auto-cert mode
301301- - Use `SERVER_CERT_STAGING=true` for testing to avoid rate limits
298298+1. **Configuration Issues**
299299+ - Ensure `SERVER_CHALLENGE_SECRET` is set (required for Netdata webhook verification)
300300+ - Verify `.zuliprc` file contains all required fields
301301+ - Check that Zulip bot has permission to post to the configured stream
302302303303-2. **Zulip Connection Failed**
304304- - Verify API credentials in zuliprc
305305- - Test connection with Zulip's API
303303+2. **Reverse Proxy Issues**
304304+ - Ensure Caddy configuration uses correct domain name
305305+ - Verify Netdata CA certificate is properly configured
306306+ - Check that port 80 is accessible for Let's Encrypt challenges
3063073073083. **Webhook Not Receiving Data**
308308- - Check firewall settings for port 8443
309309- - Verify domain DNS resolution
310310- - Check Netdata Cloud webhook configuration
309309+ - Verify Netdata Cloud webhook URL points to your reverse proxy
310310+ - Check webhook challenge secret matches configuration
311311+ - Review service logs for error messages
311312312313### Logs
313314···318319319320## License
320321321321-MIT License - see LICENSE file for details.
322322+MIT License - see LICENSE file for details.