Back to portfolio

Webhook Fan-out Relay for Local Development

Open-source gRPC relay that broadcasts webhooks from a single public endpoint to every developer on a team simultaneously.

Live

Webhook fan-out relay for local development

Open-source gRPC relay that broadcasts webhooks from a single public endpoint to every developer on a team simultaneously.

hookie.sh

Teams consuming webhooks from PagerDuty, Slack, and GitHub during local development hit the same wall: each developer needs to receive the same events on their machine, but the webhook source only accepts one destination URL. ngrok solves the single-developer case. It does not solve the team case without duplicating webhook configurations per person, per platform.

I built Hookie, a webhook fan-out relay that inverts the ngrok model. Instead of tunnelling a local server to the internet, a single stable public endpoint receives webhooks centrally, and a gRPC relay broadcasts each event to every connected developer CLI.

Problem

Webhook-driven workflows (incident response, deploy hooks, bot integrations) require the full event payload locally to develop and debug handlers. The standard approach is ngrok: expose a local port behind a named public URL, configure the webhook source to point there.

That works for one developer. For a team of five, it means five ngrok tunnels, five webhook URLs registered in PagerDuty or Slack, and five sets of credentials to rotate when someone leaves. The configuration overhead scaled linearly with team size and was operationally unsustainable.

The constraint was clear: one configured endpoint per platform, unlimited local consumers.

Solution

Hookie has three pieces: a public ingest API, a gRPC relay service, and a developer CLI.

The ingest API receives webhooks at a single stable URL configured once in each source platform. It stores the raw request (headers, body, method) and publishes it to the relay layer. The gRPC relay fans the event out to every CLI currently connected. Each developer runs hookie listen and gets the full webhook forwarded to a local port, as if the source had called them directly.

The architectural inversion matters. ngrok pushes local services outward; Hookie pulls external events inward. One webhook registration covers the whole team. Developers connect and disconnect freely without touching the source platform's configuration.

Hard Parts

Preserving HMAC signatures through the relay

Webhook providers sign payloads with HMAC so the receiver can verify authenticity. If the relay modifies anything (re-encodes the body, normalizes headers, changes content length), the signature breaks and the developer's local handler rejects the event as tampered.

I designed around this from the start. The ingest layer captures the raw byte stream and the full header set, then the relay forwards both unchanged. No JSON parsing, no header normalization, no re-serialization. The CLI reconstructs the original HTTP request on the local side so signature verification passes as if the source had called the developer directly.

Backpressure between ingest and local CLIs

A high-throughput webhook source (GitHub org-level hooks during a busy deploy window, for example) can produce events faster than a developer's local machine processes them. If the relay does not handle this, events either queue unboundedly in memory or get dropped.

This is an active research area. The relay uses Redis Streams as the underlying transport, which gives durable ordering and consumer group semantics. The open question is the right strategy when a consumer falls behind: buffer with a cap and drop oldest, apply per-consumer flow control, or let the consumer signal its own capacity. I'm stress-testing each approach before committing to a default.

Outcome

The core fan-out model was dogfooded at a major telco, where multiple developers on the incident response team needed real PagerDuty and Slack events during local development. The relay validated under real team conditions: one endpoint configured in each source, every developer receiving events without coordination overhead.

The project removed the per-developer webhook registration tax that made ngrok unworkable at team scale. Configuration went from O(developers x platforms) to O(platforms). The open-source product was built from these internal learnings and is approaching public launch.

What I would do differently

note

I underestimated the depth of Redis Streams before building around them. Consumer groups, acknowledgment semantics, and the trimming model each carry edge cases that surfaced late. I would invest more upfront in understanding the data structure's failure modes before committing the relay's durability story to it.

Backpressure handling should have been a first-class design concern, not something discovered through stress testing. Researching flow-control strategies (leaky bucket, credit-based, explicit consumer signaling) earlier in the design phase would have saved rework and narrowed the option space before code existed.

What I would do the same

The architectural inversion (pull events centrally, broadcast down) instead of extending the ngrok tunnel model. It keeps webhook source configuration constant regardless of team size.

Preserving raw bytes and headers through the relay so HMAC signatures remain valid. Treating signature integrity as a design constraint from day one, not a retrofit.

Building the CLI as a lightweight gRPC consumer that reconstructs the original HTTP request locally. It keeps the developer's environment simple: run one command, get webhooks on a port.

What I would do next

Resolve the backpressure challenge through deeper stress testing and a committed flow-control strategy for the Redis Streams consumer layer.

Ship a Replay feature so developers can re-fire captured webhook events on demand, useful for reproducing edge cases without waiting for the source to generate another event.

Add observability tooling for inspecting webhook traffic over time: event history, payload diffs, delivery latency per consumer.

Build AI-agent-friendly CLI capabilities so an agent can listen to and replay webhooks, then help developers scaffold the appropriate handlers from real payloads.


  • Developer tools
  • gRPC
  • Open source
  • Webhooks
Made with ❤️ in 🇨🇦 · Copyright © 2026 Valentin Prugnaud
Foxy seeing you here!
Wondering if I'd fit your role?
Logo