An open-source, Go-based notification platform called NotifyHub for event-driven products.
This project is building toward a "Kubernetes for notifications" model: business systems publish notification intent, while the platform owns delivery orchestration, policy evaluation, retries, provider failover, callbacks, auditability, and observability.
clideo_editor_0ee5c18547ab4caab17b3099c1ae72d5.mp4
The current platform already supports:
- canonical notification intake through a REST API
- asynchronous worker processing backed by Kafka
- PostgreSQL-backed request, attempt, policy, and audit state
- templates, routing policies, preference policies, and delivery policies
- provider bindings with binding-set aware routing
- pluggable
email,sms, andwebhookconnectors - retry classification, dead-lettering, replay, and provider failover
- provider health tracking and
gobreaker-backed circuit breakers - provider callback normalization and outbound lifecycle webhooks
- Prometheus/Grafana metrics, Kafka UI, and Adminer
- local Docker observability stack
- unit tests, live integration tests, and a basic CI workflow
For the build log and production-readiness checklist, see docs/project-status.md.
Your application decides:
- why a notification should happen
- who should receive it
- what business event or template key it maps to
The control plane decides:
- which channels should be used
- which provider binding set should handle them
- how templates are rendered
- whether preferences suppress delivery
- how retries, failover, DLQ, replay, and callbacks are handled
apiControl-plane API for requests, policies, templates, provider bindings, dead letters, and status queries.workerAsync reconciliation engine that evaluates policy, renders content, dispatches connectors, schedules retries, and manages circuit-breaker state.callback-gatewayNormalizes provider callbacks into delivery-attempt and request-status updates.connector-email,connector-sms,connector-webhook,connector-push,connector-whatsappReference out-of-process provider adapters.migrateVersioned SQL migration runner used by Docker and local commands.
apps/
api/
callback-gateway/
migrate/
worker/
connectors/
email/
sms/
webhook/
libs/
contracts/
core/
messaging/
observability/
storage/
deployments/docker/
deployments/helm/
docs/
api/
architecture/
project-status.md
migrations/
tests/
integration/
load/
Start the local stack:
make upRun migrations directly:
make migrateInspect the stack:
- API:
http://localhost:8080/healthz - Worker:
http://localhost:8081/healthz - Callback gateway:
http://localhost:8082/healthz - Email connector:
http://localhost:8091/healthz - SMS connector:
http://localhost:8092/healthz - Webhook connector:
http://localhost:8093/healthz - Kafka UI:
http://localhost:8085 - Adminer:
http://localhost:8086 - Prometheus:
http://localhost:9090 - Grafana:
http://localhost:3000
Grafana default login:
- user:
admin - password:
admin
Adminer default login:
- system:
PostgreSQL - server:
postgres - username:
postgres - password:
postgres - database:
notification_control_plane
{
"idempotency_key": "order-123-delayed",
"event_name": "order.delayed",
"template_key": "order-delayed-v1",
"channels": ["email"],
"recipient": {
"user_id": "user-123",
"email": "alice@example.com"
},
"variables": {
"order_id": "ORD-123",
"reason": "carrier_delay"
},
"priority": "high"
}Submit it:
curl -s -X POST http://localhost:8080/v1/notification-requests \
-H 'Content-Type: application/json' \
-d @request.jsonRun the normal Go suite:
make testCheck formatting:
make fmt-checkRun the live integration suite against the local stack:
make integration-testRun the load test:
make load-test- Documentation index: docs/README.md
- OpenAPI-style contract: docs/api/openapi.yaml
- Architecture notes: docs/architecture/v1.md
- Current project status: docs/project-status.md
- Operator guide: docs/operator-guide.md
- Production deployment guide: docs/guides/deploy-to-production.md
- Observability deployment guide: docs/guides/deploy-observability-stack.md
- Connector extension guide: docs/connector-sdk.md
- Roadmap: docs/roadmap.md
- Transactional email: examples/transactional-order-delay/README.md
- OTP SMS: examples/otp-sms/README.md
- Operational webhook alert: examples/ops-webhook-alert/README.md
Key endpoints currently exposed by api:
POST /v1/notification-requestsGET /v1/notification-requests/{requestID}GET /v1/provider-definitionsGET /v1/provider-accountsGET /v1/provider-accounts/{providerAccountID}GET /v1/provider-accounts/{providerAccountID}/statusPOST /v1/provider-accountsPATCH /v1/provider-accounts/{providerAccountID}POST /v1/provider-accounts/{providerAccountID}/disableGET /v1/provider-bindingsGET /v1/provider-bindings/{channel}POST /v1/provider-bindingsGET /v1/provider-binding-healthGET /v1/provider-binding-health/{bindingID}POST /v1/provider-binding-health/{bindingID}/resetGET /v1/callback-routesGET /v1/callback-routes/{providerKey}POST /v1/callback-routesGET /v1/routing-policiesGET /v1/preference-policiesGET /v1/templatesGET /v1/delivery-policiesGET /v1/webhook-subscriptionsGET /v1/dead-lettersPOST /v1/dead-letters/{deadLetterID}/replay
Provider callbacks are received through callback-gateway:
POST /v1/providers/{provider}/{providerAccountId}/callbacks
- Add new schema changes as numbered files in migrations.
- Keep connector-specific behavior behind connector processes rather than embedding provider logic into
apiorworker. - Prefer extending the unit and integration suites when changing worker decision logic, retries, or provider behavior.
- Review architecture decisions in docs/adr before making large design changes.
This is already a strong working platform foundation, but it is not yet finished as a production-ready OSS control plane. Remaining work includes broader operator docs, stronger multi-channel semantics, auth/RBAC, secret-manager integration, more CI/build automation, and deployment packaging beyond local Docker.