@prash0029/webhook-interceptor
v0.1.0
Published
Self-hosted webhook inspector and forwarding proxy: a hop between sender and target that captures every request/response to Postgres and optionally forwards to a configured URL.
Maintainers
Readme
@prash0029/webhook-interceptor
Self-hosted webhook inspector and forwarding proxy. A hop you place between a webhook sender (A) and its real target (B): it captures every request and the response, and can optionally forward the request to B and relay B's response back to A.
- Create a unique capture URL per service.
- Point sender A at that URL.
- Toggle forward per service:
- on -> the full request is proxied to the target B; B's response (status, headers, body) is relayed back to A. If B is unreachable or times out, A gets
502. - off -> A gets a default
200.
- on -> the full request is proxied to the target B; B's response (status, headers, body) is relayed back to A. If B is unreachable or times out, A gets
- Every request and response is stored in Postgres for inspection.
- Minimal dashboard to create services, toggle forwarding, and browse captures.
Distributed as an npm package with a CLI; run it on your server (e.g. under supervisord) against a Postgres database.
Data model
services- one row per capture endpoint (public_id, optionalname,forward_enabled,forward_url).requests- every received request + the response returned, foreign-keyed to its service (service_id), indexed by(service_id, received_at desc).
Install
npm install @prash0029/webhook-interceptorConfigure
Set environment variables (see .env.example):
| Var | Required | Default | Meaning |
| --- | --- | --- | --- |
| DATABASE_URL | yes | - | Postgres connection string |
| PORT | no | 3000 | listen port |
| PUBLIC_BASE_URL | no | http://localhost:<port> | external URL shown for capture endpoints |
| FORWARD_TIMEOUT_MS | no | 10000 | forward target timeout before 502 |
| BODY_LIMIT_BYTES | no | 5242880 | max captured body size |
| ADMIN_TOKEN | no | (unset) | when set, dashboard + read API require this token |
Run
# 1. create the schema (idempotent)
npx webhook-interceptor migrate
# 2. start the server
npx webhook-interceptor startDashboard: PUBLIC_BASE_URL/ . Capture endpoint for a service: PUBLIC_BASE_URL/hook/<public_id>.
Under supervisord
[program:webhook-interceptor]
command=npx webhook-interceptor start
directory=/srv/webhook-interceptor
environment=DATABASE_URL="postgres://...",PUBLIC_BASE_URL="https://hooks.example.com",ADMIN_TOKEN="..."
autostart=true
autorestart=trueMap your external URL (hooks.example.com) to this process via your reverse proxy.
Security
- Capture URLs (
/hook/...) are public by design - external senders must reach them with no auth. - The dashboard exposes every captured payload, which routinely includes signing signatures, API keys, tokens, and personal data. Set
ADMIN_TOKENto require a token on the dashboard and read API. Without it, anyone who can reach the server reads all captured secrets. - Bodies are stored in plaintext. Consider retention limits and redaction before capturing production traffic. Do not capture regulated personal data without the appropriate controls.
Programmatic use
import { createPool, migrate, createServer, loadConfig } from "@prash0029/webhook-interceptor";
const config = loadConfig();
const pool = createPool(config.databaseUrl);
await migrate(pool);
createServer(pool, config).listen(config.port);Develop
npm install
npm run typecheck
npm test
npm run buildLimitations
- Bodies captured/stored as UTF-8 text; binary payloads are stored lossily.
content-encodingis stripped when relaying (fetch decodes the body), so the relayed body is decompressed.- Single process, single Postgres; no built-in retention/cleanup job yet.
- Redirects from the forward target are not followed (
redirect: "manual").
License
MIT
