Skip to content
rajputomsinghPublic

About

Reliable webhook delivery infrastructure with automatic retries, idempotency, delivery attempt history, and event replay ➜ built with Go and MongoDB.

Topics

Resources

Contributing

Stars

10 stars

Watchers

0 watching

Forks

Latest commit

 

History

83 Commits

Folders and files

Repository files navigation

Relay logo

Relay

Reliable webhook delivery infrastructure for modern applications.

GitHub stars License npm version Go MongoDB

Release status: The latest published release is v1.1.0 — Operational Visibility (tag). The main branch tracks stable development.

Built with Go and MongoDB, Relay accepts events over HTTP, persists them in MongoDB, and delivers them asynchronously to matching webhook endpoints with retries, delivery tracking, and replay. Ingestion and delivery are separate stages so the ingest path stays fast and predictable while the delivery engine applies backoff, concurrency limits, and recovery independently.

  • Runtime: Go
  • Persistence: MongoDB 7 (Go Driver v2)
  • Delivery: Bounded workers, retries with backoff, per-endpoint state
  • Operations: Delivery history, health checks, Prometheus & Grafana dashboards
  • API: Fully documented OpenAPI (Swagger) specification
  • SDKs: Official Node.js/TypeScript client

Quick Start

Docker (Recommended)

The fastest way to get Relay running locally is with Docker Compose:

git clone https://github.com/rajputomsingh/relay.git
cd relay
make docker-up

See docs/deployment.md for production configuration details.

Manual Setup

git clone https://github.com/rajputomsingh/relay.git
cd relay
go mod download

Configure .env from .env.example (see Configuration), then:

go run ./cmd/relay

Validate changes with the full gate:

go test ./...
go vet ./...
go build ./...
git diff --check

Official SDKs

Node.js / TypeScript

Install the official SDK to integrate Relay into your Node.js or Next.js app:

npm install @rajputomsingh/relay-node
# or pnpm add @rajputomsingh/relay-node

Usage:

import { Relay } from "@rajputomsingh/relay-node";

const relay = new Relay({ apiKey: "your-api-key" });

// Send an event with a guaranteed idempotency key
await relay.sendEvent(
  {
    source: "my-app",
    event_type: "user.created",
    data: { userId: 123 },
  },
  "idempotency-key-123",
);

Architecture

Relay architecture

Each accepted event flows through five stages:

  1. Ingestion. POST /v1/events authenticates the caller, validates the JSON body, and enforces Idempotency-Key.
  2. Persistence. Events are written to MongoDB before any delivery begins. Each matching endpoint gets an independent delivery record.
  3. Matching and scheduling. Enabled endpoints are filtered by source and event_type. Matching deliveries enter the pending pool.
  4. Bounded delivery workers. Workers claim deliveries with lease tokens, execute the outbound HTTP request, and record the attempt.
  5. Outcome and recovery. Attempts resolve to success, retry with backoff, or terminal failure. Expired leases are reclaimed; terminal failures are eligible for replay.

Delivery Semantics

Relay provides at-least-once delivery. It does not guarantee exactly-once processing — a successful HTTP response plus a persisted attempt record cannot jointly guarantee a single downstream effect. Any downstream requiring single-effect semantics must key idempotency on the event id.

  • Idempotency keys. The Idempotency-Key header rejects duplicate submissions of the same logical event. It prevents duplicate ingestion; it does not prevent duplicate delivery attempts.
  • Retry classification. Relay retries 5xx, network errors, connection refusals, and timeouts. 4xx other than 408 and 429 are terminal.
  • Worker crash. If a worker dies mid-attempt, its lease expires and the delivery is reclaimed. The retry after reclamation is logged as a new attempt. Downstreams may see a delivery more than once.
  • Lease fencing. A stale worker attempting to record an outcome after its lease is reclaimed is rejected, so stale results cannot overwrite current state.
  • Downstream idempotency. Because duplicate delivery is possible, downstream handlers must be idempotent.

Configuration

Relay is configured entirely through environment variables, read at startup; validation failures abort the process.

Variable Required Default Notes
APP_ENV No development development / staging / production
PORT No 8080 Valid TCP port
MONGODB_URI Yes — Must parse as a MongoDB URI
MONGODB_DATABASE No relay Non-empty
RELAY_API_KEY Yes — Validated for minimum length (32 chars)
RELAY_WEBHOOK_URL No — First-run bootstrap only
RELAY_WEBHOOK_SIGNING_SECRET No — If set, at least 32 characters

