npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@jsleekr/hookbox

v0.4.0

Published

<div align="center">

Downloads

65

Readme

📦 hookbox

Capture, inspect, and replay webhooks locally

GitHub Stars License TypeScript Tests

Local webhook development proxy -- no SaaS required.

Capture + Inspect + Replay + Mock

Quick Start | Features


Why This Exists

Debugging webhooks is painful when you depend on cloud tunnels and third-party dashboards. hookbox runs entirely on your machine -- your payloads never leave. Zero config gets you capturing in one command, and every request is persisted to SQLite so you can replay it hours later to reproduce a bug. Mock responses let you test your integration without a live webhook provider even being involved.

Features

| Feature | Detail | |---|---| | HTTP proxy capture | All methods, headers, body, query params saved to SQLite | | Request replay | Forward any saved request to a target URL | | Retry with backoff | Configurable exponential backoff on replay failures | | Mock responses | Route-pattern-matched mock rules with custom status/headers/body; persisted to ~/.hookbox/mocks.json | | Webhook signature verification | GitHub (HMAC-SHA256) and Stripe (timestamped v1) | | Stripe timestamp tolerance | Reject replayed webhooks older than 5 minutes (configurable) | | LRU eviction | --max-requests enforces a hard cap; oldest requests evicted automatically | | Body size limit | Requests exceeding 10 MB are rejected to prevent OOM | | Hop-by-hop header stripping | connection, transfer-encoding, host, etc. removed before replay | | Fetch timeout | 30-second timeout on all outbound forwarding and replay calls | | Request tagging | Attach arbitrary string tags; filter and list by tag | | Search | Full-text search across body and header key/value | | Diff | Side-by-side comparison of two captured requests | | CSV export (RFC 4180) | Fields with commas, quotes, or newlines are correctly escaped | | HAR export | Standard HTTP Archive format for import into browser tools | | CLI formatters | prettyPrintBody, diffRequests, exportToHar extracted to src/core/formatters.ts | | SQLite foreign keys | Tag rows cascade-delete when parent request is deleted | | Middleware hooks | Register before/after hooks to transform or observe requests | | Request statistics | hookbox stats — total, by-method, by-content-type, avg size | | JSDoc documentation | Every public class and method has JSDoc comments |

Quick Start

# Install globally (or use npx hookbox)
npm install -g hookbox

# Start capturing webhooks on port 3000
hookbox start --port 3000

# Forward captured webhooks to your local app
hookbox start --port 3000 --target http://localhost:8080

# List captured requests
hookbox list

# Show full details of a request
hookbox show <request-id>

# Replay a request to your app
hookbox replay <request-id> --target http://localhost:8080

# For local development from source:
#   npm run build && node dist/cli/index.js start --port 3000

Commands

hookbox start

Start the webhook proxy server.

hookbox start [options]
  -p, --port <port>           Port to listen on (default: 3000)
  -t, --target <url>          Forward requests to this URL
  -d, --db <path>             Database file path (default: ./hookbox.db)
  -m, --max-requests <count>  Maximum requests to store (default: 10000)

hookbox list

List captured requests in a table or JSON format.

hookbox list [options]
  -l, --limit <count>   Max requests to show (default: 20)
  -m, --method <method> Filter by HTTP method (GET, POST, etc.)
  -p, --path <path>     Filter by path
  -t, --tag <tag>       Filter by tag
  --json                Output as JSON
  -d, --db <path>       Database file path

hookbox show <id>

Show full details of a captured request including headers, body (pretty-printed), and metadata.

hookbox show <id> [options]
  --json          Output as JSON
  -d, --db <path> Database file path

hookbox replay <id>

Replay a captured request to a target URL.

hookbox replay <id> [options]
  -t, --target <url>  Target URL (required)
  --retry <count>     Retry with exponential backoff
  --json              Output as JSON
  -d, --db <path>     Database file path

hookbox mock

Manage mock response rules.

