Skip to content
oxSwightPublic

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

HookForge 🔨

A developer-first webhook gateway: verify → transform → filter → reliably fan-out.

Every SaaS sends webhooks. Almost none make them pleasant to receive: payload shapes differ per provider, signature schemes vary, there are no retries, and a single downstream hiccup drops events silently. HookForge sits in front of your service and fixes all of that with a small, transparent, fully-tested core.

 Provider ──▶  HookForge  ──▶  your service(s)
              │  1. verify HMAC signature (replay-safe)
              │  2. filter (declarative predicates)
              │  3. transform (declarative, no code-exec)
              │  4. fan-out with retries + idempotency

Status: Production-hardening (Phase 7 + review-driven bugfix pass). 208 passing tests, ~92% statement coverage, lint clean. Pure-logic core with zero network dependencies in the test suite. PostgreSQL persistence, a durable delivery queue (202 + background retries), API-key auth, provider signature presets (Stripe / GitHub / Shopify), CORS, and a React dashboard. Hardened against prototype pollution and filter ReDoS, with release-on-failure idempotency, guarded async routes, bounded memory, paginated delivery logs, and graceful shutdown.


Why HookForge

  • Reliable delivery — exponential backoff + full jitter, capped, with smart retry classification (5xx/429/408 retry; other 4xx are permanent).
  • Idempotent fan-out — the same logical event is never delivered twice to the same destination, even across retries.
  • Safe transforms — a declarative, whitelisted spec (map/rename/pick/omit/ template/…). There is no eval. User-supplied rules can't execute code.
  • Signature verification — Stripe-style HMAC-SHA256 over ${timestamp}.${body}, timing-safe comparison, and a replay window.
  • Portable & self-hostable — one runtime dependency (Express). Core is plain, side-effect-free JavaScript.

Quick start

npm install
npm test          # 131 tests
npm start         # boots the gateway on :3000

Create an endpoint

curl -X POST localhost:3000/endpoints -H 'content-type: application/json' -d '{
  "name": "stripe-prod",
  "secret": "whsec_...",
  "filter": { "field": "type", "op": "eq", "value": "charge.succeeded" },
  "transform": [
    { "op": "rename", "from": "data.object.amount", "to": "total" },
    { "op": "set",    "to": "source", "value": "stripe" }
  ],
  "destinations": ["https://api.myapp.com/webhooks/payments"]
}'

Point your provider at the ingest URL

POST /ingest/:endpointId
  x-hookforge-timestamp: <unix seconds>
  x-hookforge-signature: <hex HMAC-SHA256 of `${timestamp}.${rawBody}`>

HookForge verifies the signature, applies the filter and transform, then delivers to every destination with retries — recording each attempt.

CLI playground (free lead-magnet tool)

Zero network. Sign, verify, transform, and test filters locally:

node bin/hookforge.js sign      --secret sec --body '{"id":"evt_1","amount":500}'
node bin/hookforge.js transform --spec '[{"op":"rename","from":"amount","to":"total"}]' --body '{"amount":500}'
node bin/hookforge.js filter    --filter '{"field":"amount","op":"gt","value":100}' --body '{"amount":500}'
node bin/hookforge.js verify    --secret sec --body @event.json --timestamp 1700000000 --signature <sig>

Transform spec reference

Ops run as a pipeline over a single running document (deep-cloned from the payload):

op shape effect
pick { op, fields: ["a","b.c"] } keep only these paths
omit { op, fields: ["secret"] } drop these paths
rename { op, from: "a.b", to: "x" } move a value
copy { op, from: "a", to: "b" } duplicate a value
set { op, to: "meta.src", value: "stripe" } write a static value
default { op, to: "currency", value: "usd" } write only if missing
template { op, to: "msg", template: "Order {{id}}" } interpolate {{path}} tokens

Filter reference

Leaf: { field, op, value } with ops eq, neq, gt, gte, lt, lte, in, nin, exists, contains, startsWith, endsWith, matches. Compose with { and: [...] }, { or: [...] }, { not: <node> }. A null filter matches everything.

API

Method Path Purpose
GET /health liveness + stats
POST /endpoints create endpoint
GET /endpoints list (secrets redacted)
GET /endpoints/:id fetch one
PATCH /endpoints/:id update
DELETE /endpoints/:id remove
GET /endpoints/:id/deliveries delivery log
POST /ingest/:id receive a webhook

Production features (Phase 7)

  • Persistence — PgStore (PostgreSQL) behind the same interface as the in-memory store; the server awaits every store call so they are interchangeable. Set DATABASE_URL to switch on Postgres mode.
  • Durable delivery — /ingest verifies/filters/transforms synchronously, then enqueues delivery jobs and returns 202 Accepted. A worker delivers with durable retry/backoff and dead-letters exhausted jobs. InMemoryQueue (dev/tests) and PgBossQueue (prod) share one enqueue() surface.
  • API-key auth — management routes require x-api-key (or Authorization: Bearer); /health and /ingest/* are exempt (webhooks self-authenticate). Configure with HOOKFORGE_API_KEYS=key1,key2.
  • Provider presets — per-endpoint provider: hookforge (default), stripe, github, shopify — each with its correct signature scheme + headers.
  • CORS — configurable via HOOKFORGE_CORS_ORIGINS (default *).
  • Dashboard — frontend/ Vite + React SPA: manage endpoints and browse the delivery log. cd frontend && npm install && npm run build.

Architecture

src/core/      pure logic (no network): signature, providers, transform, filter,
               retry, idempotency, delivery, pipeline, httpTransport
src/store/     pluggable storage: MemoryStore + PgStore (shared validators)
src/queue/     durable queue: InMemoryQueue + Worker, PgBossQueue (prod)
src/api/       Express glue (thin): server, auth, cors
bin/           CLI playground
frontend/      Vite + React dashboard
test/          179 Jest + Supertest tests

Design notes and every autonomous decision are recorded in decision_log.md; the studio pipeline that produced this lives in docs/ and project_board.md.

License

MIT © Igor Zommer

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages