OpenCrafts' notification relay infrastructure.
Gossip Monger is a standalone service that relays notifications on behalf of every io.opencrafts.* service — not just one product. It sits between your service and the providers that actually deliver messages: OneSignal for push, Resend for email. Your service publishes an event to RabbitMQ; Gossip Monger validates it, records it, and dispatches it to the right provider.
Gossip Monger is consumer-only — it has no public HTTP or gRPC API beyond a /ping health check. Every interaction happens over RabbitMQ:
- Your service publishes a message to a topic exchange, using a routing key for the channel you want (email or push).
- Gossip Monger consumes it, validates the payload, persists a record, and dispatches it to the provider.
- The outcome — including provider errors — is persisted for auditing and debugging.
No pre-registration is required beyond RabbitMQ publish access: any message from a service namespaced io.opencrafts.* is accepted and registered automatically on first use.
| Exchange | Type | Routing key | Purpose |
|---|---|---|---|
gossip.topic.exchange |
topic | gossip.emails.send |
Send an email via Resend |
gossip.topic.exchange |
topic | gossip.push.send |
Send a push notification via OneSignal |
verisafe.exchange |
fanout | verisafe.user.* |
Sync Gossip Monger's local user directory from Verisafe |
Every message shares the same envelope shape — a channel-specific payload plus shared metadata:
{
"email": { "...": "..." },
"metadata": {
"event_type": "email.send",
"source_service_id": "io.opencrafts.<your-service>",
"request_id": "a UUID, unique per event",
"timestamp": "2024-11-01T10:00:00Z"
}
}(Push notifications use the same metadata shape with a notification payload instead of email.)
Field references, valid/invalid payload examples, and idempotency rules live under docs/:
- Sending email
- Sending push notifications
- Architecture decisions — why things work the way they do, and what's deliberately deferred
Copy example.env to .env and fill in your own values.
| Variable | Purpose |
|---|---|
GOSSIP_MONGER_PORT, GOSSIP_MONGER_ADDRESS |
Health-check server binding |
DB_* |
Postgres connection and pool sizing |
RABBITMQ_* |
Broker connection |
ONESIGNAL_APP_ID, ONESIGNAL_REST_API_KEY |
Push provider credentials |
RESEND_API_KEY |
Email provider credentials |
RESEND_ALLOWED_SENDER_DOMAINS |
Comma-separated domains a from_address is allowed to end with |
GOOSE_* |
Migration runner settings |
docker-compose.yml runs the app container only — it
does not bundle Postgres or RabbitMQ, both of which are expected to already
be reachable on the network (Postgres via DB_* env vars, RabbitMQ via
RABBITMQ_*). Set every variable in the table above through Dokploy's
environment variable UI; GOOSE_* vars are only used by the standalone
goose CLI for manual migrations and are not needed here — migrations run
automatically on startup regardless.
Requires Go 1.25+, PostgreSQL, and RabbitMQ running locally.
cp example.env .env # then fill in your local values
go run ./cmd/api # migrations run automatically on startupRun the tests:
go test ./...The repository layer (internal/repository/) is generated by sqlc from database/queries/*.sql. After changing a query, regenerate it:
sqlc generateGo · PostgreSQL · RabbitMQ · sqlc · goose · OneSignal · Resend