# Notifier Microservice A production-ready notification delivery microservice that supports multiple notification channels (Email, Slack, Ntfy, Stdout) with both REST and gRPC APIs running simultaneously. [![Go Version](https://img.shields.io/badge/go-1.23.2-blue.svg)](https://golang.org) [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE) ## Overview Notifier is a cloud-native microservice designed to handle all your application's notification needs through a single, unified API. Send emails, Slack messages, push notifications, and more with a consistent interface and robust delivery guarantees. Perfect for: - Microservices architectures needing centralized notifications - Applications requiring multiple notification channels - Systems needing reliable async notification delivery - Teams wanting to standardize notification handling ## Features ### Core Capabilities - πŸ”” **Multi-Channel Support**: Email (SMTP), Slack, Ntfy.sh, and Stdout - πŸš€ **Dual API**: REST and gRPC running simultaneously in one process - πŸ“¦ **Queue-Based**: Async processing with configurable worker pools - πŸ”„ **Retry Logic**: Exponential backoff with configurable attempts - ⚑ **Priority Levels**: Low, Normal, High, and Critical - πŸ“Š **Batch Operations**: Send multiple notifications efficiently - 🎯 **Status Tracking**: Monitor notification lifecycle and delivery ### Production Features - βš™οΈ **Configuration**: Viper-based with environment variable support - 🐳 **Containerized**: Multi-stage Docker builds with non-root user - ☸️ **Kubernetes Ready**: Complete manifests with HPA, health checks, and RBAC - πŸ”’ **Secure**: Token-based auth for ntfy, TLS support, secret management - πŸ“ˆ **Observable**: Health endpoints, metrics support, structured logging - πŸ”Œ **Extensible**: Clean interfaces for adding new notifiers ## Quick Start ### 1. Install and Run ```bash # Clone and install dependencies git clone https://github.com/igodwin/notifier cd notifier go mod tidy # Build and run make build ./bin/server ``` Server starts with: - **REST API** on `http://localhost:8080` - **gRPC API** on `localhost:50051` - **Stdout notifier** enabled by default **Run in different modes:** ```bash ./bin/server # Both REST and gRPC (default) NOTIFIER_SERVER_MODE=rest ./bin/server # REST only NOTIFIER_SERVER_MODE=grpc ./bin/server # gRPC only ``` ### 2. Send Your First Notification ```bash curl -X POST http://localhost:8080/api/v1/notifications \ -H "Content-Type: application/json" \ -d '{ "type": "stdout", "subject": "Hello from Notifier!", "body": "Your first notification", "recipients": ["console"] }' ``` The notification appears immediately in the server output. βœ… ### 3. Check Status ```bash # Health check curl http://localhost:8080/health # Statistics curl http://localhost:8080/api/v1/stats ``` ## Configuration ### Basic Setup Create `config.yaml` in the project root: ```yaml server: mode: "both" # Options: both, rest, grpc rest_port: 8080 grpc_port: 50051 host: "0.0.0.0" queue: type: "local" # Local in-memory queue worker_count: 10 # Concurrent workers retry_attempts: 3 retry_backoff: "exponential" notifiers: stdout: true # Always enabled for testing ``` ### Email Notifications (SMTP) Supports multiple email accounts with named instances: ```yaml notifiers: smtp: # Personal account (default) personal: host: "smtp.gmail.com" port: 587 username: "your-email@gmail.com" password: "your-app-password" from: "personal@gmail.com" use_tls: true default: true # Work account work: host: "smtp.company.com" port: 587 username: "you@company.com" password: "your-work-password" from: "notifications@company.com" use_tls: true ``` **Usage:** ```bash # Uses default account (personal) curl -X POST http://localhost:8080/api/v1/notifications \ -H "Content-Type: application/json" \ -d '{ "type": "email", "subject": "Welcome!", "body": "Thanks for signing up", "recipients": ["user@example.com"] }' # Specify account explicitly curl -X POST http://localhost:8080/api/v1/notifications \ -H "Content-Type: application/json" \ -d '{ "type": "email", "account": "work", "subject": "Welcome!", "body": "Thanks for signing up", "recipients": ["user@example.com"] }' ``` ### Slack Notifications Supports multiple workspaces with named instances: ```yaml notifiers: slack: # Main workspace (default) main: webhook_url: "https://hooks.slack.com/services/YOUR/WEBHOOK/URL" username: "Notifier Bot" icon_emoji: ":bell:" default: true # Team workspace team-a: webhook_url: "https://hooks.slack.com/services/TEAM-A/WEBHOOK/URL" username: "Team A Bot" icon_emoji: ":rocket:" # Channel-specific webhooks webhooks: "#alerts": "https://hooks.slack.com/services/ALERTS/WEBHOOK" "#monitoring": "https://hooks.slack.com/services/MONITORING/WEBHOOK" ``` **Usage:** ```bash # Uses default workspace (main) curl -X POST http://localhost:8080/api/v1/notifications \ -H "Content-Type: application/json" \ -d '{ "type": "slack", "subject": "Deployment Complete", "body": "v2.0 deployed to production", "recipients": ["#alerts"] }' # Specify workspace explicitly curl -X POST http://localhost:8080/api/v1/notifications \ -H "Content-Type: application/json" \ -d '{ "type": "slack", "account": "team-a", "subject": "Deployment Complete", "body": "v2.0 deployed to production", "recipients": ["#alerts"] }' ``` ### Ntfy Push Notifications Supports multiple ntfy servers with named instances: ```yaml notifiers: ntfy: # Public ntfy.sh server (default) public: server_url: "https://ntfy.sh" token: "tk_your_access_token" # Optional, for private topics default_topic: "myapp-alerts" default: true # Private self-hosted server private: server_url: "https://ntfy.mycompany.com" username: "your-username" password: "your-password" default_topic: "company-alerts" insecure_skip_verify: false # Set true for self-signed certs ``` **Usage:** ```bash # Uses default server (public) curl -X POST http://localhost:8080/api/v1/notifications \ -H "Content-Type: application/json" \ -d '{ "type": "ntfy", "priority": 3, "subject": "Critical Alert", "body": "Server CPU at 95%", "recipients": ["alerts"], "metadata": { "tags": ["warning", "rotating_light"], "click": "https://dashboard.example.com" } }' # Specify server explicitly curl -X POST http://localhost:8080/api/v1/notifications \ -H "Content-Type: application/json" \ -d '{ "type": "ntfy", "account": "private", "subject": "Critical Alert", "body": "Server CPU at 95%", "recipients": ["alerts"] }' ``` See [docs/NTFY_GUIDE.md](docs/NTFY_GUIDE.md) for advanced ntfy features (action buttons, attachments, delays, etc.). ### Environment Variables Override any config with environment variables: ```bash export NOTIFIER_SERVER_MODE=both export NOTIFIER_SERVER_REST_PORT=8080 export NOTIFIER_QUEUE_WORKER_COUNT=20 export NOTIFIER_NOTIFIERS_SMTP_HOST=smtp.gmail.com export NOTIFIER_NOTIFIERS_SMTP_PASSWORD=secret export NOTIFIER_NOTIFIERS_NTFY_TOKEN=tk_your_token ``` Format: `NOTIFIER_
_` (use `_` for nested keys) ## API Reference ### REST Endpoints | Method | Endpoint | Description | |--------|----------|-------------| | `GET` | `/health` | Health check (always unauthenticated) | | `POST` | `/api/v1/notifications` | Send single notification (returns `202 Accepted`) | | `POST` | `/api/v1/notifications/batch` | Send multiple notifications (returns `202 Accepted`) | | `GET` | `/api/v1/notifications` | List notifications (with filters) | | `GET` | `/api/v1/notifications/{id}` | Get notification by ID | | `DELETE` | `/api/v1/notifications/{id}` | Cancel pending notification | | `POST` | `/api/v1/notifications/{id}/retry` | Retry failed notification | | `GET` | `/api/v1/notifiers` | List configured notifier types and accounts | | `GET` | `/api/v1/stats` | Get service statistics | When `auth.enabled` is set, all `/api/v1` routes require an API key (see [Authentication](#authentication)); `/health` is always open. #### Admin β€” API Key Management Registered only when authentication **and** a key store are configured. See [docs/KEY_MANAGEMENT.md](docs/KEY_MANAGEMENT.md) for the full workflow. | Method | Endpoint | Description | |--------|----------|-------------| | `POST` | `/api/v1/admin/keys` | Create an API key | | `GET` | `/api/v1/admin/keys` | List API keys | | `DELETE` | `/api/v1/admin/keys/{name}` | Revoke an API key | | `POST` | `/api/v1/admin/keys/{name}/rotate` | Rotate an API key | | `GET` | `/api/v1/admin/keys/{name}/audit` | Get a key's audit log | ### Request Format ```json { "type": "email|slack|ntfy|stdout", "priority": 0-3, "subject": "Notification title", "body": "Notification body", "recipients": ["email@example.com", "#channel", "topic"], "metadata": { "key": "value" }, "max_retries": 3 } ``` ### Response Format ```json { "result": { "notification_id": "uuid", "success": true, "message": "notification queued successfully", "sent_at": "2025-10-16T21:05:27Z" } } ``` ### Priority Levels - `0` - Low (background notifications) - `1` - Normal (default) - `2` - High (important updates) - `3` - Critical (urgent alerts) ### Batch Operations ```bash curl -X POST http://localhost:8080/api/v1/notifications/batch \ -H "Content-Type: application/json" \ -d '{ "notifications": [ {"type": "email", "subject": "Welcome", ...}, {"type": "slack", "subject": "Alert", ...} ] }' ``` ### Filtering Notifications ```bash # Get all sent email notifications curl "http://localhost:8080/api/v1/notifications?type=email&status=sent&limit=10" # Get recent failures curl "http://localhost:8080/api/v1/notifications?status=failed&limit=20" ``` Supported query parameters: `type` and `status` (both repeatable), `recipient` (repeatable), `limit`, and `offset`. ## gRPC API The gRPC service mirrors the REST API with full feature parity. The service is `notifier.v1.NotifierService` (RPCs: `SendNotification`, `SendBatchNotifications`, `GetNotification`, `ListNotifications`, `CancelNotification`, `RetryNotification`, `GetStats`, `GetNotifiers`, `HealthCheck`). See [api/grpc/notifier.proto](api/grpc/notifier.proto) for the full definitions. Server [reflection](https://github.com/grpc/grpc/blob/master/doc/server-reflection.md) is enabled, so tools like `grpcurl` work without a local copy of the proto: ```bash # List methods grpcurl -plaintext localhost:50051 list notifier.v1.NotifierService # Call GetNotifiers grpcurl -plaintext localhost:50051 notifier.v1.NotifierService/GetNotifiers ``` When `auth.enabled` is set, pass the API key as metadata (`-H "authorization: Bearer "`); see [docs/AUTH.md](docs/AUTH.md). **Generate Go code:** ```bash make proto-gen # or protoc --go_out=. --go-grpc_out=. api/grpc/notifier.proto ``` ## Authentication Authentication is **disabled by default** (`auth.enabled: false`). When enabled, the service uses API keys with optional role-based access control (RBAC) over both the REST and gRPC APIs: ```yaml auth: enabled: true default_rate_limit: 100 # requests per minute per key bootstrap: enabled: true # mint an admin key on first start print_to_stdout: false kubernetes_secret_name: notifier-admin-key kubernetes_secret_key: admin-key ``` - **REST:** send `Authorization: Bearer ` (or `X-API-Key: `). - **gRPC:** send an `authorization: Bearer ` metadata header. - Keys can be managed at runtime via the [admin endpoints](#admin--api-key-management). - An admin key can be bootstrapped to stdout, a file, or a Kubernetes Secret. See the guides for details: - [docs/AUTH_QUICK_START.md](docs/AUTH_QUICK_START.md) β€” 5-minute setup - [docs/AUTH.md](docs/AUTH.md) β€” full authentication & client examples - [docs/KEY_MANAGEMENT.md](docs/KEY_MANAGEMENT.md) β€” key lifecycle (create/rotate/revoke/audit) - [docs/RBAC.md](docs/RBAC.md) β€” role-based access to notifier accounts ## Deployment ### Docker **Build (single-arch, local):** ```bash make docker-build ``` **Build and push a multi-arch image (linux/amd64 + linux/arm64):** ```bash # Pushes $REGISTRY/$IMAGE:$VERSION and :latest. # Requires Docker buildx (bundled with Docker Desktop; on Linux also run # `docker run --rm --privileged tonistiigi/binfmt --install all` once to # register the QEMU emulators). REGISTRY=registry.example.com/org VERSION=v0.1.3 make docker-build ``` Override `IMAGE` (default `notifier`) or `PLATFORMS` (default `linux/amd64,linux/arm64`) as needed. **Run:** ```bash docker run -d \ --name notifier \ -p 8080:8080 \ -p 50051:50051 \ -v $(pwd)/config.yaml:/app/config.yaml:ro \ notifier:latest ``` **Run in different modes:** ```bash # Both REST and gRPC (default) docker run -d -p 8080:8080 -p 50051:50051 notifier:latest # REST only docker run -d -p 8080:8080 -e NOTIFIER_SERVER_MODE=rest notifier:latest # gRPC only docker run -d -p 50051:50051 -e NOTIFIER_SERVER_MODE=grpc notifier:latest ``` **Docker Compose:** ```bash docker-compose up -d ``` Includes optional services: Kafka, Prometheus, Grafana (commented out by default) ### Kubernetes (GitOps) The recommended way to run notifier in Kubernetes is from a GitOps repository (ArgoCD, Flux, or similar) that reconciles a Kustomize base + per-cluster overlay, rather than applying manifests by hand. A typical setup: - **Kustomize layout:** a `base/` with the Deployment, Service, ConfigMap, and routing resources, plus an overlay per cluster/environment that sets the namespace and pins the image tag. Pin an explicit version tag in the overlay β€” don't deploy `latest`. - **Secrets:** keep credentials out of git entirely. Use a secrets operator (e.g. Vault Secrets Operator, External Secrets) to materialize a Secret, and inject it via `envFrom` β€” notifier layers `NOTIFIER_*` environment variables over the mounted `config.yaml`, so secret fields can be left blank in the committed ConfigMap. - **Config:** mount `config.yaml` from a ConfigMap at `/app/config.yaml` with the non-secret settings (server mode, queue, notifier accounts). - **Topology:** run a **single replica** for now β€” the queue and notification state are in-memory, so multiple replicas won't share state (see Roadmap). Point startup/readiness/liveness probes at `/health` on the REST port, and use a restricted security context (non-root, read-only rootfs, seccomp `RuntimeDefault`, all capabilities dropped). One Service can expose both the HTTP and gRPC ports. - **Exposure:** with Gateway API, use an `HTTPRoute` matching the `/api/v1` and `/health` prefixes and a `GRPCRoute` matching the `notifier.v1.NotifierService` service. Keeping the two routes' matches disjoint lets REST and gRPC share one TLS-terminated hostname without shadowing each other; the gateway terminates TLS and speaks h2c to the pod's plaintext gRPC port. **Deploying a change:** ```bash # 1. Build and push a versioned multi-arch image REGISTRY=registry.example.com/org VERSION=v0.1.5 make docker-build # 2. Bump the pinned tag in your GitOps overlay (kustomization.yaml -> newTag) # 3. Commit and push β€” your GitOps controller syncs it ``` **Access for debugging:** ```bash kubectl -n port-forward svc/notifier 8080:80 50051:50051 ``` ### Reference manifests (`k8s/`) The `k8s/` directory in this repo contains **standalone example manifests** (Deployment, REST/gRPC/metrics Services, ConfigMap, HPA, Ingress, RBAC, secret template) for trying the service on a generic cluster with `kubectl apply -k k8s/`. They are illustrative starting points, not a production reference β€” a real GitOps deployment will differ (replica count, Gateway API vs. Ingress, operator-managed secrets vs. plain Secret manifests). ## Architecture ### High-Level Design ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ REST Client │────▢│ REST API β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ (port 8080) β”‚ β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ gRPC Client │────▢│ gRPC API β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ (port 50051) β”‚ β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜ β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Notification β”‚ β”‚ Service β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Queue (Local) │◀──┐ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β–Ό β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ Worker Pool β”‚β”€β”€β”€β”˜ β”‚ (10 workers) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β–Ό β–Ό β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ SMTP β”‚ β”‚ Slack β”‚ β”‚ Ntfy β”‚ β”‚Notifierβ”‚ β”‚ Notifier β”‚ β”‚ Notifier β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` ### Key Components - **API Layer**: REST (Gorilla mux) and gRPC (Protocol Buffers) - **Service Layer**: Business logic, validation, orchestration - **Queue**: In-memory with optional disk persistence (Kafka planned) - **Workers**: Concurrent processors with retry logic - **Notifiers**: Pluggable providers implementing `domain.Notifier` - **Config**: Viper-based with file + env var support See [ARCHITECTURE.md](ARCHITECTURE.md) for detailed design documentation. ## Project Structure ``` notifier/ β”œβ”€β”€ api/ β”‚ β”œβ”€β”€ grpc/ β”‚ β”‚ └── notifier.proto # gRPC service definition β”‚ └── rest/ β”‚ β”œβ”€β”€ handlers.go # HTTP handlers β”‚ β”œβ”€β”€ keys.go # API key management handlers β”‚ β”œβ”€β”€ router.go # Route configuration β”‚ └── types.go # Request/response types β”œβ”€β”€ cmd/ β”‚ └── server/main.go # Unified server (configurable mode) β”œβ”€β”€ internal/ β”‚ β”œβ”€β”€ auth/ # API key auth, RBAC, key store, bootstrap β”‚ β”œβ”€β”€ config/ β”‚ β”‚ └── config.go # Configuration management β”‚ β”œβ”€β”€ domain/ β”‚ β”‚ β”œβ”€β”€ notification.go # Core types β”‚ β”‚ β”œβ”€β”€ notifier.go # Notifier interface β”‚ β”‚ └── queue.go # Queue interface β”‚ β”œβ”€β”€ logging/ # Structured logger β”‚ β”œβ”€β”€ notifier/ β”‚ β”‚ β”œβ”€β”€ notifier.go # Factory & base β”‚ β”‚ β”œβ”€β”€ smtp.go # Email notifier β”‚ β”‚ β”œβ”€β”€ slack.go # Slack notifier β”‚ β”‚ β”œβ”€β”€ ntfy.go # Ntfy notifier β”‚ β”‚ └── stdout.go # Stdout notifier β”‚ β”œβ”€β”€ queue/ β”‚ β”‚ └── local.go # In-memory queue β”‚ └── service/ β”‚ └── service.go # Business logic β”œβ”€β”€ k8s/ β”‚ β”œβ”€β”€ deployment.yaml β”‚ β”œβ”€β”€ service.yaml β”‚ β”œβ”€β”€ configmap.yaml β”‚ β”œβ”€β”€ ingress.yaml β”‚ β”œβ”€β”€ hpa.yaml β”‚ └── kustomization.yaml β”œβ”€β”€ docs/ # Guides & references (see docs/INDEX.md) β”œβ”€β”€ config.yaml # Default configuration β”œβ”€β”€ docker-compose.yaml β”œβ”€β”€ Dockerfile β”œβ”€β”€ Makefile β”œβ”€β”€ QUICKSTART.md # API examples β”œβ”€β”€ ARCHITECTURE.md # Design documentation └── README.md # This file ``` ## Development ### Build Commands ```bash make build # Build server binary make run # Run server (both REST and gRPC) make run-rest # Run server in REST-only mode make run-grpc # Run server in gRPC-only mode make test # Run tests with race detector make test-coverage # Generate HTML coverage report make fmt # Format code with gofmt make vet # Run go vet make lint # Run golangci-lint make check # Run fmt-check + vet + mod verify make qa # Run all quality checks make proto-gen # Generate protobuf code make docker-build # Build Docker image (multi-arch + push when REGISTRY is set) make clean # Clean build artifacts make help # Show all available targets ``` ### Adding a New Notifier 1. Create `internal/notifier/mynotifier.go` 2. Implement `domain.Notifier` interface 3. Add config struct to `internal/config/config.go` 4. Register in `cmd/server/main.go` 5. Update `config.yaml` with example config 6. Add tests Example: ```go type MyNotifier struct { BaseNotifier config *MyNotifierConfig } func (m *MyNotifier) Send(ctx context.Context, n *domain.Notification) (*domain.NotificationResult, error) { // Implementation } ``` See [ARCHITECTURE.md](ARCHITECTURE.md) for detailed guide. ## Documentation - [QUICKSTART.md](QUICKSTART.md) - Quick start guide with examples - [ARCHITECTURE.md](ARCHITECTURE.md) - Architecture and design details - [docs/INDEX.md](docs/INDEX.md) - Full documentation index - [api/grpc/notifier.proto](api/grpc/notifier.proto) - gRPC API definition **Guides:** - [docs/AUTH.md](docs/AUTH.md) / [docs/AUTH_QUICK_START.md](docs/AUTH_QUICK_START.md) - Authentication & RBAC - [docs/KEY_MANAGEMENT.md](docs/KEY_MANAGEMENT.md) - API key lifecycle - [docs/EMAIL_GUIDE.md](docs/EMAIL_GUIDE.md) - Email/SMTP setup - [docs/NTFY_GUIDE.md](docs/NTFY_GUIDE.md) - Ntfy integration - [docs/TLS_SECURITY.md](docs/TLS_SECURITY.md) - TLS configuration - [docs/CORS_KUBERNETES_GUIDE.md](docs/CORS_KUBERNETES_GUIDE.md) - CORS in Kubernetes ## Testing ```bash # Run all tests go test ./... # Run with coverage go test -cover ./... # Run specific package go test ./internal/notifier/... ``` **Current Status:** Core implementation complete with unit tests across the config, service, notifier, and auth packages. Run `make test` (race detector enabled) or `make test-coverage` for an HTML report. ## Monitoring & Operations ### Health Checks ```bash curl http://localhost:8080/health ``` Returns: ```json { "status": "healthy", "service": "notifier", "time": "2025-10-16T21:05:27Z" } ``` ### Statistics ```bash curl http://localhost:8080/api/v1/stats ``` Returns: ```json { "total_sent": 1234, "total_failed": 5, "total_pending": 0, "total_queued": 2, "by_type": { "email": 800, "slack": 400, "ntfy": 34 }, "by_status": { "sent": 1234, "failed": 5 } } ``` ### Graceful Shutdown The server handles `SIGINT` and `SIGTERM` gracefully: - Stops accepting new requests - Completes in-flight notifications - Drains the queue - Closes all connections - 30-second timeout ## Roadmap ### Completed βœ… - [x] Core notification system - [x] REST API (fully functional) - [x] gRPC API (fully functional, with reflection) - [x] Local queue with workers - [x] Multiple notifiers (SMTP, Slack, Ntfy, Stdout) - [x] Priority and retry logic - [x] Batch operations - [x] API key authentication with RBAC and runtime key management - [x] CORS support - [x] Notification retention/cleanup - [x] Docker support - [x] Kubernetes manifests with HPA - [x] Health checks and stats - [x] Configuration management - [x] Unit test suite (race detector) ### Planned 🚧 - [ ] Kafka queue adapter - [ ] Database-backed notification persistence (PostgreSQL) - [ ] Notification templates - [ ] Webhook callbacks - [ ] OAuth authentication - [ ] Per-notifier rate limiting - [ ] Prometheus metrics endpoint - [ ] OpenTelemetry tracing - [ ] Circuit breakers for notifiers - [ ] Dead letter queue - [ ] Admin dashboard ## Contributing Contributions are welcome! Please: 1. Fork the repository 2. Create a feature branch (`git checkout -b feature/amazing-feature`) 3. Write tests for your changes 4. Ensure tests pass (`go test ./...`) 5. Commit your changes (`git commit -m 'Add amazing feature'`) 6. Push to the branch (`git push origin feature/amazing-feature`) 7. Open a Pull Request Please follow Go best practices and maintain test coverage. ## Security ### Reporting Vulnerabilities Please report security vulnerabilities privately via GitHub Security Advisories. ### Best Practices - Store credentials in environment variables or secrets managers - Use TLS for production deployments - Enable authentication for ntfy private topics - Rotate tokens regularly - Follow principle of least privilege for K8s RBAC - Keep dependencies updated ## License This project is licensed under the MIT License - see [LICENSE](LICENSE) for details. ## Support & Community - **Issues**: [GitHub Issues](https://github.com/igodwin/notifier/issues) - **Discussions**: [GitHub Discussions](https://github.com/igodwin/notifier/discussions) - **Documentation**: See `docs/` directory - **Examples**: See [QUICKSTART.md](QUICKSTART.md) ## Acknowledgments Built with: - [Gorilla Mux](https://github.com/gorilla/mux) - HTTP routing - [Viper](https://github.com/spf13/viper) - Configuration - [gRPC-Go](https://github.com/grpc/grpc-go) - RPC framework - [Ntfy](https://ntfy.sh) - Push notifications --- **Made with ❀️ for reliable notifications**