.env and files containing credentials must never be committed. In production, source these from a secrets manager.

API Contract & Documentation

All /v1/* routes require Authorization: Bearer <RELAY_API_KEY>.

Relay includes an interactive Swagger UI. When running the server locally, navigate to: 👉 http://localhost:8080/swagger/index.html

Method Endpoint Auth Purpose
GET /healthz No Liveness (process running)
GET /readyz No Readiness (MongoDB reachable)
GET /metrics Yes Prometheus metrics endpoint
POST /v1/events Yes Submit an event
POST /v1/events/{id}/replay Yes Replay an eligible failed event
POST /v1/endpoints Yes Register an endpoint
GET /v1/endpoints Yes List endpoints
GET /v1/endpoints/{id} Yes Retrieve one endpoint
PATCH /v1/endpoints/{id} Yes Update endpoint configuration
DELETE /v1/endpoints/{id} Yes Disable an endpoint (soft delete)
GET /v1/events/{id}/deliveries Yes Per-endpoint delivery status
GET /v1/events/{id}/attempts Yes Attempt history

Webhook Signing

When an endpoint has a signing secret, Relay signs outbound requests with HMAC-SHA256 over the raw body and sends the hex digest in X-Relay-Signature.

Verify in Go

func verifyRelaySignature(rawBody []byte, header string, secret []byte) error {
    if header == "" {
        return errors.New("missing X-Relay-Signature")
    }
    mac := hmac.New(sha256.New, secret)
    mac.Write(rawBody)
    expected := hex.EncodeToString(mac.Sum(nil))
    if !hmac.Equal([]byte(header), []byte(expected)) {
        return errors.New("signature mismatch")
    }
    return nil
}

Verify in Node.js

function verifyRelaySignature(rawBody, header, secret) {
  if (!header) throw new Error("missing X-Relay-Signature");
  const expected = crypto
    .createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");
  const a = Buffer.from(header, "utf8");
  const b = Buffer.from(expected, "utf8");
  if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
    throw new Error("signature mismatch");
  }
}

Operations & Observability

  • Health. /healthz returns 200 while the process runs. /readyz returns 200 only when MongoDB responds to ping.
  • Metrics. /metrics exposes Prometheus metrics for outbound delivery duration and delivery attempt outcomes.
  • Dashboards. A pre-provisioned Grafana dashboard and Prometheus config are included in docker-compose.monitoring.yml.
  • Shutdown. On SIGINT/SIGTERM, Relay stops accepting new requests, drains in-flight deliveries up to the shutdown deadline, then exits.
  • Retry policy. Backoff is retryDelay(n) = 2s × 2^(n-1) + jitter(0–25%), capped per delay and bounded by max_attempts.

Benchmarks

Relay includes a benchmark harness for sustained load, concurrency, failure modes, outage recovery, and queue growth. Full methodology and raw results: docs/benchmarks.md.

Measurement Result
Workload 5,000 deliveries
Highest tested concurrency 32
Highest reported throughput 43.5 deliveries/s
Highest-concurrency drain 115 s

Known Limitations

  • Metrics currently query MongoDB on scrape. For very high-throughput setups, an in-memory metrics aggregator is recommended.
  • No tenant isolation or per-tenant authorization (single-tenant instance per deployment).
  • No web UI for event inspection, monitoring, or replay (API-only).

Project Status and Roadmap

Completed (v1.0.0 & v1.1.0)

  • Production-ready Dockerfile & Compose setup.
  • Prometheus metrics instrumentation & Grafana dashboards.
  • OpenAPI (Swagger) interactive documentation.
  • Hardened CI/CD pipeline (CircleCI).
  • Official Node.js/TypeScript SDK.

Planned (v1.2.0+)

  • Tenant isolation and multi-tenant authorization.
  • Web dashboard for event inspection and replay.
  • Python and Go SDKs.

Development and Testing

Unit tests (no infrastructure):

go test ./internal/config ./internal/security ./internal/handlers -count=1

Integration tests (MongoDB required):

export MONGODB_TEST_URI="mongodb://localhost:27017"
go test ./internal/delivery -count=1

Full validation gate (before proposing changes):

go test ./...
go vet ./...
go build ./...
git diff --check

License

Licensed under the Apache License 2.0. See LICENSE for details.

About

Reliable webhook delivery infrastructure with automatic retries, idempotency, delivery attempt history, and event replay ➜ built with Go and MongoDB.

Topics

Resources

Contributing

Stars

10 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages