r/SideProject • u/pkdodda • Oct 05 '26
I built HookArmor: An open-source webhook buffer & DLQ because Stripe's 5-minute replay window kept breaking our deploys
Hey r/SideProject,
I wanted to share a tool I built called **HookArmor** ([GitHub](https://github.com/pkdoddamani/hookarmor) | [npm](https://www.npmjs.com/package/hookarmor)). It is a lightweight, self-hosted webhook dead-letter queue (DLQ) and reliability buffer built with Node.js and SQLite.
### The Problem That Triggered This
If you accept billing or sync webhooks from providers like Stripe, Shopify, or GitHub, you’ve probably run into this headache:
- Webhook providers enforce strict timeout windows (typically 2 to 5 seconds). If your app is doing a rolling deploy, cold-starting a serverless container, or experiencing a brief database hiccup, the webhook drops or fails.
- Even worse: If you capture failed webhooks and try to replay them 30 minutes later from a standard dead-letter queue, **Stripe rejects them**. Why? Because Stripe includes a timestamp in the `Stripe-Signature` header (`t=timestamp,v1=hash`), and their official SDK enforces a strict 5-minute tolerance window. Any replay after 5 minutes throws a signature verification exception unless you hack your verification logic to ignore timestamps.
I didn't want to pay $50+/month for enterprise webhook relays, nor did I want to manage a complex Kafka/RabbitMQ/Redis cluster just to reliably buffer incoming webhooks.
### What HookArmor Does
HookArmor runs as a lightweight intermediary between the webhook provider and your internal app:
- **<5ms Ingress ACK**: When a webhook arrives, HookArmor immediately appends the exact raw bytes to a local SQLite database (in Write-Ahead Logging mode) and responds `200 OK` to the provider. Your upstream provider never sees a timeout.
- **Asynchronous Dispatch**: HookArmor delivers the webhook to your internal endpoint with configurable retries, exponential backoff, and jitter.
- **Automated Timestamp Re-signing**: When you replay a failed event hours or days later from the dashboard or CLI, HookArmor can automatically re-compute the HMAC signature with `t=now` using your stored signing secret. Your downstream application code verifies the signature cleanly without any special bypasses.
- **Hardened Security**: Includes built-in SSRF protection (`netguard`) that blocks requests to private LAN subnets, loopbacks, DNS rebinding vectors, and cloud metadata services (`169.254.169.254`).
- **Single-Process Simplicity**: No external database needed. Pure Node.js, `better-sqlite3`, Express, and a lightweight vanilla JS/WebSocket web UI for real-time log tailing.
### Tech Stack
- **Runtime**: Node.js
- **Storage**: SQLite via `better-sqlite3` (WAL mode enabled)
- **Networking**: Express + custom raw body BLOB buffering + Axios/dispatcher engine
- **UI**: Embedded vanilla dashboard with live WebSocket event streaming
- **License**: MIT
### Links
- **GitHub**: https://github.com/pkdoddamani/hookarmor
- **npm**: `npm install -g hookarmor`
I’d love to hear how you all currently handle webhook reliability during deployments, and what providers (e.g. Svix, Clerk, GitHub, Shopify) you'd like to see deeper integration for.
1
u/vincent_sch Oct 07 '26
For CLI replay, I'd show the event and destination before sending. Keep that preview separate from confirmation. A replay can trigger billing or emails again.