Skip to content

Repository files navigation

webhookit

A self-hosted, open-source alternative to webhook.site. Captures HTTP requests, inbound emails, and DNS queries in real time and displays them in a live UI.

Two deployment targets — pick one or run both:

Local dev AWS Cloudflare
Start node dev-server.js cdk deploy wrangler deploy
HTTP capture API Gateway + Lambda Workers
Email capture ✓ SMTP on :2525 SES receipt rules Email Workers
DNS capture ✓ UDP on :5353 Route 53 query logs DNS-over-HTTPS
Real-time UI SSE + WebSocket API GW WS + DDB Streams Durable Objects
Cost Free ~$5/mo Free tier

How it works

Every captured event is identified by a token ID — a short random string the browser generates and stores in localStorage. The token appears in all three capture URLs shown in the UI header.

Source Token convention
HTTP First path segment: /{tokenId}/anything
Email Local part of recipient: {tokenId}@hooks.example.com
DNS First label of queried domain: {tokenId}.hooks.example.com

Events arrive in the left panel in real time. Clicking one opens the detail view with full headers, body (JSON-highlighted), and a ready-to-copy curl replay command.


Project layout

webhookit/
├── frontend/dist/index.html   # Single-file SPA — same for all deployments
├── dev-server.js              # Zero-dependency local dev server (Node.js stdlib only)
├── test_webhookit.py          # Test suite — runs against any deployment target
│
├── cloudflare/                # Cloudflare Workers deployment
│   ├── wrangler.toml
│   └── src/
│       ├── worker.ts          # Router — all HTTP routes
│       ├── token-hub.ts       # Durable Object — WebSocket hub per token
│       ├── kv-store.ts        # Event persistence in Workers KV
│       ├── http-capture.ts    # HTTP request capture
│       ├── email-handler.ts   # Email Workers handler
│       ├── dns-handler.ts     # DNS-over-HTTPS proxy + capture
│       ├── broadcast.ts       # Routes events to the TokenHub DO
│       ├── types.ts
│       └── util.ts
│
├── bin/app.ts                 # CDK app entry point
├── lib/
│   ├── webhookit-stack.ts     # Root CDK stack
│   └── constructs/
│       ├── storage.ts         # DynamoDB tables + S3 bucket
│       ├── event-bus.ts       # SNS topic + Lambda processors
│       ├── http-ingress.ts    # API Gateway HTTP catch-all
│       ├── email-ingress.ts   # SES receipt rules
│       ├── dns-ingress.ts     # Route 53 → CloudWatch → Lambda
│       ├── realtime.ts        # WebSocket API + DynamoDB Stream broadcaster
│       └── frontend.ts        # S3 + CloudFront
└── lib/lambdas/
    ├── http-capture.ts
    ├── http-processor.ts
    ├── email-processor.ts
    ├── dns-log-parser.ts
    ├── dns-processor.ts
    ├── ws-connect.ts
    └── ws-broadcast.ts

Local development

No npm install required — dev-server.js uses only the Node.js standard library.

node dev-server.js

Open http://localhost:8080. The server starts three listeners:

Protocol Port Purpose
HTTP + WebSocket 8080 UI, capture endpoint, API
SMTP 2525 Email capture
DNS (UDP) 5353 DNS query capture

To use a different port:

PORT=3000 DNS_PORT=8053 SMTP_PORT=2526 node dev-server.js

Sending test traffic

TOKEN=your-token-id   # copy from the UI

# HTTP
curl -X POST http://localhost:8080/$TOKEN/webhook \
  -H "Content-Type: application/json" \
  -d '{"event": "push", "repo": "acme/api"}'

# Email (Python stdlib)
python3 -c "
import smtplib
s = smtplib.SMTP('localhost', 2525)
s.sendmail('from@example.com', '$TOKEN@localhost', 'Subject: test\n\nhello')
s.quit()
"

# DNS
dig @localhost -p 5353 $TOKEN.hooks.example.com A

Running the test suite

The test suite works against any deployment target — local, AWS, or Cloudflare.

# Against local dev server (all tests)
python3 test_webhookit.py --token my-token

# Skip DNS (port 5353 in use on macOS — system resolver)
python3 test_webhookit.py --token my-token --skip-dns

# Use a free DNS port
DNS_PORT=8053 node dev-server.js
python3 test_webhookit.py --token my-token --dns-port 8053