# Add a mock rule
hookbox mock add --path /webhook --status 200 --body '{"ok":true}'
hookbox mock add --path /webhook/* --method POST --status 201

# List mock rules
hookbox mock list

# Remove a mock rule
hookbox mock remove <rule-id>

Mock rules are persisted to ~/.hookbox/mocks.json and automatically loaded when the server starts.

hookbox stats

Show aggregate request statistics.

hookbox stats [options]
  --json          Output as JSON
  -d, --db <path> Database file path

hookbox search

Search captured requests by body content or headers.

hookbox search [options]
  -b, --body <text>          Search in request body
  -k, --header-key <key>     Search by header key
  -v, --header-value <value> Search by header value
  --json                     Output as JSON
  -d, --db <path>            Database file path

hookbox diff <id1> <id2>

Compare two captured requests side-by-side showing differences in method, path, headers, and body.

hookbox tag <action> <id> [tag]

Manage request tags for organization.

hookbox tag add <id> stripe      # Add tag
hookbox tag remove <id> stripe   # Remove tag
hookbox tag list <id>            # List tags

hookbox clear

Delete all captured requests.

hookbox export

Export captured requests in JSON, CSV, or HAR format.

hookbox export [options]
  -f, --format <format>  Output format: json, csv, har (default: json)
  -d, --db <path>        Database file path

Recipes

GitHub Push Webhook

Capture and verify GitHub push events:

# Start proxy to capture GitHub webhooks
hookbox start --port 3000 --target http://localhost:8080

# Configure your GitHub repo webhook URL to http://<your-host>:3000/webhook/github

# After receiving webhooks, verify signatures programmatically:
import { RequestStore, verifyGitHubSignature } from 'hookbox';

const store = new RequestStore('./hookbox.db');
const requests = store.list({ path: '/webhook/github' });

for (const req of requests) {
  const valid = verifyGitHubSignature(
    req.body,
    req.headers['x-hub-signature-256'],
    process.env.GITHUB_WEBHOOK_SECRET!,
  );
  console.log(`${req.id}: signature ${valid ? 'VALID' : 'INVALID'}`);
}
store.close();

Stripe Payment Webhook

Capture Stripe events with signature verification and mock responses for testing:

# Mock Stripe's expected 200 OK response during development
hookbox mock add --path /webhook/stripe --method POST --status 200 --body '{"received":true}'

# Start proxy — matching requests get the mock response while still being captured
hookbox start --port 3000

# List only Stripe webhooks
hookbox list --path /webhook/stripe

# Replay a charge.succeeded event to your handler
hookbox replay <request-id> --target http://localhost:8080 --retry 3
import { verifyStripeSignature } from 'hookbox';

// Verify in your handler
const valid = verifyStripeSignature(
  rawBody,
  headers['stripe-signature'],
  process.env.STRIPE_WEBHOOK_SECRET!,
);

Slack Events API

Capture and replay Slack events with URL verification handling:

# Mock the URL verification challenge response Slack expects
hookbox mock add --path /webhook/slack --method POST --status 200 \
  --body '{"challenge":"mock-challenge-token"}' \
  --header 'Content-Type:application/json'

# Start capturing
hookbox start --port 3000

# Search for specific Slack event types
hookbox search --body '"type":"event_callback"'

# Tag interesting events for later review
hookbox tag add <request-id> slack-event

# Export all Slack events for analysis
hookbox export --format har > slack-events.har

How It Works

Incoming HTTP Request
        |
        v
  ProxyServer (src/core/proxy.ts)
        |
        |-- Body read (max 10 MB enforced)
        |-- Before hooks run
        |-- MockEngine.match() checked
        |     |
        |     yes --> return mock response
        |     no  --> continue
        |
        |-- RequestStore.save() → SQLite (WAL mode, foreign keys ON)
        |-- After hooks run
        |
        |-- target URL set?
              |
              yes --> fetch(targetUrl, { signal: AbortSignal.timeout(30000) })
              |       strip hop-by-hop headers (Replayer)
              |       return upstream response
              no  --> 200 { captured: true, id }

CLI (src/cli/index.ts)
  |
  ├── list / show / search / diff / tag
  ├── replay  ──→  Replayer (retry + backoff, 30s timeout, hop-by-hop strip)
  ├── mock    ──→  MockEngine (persisted to ~/.hookbox/mocks.json)
  ├── stats   ──→  RequestStore.getStats()
  ├── export  ──→  JSON | CSV (RFC 4180) | HAR
  └── clear

Core modules
  ├── src/core/proxy.ts       — HTTP server + request capture pipeline
  ├── src/core/store.ts       — SQLite persistence via better-sqlite3
  ├── src/core/replayer.ts    — Replay + retry/backoff + header stripping
  ├── src/core/mock.ts        — Mock rule engine
  ├── src/core/signature.ts   — GitHub + Stripe signature verification
  ├── src/core/formatters.ts  — prettyPrintBody, diffRequests, exportToHar
  └── src/utils.ts            — CSV field escaping (RFC 4180)

Security

Body Size Limit

The proxy enforces a 10 MB maximum on incoming request bodies. Requests exceeding this limit are rejected immediately and the connection is destroyed, preventing out-of-memory attacks.

export const MAX_BODY_SIZE = 10 * 1024 * 1024; // 10MB

Header Masking

Sensitive headers such as Authorization are stored as-is in the local SQLite database (local-only, under your control). The replay path strips hop-by-hop headers (connection, keep-alive, transfer-encoding, host, content-length, etc.) before forwarding to protect upstream services from protocol-level issues.

Webhook Signature Verification

import { verifyGitHubSignature, verifyStripeSignature } from 'hookbox';

// GitHub — HMAC-SHA256 over raw body
const isValid = verifyGitHubSignature(
  requestBody,                        // Raw request body string
  headers['x-hub-signature-256'],     // SHA-256 signature header
  'your-webhook-secret'               // Your webhook secret
);

// Stripe — timestamped v1 signature, 5-minute tolerance by default
const isValid = verifyStripeSignature(
  requestBody,                        // Raw request body string
  headers['stripe-signature'],        // t=...,v1=... header
  'whsec_your_endpoint_secret',       // Your endpoint secret
  300,                                // Tolerance in seconds (default: 300)
);

Both functions use timingSafeEqual for constant-time comparison to prevent timing attacks. The Stripe verifier rejects signatures with a timestamp older than the configured tolerance (default 5 minutes / 300 seconds), matching Stripe's own SDK behavior.

Middleware Hooks

Register before/after hooks to transform or observe captured requests:

import { ProxyServer } from 'hookbox';

proxy.addHook({
  name: 'log-requests',
  phase: 'before',
  handler: (request) => {
    console.log(`Incoming: ${request.method} ${request.path}`);
    return request;
  },
});

proxy.addHook({
  name: 'add-metadata',
  phase: 'after',
  handler: (request) => {
    // Post-capture processing
    return request;
  },
});

License

MIT