# Against a deployed instance
python3 test_webhookit.py \
  --http-url https://your-api-endpoint \
  --token my-token \
  --skip-dns \
  --skip-email

All 32 tests, covering:

  • API endpoints (/_/entries, /_/entries/{id}, /_/clear, /config.json)
  • HTTP: POST/GET/PUT/DELETE, headers, body, query strings, nested paths, large bodies, token isolation
  • DNS: A/AAAA/MX/TXT records, NXDOMAIN response, field extraction
  • Email: connect, EHLO, send, multiple messages, raw body, token derivation

Cloudflare deployment

Prerequisites: A Cloudflare account with Workers enabled (free tier is enough).

cd cloudflare

# 1. Authenticate
npm install -g wrangler
wrangler login

# 2. Create the KV namespace and copy the id into wrangler.toml
wrangler kv:namespace create EVENTS

# 3. Deploy
npm install
npm run deploy

Your Worker is live at https://webhookit.<subdomain>.workers.dev.

Email capture (Cloudflare)

  1. Cloudflare dashboard → EmailEmail RoutingRouting Rules
  2. Add rule: match *@yourdomain.comSend to Workerwebhookit
  3. Send email to {tokenId}@yourdomain.com

DNS capture (Cloudflare)

Cloudflare Workers can't bind UDP port 53, so DNS is captured via DNS-over-HTTPS. Point your browser or OS to:

https://webhookit.<subdomain>.workers.dev/dns-query?token={tokenId}

The Worker proxies to 1.1.1.1 so DNS resolution still works normally.

Custom domain (Cloudflare)

Add to cloudflare/wrangler.toml:

routes = [
  { pattern = "hooks.yourdomain.com/*", zone_name = "yourdomain.com" }
]

Then add a CNAME: hooks.yourdomain.comwebhookit.<subdomain>.workers.dev

Local Cloudflare dev

cd cloudflare
npm run dev     # http://localhost:8787, KV + DO simulated by Miniflare

AWS deployment

Prerequisites: AWS CLI configured, Node.js 20+.

npm install -g aws-cdk

# One-time bootstrap per account/region
cdk bootstrap

# Deploy — HTTP and DNS capture, no custom domain
cdk deploy

# Deploy with email and a custom domain
cdk deploy \
  --context domain=hooks.example.com \
  --context sesDomain=hooks.example.com \
  --context retentionDays=14

cdk deploy prints all endpoints as stack outputs.

Email capture (AWS)

  1. Verify your domain in SES → Verified identities
  2. Add an MX record: hooks.example.com MX 10 inbound-smtp.us-east-1.amazonaws.com
  3. Deploy with --context sesDomain=hooks.example.com
  4. SES console → Email receiving — activate the webhookit-inbound rule set

DNS capture (AWS)

Run the command printed in the EnableDnsLoggingCommand stack output:

aws route53 create-query-logging-config \
  --hosted-zone-id YOUR_ZONE_ID \
  --cloud-watch-logs-log-group-arn <ARN from stack output>

AWS cost estimate

Service Monthly cost
Lambda (1M invocations) ~$0.20
DynamoDB on-demand ~$0.25 per million writes
API Gateway HTTP ~$1 per million requests
API Gateway WebSocket ~$0.25 per million messages
CloudFront ~$0.01 per GB
SNS ~$0.50 per million publishes
Total < $5/month at typical dev volume

Teardown

cdk destroy

The webhookit-events DynamoDB table and raw-email S3 bucket use RETAIN policy — delete them manually if you want a clean sweep.


AWS vs Cloudflare comparison

AWS Cloudflare
HTTP capture API Gateway + Lambda Worker fetch handler
Real-time push API GW WebSocket + DDB Streams + Lambda broadcaster Durable Object (WebSocket hibernation)
Email SES receipt rules Email Workers
DNS Route 53 query logs → CloudWatch → Lambda DNS-over-HTTPS proxy
Event storage DynamoDB (TTL) + S3 (raw emails) Workers KV (TTL)
Frontend S3 + CloudFront Workers static assets
IaC AWS CDK (TypeScript) wrangler.toml
Cold start ~100ms (Lambda) ~0ms (V8 isolates)
Deploy time ~5 min ~10 seconds
Free tier Limited Generous

About

DNS, HTTP, Email Receiver -

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages