Problem Statement
Developers need to receive third-party webhooks in local development without exposing a local HTTP server to the public internet. Reverse tunnels make the developer machine part of the availability path: webhooks can be lost while the machine sleeps, disconnects, restarts, or changes networks. Teams also lack a durable, searchable record of incoming webhook requests and their local delivery attempts.
Solution
Build Hooky, a multi-tenant SaaS that generates public webhook URLs, durably records incoming requests in Neon Postgres, and lets an authenticated hooky CLI claim and forward stored events to a localhost destination using outbound HTTPS only.
A webhook sender receives success only after the event and initial delivery record have committed. The CLI leases pending deliveries, reconstructs the original HTTP request locally, and acknowledges or rejects each attempt. Expired leases become eligible for redelivery. The web dashboard provides hook management, event inspection, delivery history, replay, retention controls, and secret rotation.
User Stories
- As a developer, I want to create a hook from the dashboard, so that I immediately receive a public webhook URL.
- As a developer, I want to create a hook from the CLI, so that I can stay in my terminal.
- As a developer, I want to select an existing hook from the CLI, so that I can resume a previous integration.
- As a developer, I want to supply a localhost destination when listening, so that the cloud service never needs access to my private network.
- As a developer, I want incoming webhooks stored while my machine is offline, so that I do not lose development events.
- As a developer, I want stored events delivered when the CLI reconnects, so that sleep and network changes are harmless.
- As a developer, I want the original HTTP method, path suffix, query, safe headers, content type, and raw body preserved, so that my local handler sees an accurate request.
- As a developer, I want binary request bodies preserved byte-for-byte, so that Hooky is not limited to JSON.
- As a developer, I want to see events arrive in the dashboard, so that I can inspect an integration without running the CLI.
- As a developer, I want to inspect request and delivery metadata, so that I can debug local handler failures.
- As a developer, I want to replay an event, so that I can test a fix without asking the provider to resend it.
- As a developer, I want delivery attempts and local response details recorded, so that I can understand retries.
- As a developer, I want failed deliveries retried with backoff, so that temporary local failures recover.
- As a developer, I want slow deliveries to extend their leases, so that another listener does not claim active work.
- As a developer, I want expired leases recovered automatically, so that CLI crashes do not strand events.
- As a developer, I want graceful CLI shutdown, so that outstanding work is handled predictably.
- As a developer, I want browser-based CLI login, so that I do not paste long-lived secrets into shell history.
- As a developer, I want CLI credentials stored in the OS credential store, so that they are not saved as plaintext.
- As a developer, I want hook secrets to be rotatable, so that leaked URLs can be replaced.
- As a developer, I want configurable data retention, so that captured payloads are not stored indefinitely.
- As an account owner, I want tenant isolation, so that no other account can access my hooks or events.
- As an account owner, I want usage quotas, so that an accidental webhook loop cannot create an unlimited bill.
- As an account owner, I want sensitive headers redacted, so that authorization values are not unnecessarily exposed.
- As an operator, I want ingestion, lease, delivery, and failure metrics, so that I can detect service degradation.
- As an operator, I want an audit trail for secret and retention changes, so that security-sensitive operations are attributable.
- As an operator, I want database failures to result in non-success responses, so that providers can retry instead of events being silently lost.
- As an operator, I want endpoint-level rate limiting, so that abuse cannot overwhelm the application.
- As an operator, I want cleanup jobs to enforce retention, so that storage growth is controlled.
- As a maintainer, I want the relay state machine isolated behind a small interface, so that it can be tested independently of HTTP and UI code.
- As a maintainer, I want CI to verify formatting, linting, unit tests, E2E tests, and production builds, so that every change has a consistent release gate.
Implementation Decisions
Domain vocabulary
- A hook is a public webhook ingress owned by an account.
- An event is an immutable captured HTTP request.
- A destination is a localhost URL supplied to the CLI.
- A listener is a running CLI process.
- A delivery is the logical work of forwarding an event.
- A delivery attempt is one local HTTP request.
- A lease is a temporary exclusive claim on a delivery.
Platform
- Use a Bun workspace monorepo with separate web, CLI, database, relay-core, shared, and test-support modules.
- Use strict TypeScript and kebab-case filenames.
- Use Next.js App Router on Vercel for the dashboard, account APIs, CLI APIs, and public catch-all ingress.
- Use Neon Postgres with the pooled connection endpoint.
- Use Drizzle for schema definition, migrations, and ordinary queries.
- Use explicit SQL transactions for leasing operations requiring
FOR UPDATE SKIP LOCKED.
- Use
motion if UI animation is introduced; do not add framer-motion.
- Keep function parameter types inline rather than defining isolated parameter interfaces.
Durability
- Neon is the authoritative event store and delivery queue for the MVP.
- Create the immutable event and pending delivery in a single transaction.
- Return a webhook success response only after that transaction commits.
- Return a retryable failure when durable persistence is unavailable.
- Give every claim a random lease token, claimant identity, and expiration.
- Make ACK, NACK, replay, and lease-extension operations idempotent.
- Treat delivery as at least once. Exactly-once local execution is not promised.
- Keep Vercel Queues and WebSockets out of the correctness path. They may later provide wake-up or scaling optimizations.
Ingress
- Generate high-entropy hook secrets and store only hashes.
- Accept the HTTP methods supported by Next.js Route Handlers through a catch-all route.
- Preserve method, trailing path, query parameters, headers, content type, receipt timestamp, raw bytes, and body hash.
- Cap supported payloads below Vercel's 4.5 MB platform limit and document the limit.
- Use the hook secret as the initial generic authentication mechanism.
- Add provider-specific signature verification only after the generic relay is stable.
CLI protocol
- Provide browser/device authentication and short-lived polling credentials.
- Support creating a new hook as part of
listen and selecting an existing account hook.
- Poll authenticated Hooky APIs using outbound HTTPS.
- Claim deliveries in bounded batches and renew leases during slow local processing.
- Forward only from the developer machine to the configured localhost destination.
- Preserve request semantics while removing hop-by-hop headers and rewriting
Host.
- Record local status, duration, selected response headers, and a bounded response preview.
- Reconnect with exponential backoff and recover from laptop sleep.
- Store durable credentials through the operating-system credential store.
Dashboard
- Provide hook creation, listing, disabling, deletion, and secret rotation.
- Provide event list and detail views.
- Provide request-body viewing appropriate to JSON, text, form, and binary payloads.
- Show current delivery state and every delivery attempt.
- Provide manual replay and retention controls.
- Prevent response bodies, secrets, and authorization headers from appearing in logs by default.
- Use
motion, not framer-motion, for any UI motion.
Security and operations
- Scope every database operation to the authenticated account.
- Encrypt captured sensitive values where appropriate and support configured header redaction.
- Hash API, CLI, lease, and hook secrets.
- Add per-account and per-hook quotas.
- Apply Vercel WAF rules suitable for non-browser webhook traffic; do not use interactive challenges on ingress routes.
- Add structured logs and metrics without recording raw secrets or bodies.
- Place Vercel Functions and Neon in the same primary region.
- Enforce retention with an authenticated scheduled cleanup operation.
Delivery milestones
- Bootstrap repository, continuous integration, preview deployment, and test harness.
- Implement database schema, durable ingress, and hook-management API.
- Implement claim protocol, lease state machine, local forwarding, and CLI authentication.
- Implement dashboard event inspection, delivery history, replay, retention, and rotation.
- Add security hardening, quotas, observability, failure injection, and load testing.
- Deploy production infrastructure and publish a prerelease CLI for a constrained public beta.
Testing Decisions
Tests should assert external behavior and invariants rather than private implementation details.
Unit tests
- Hook, API, CLI, and lease secret generation and verification.
- Header filtering and request reconstruction.
- Raw request serialization and body hashing.
- Retry and backoff calculation.
- Delivery state-machine transitions.
- CLI destination and path resolution.
- Response preview truncation and redaction.
Database integration tests
- Atomic event and delivery creation.
- Concurrent claims never share an active lease.
- Expired leases become claimable.
- ACK, NACK, lease extension, and replay are idempotent.
- Events remain immutable.
- Retention cleanup respects account settings.
- Cross-account reads and mutations fail.
- Secret rotation invalidates the old secret.
End-to-end tests
- Create hook, submit webhook, and inspect it in the dashboard.
- Submit while the CLI is offline, then start listening and receive the event locally.
- Crash after claiming and verify redelivery after lease expiration.
- Deliver locally but lose the ACK response and verify at-least-once redelivery.
- Preserve a binary payload byte-for-byte.
- Preserve path suffix, query parameters, method, and safe headers.
- Display failed local responses and allow replay.
- Enforce payload, event-count, and rate quotas.
- Reject requests using a rotated hook secret.
- Verify that no inbound connection to the developer machine is required.
Required continuous-integration gates
- Lint.
- Prettier check.
bun test.
bun run test:e2e.
- Production build.
Every code change must add or update tests at the appropriate layer.
Out of Scope
- Exactly-once local execution.
- Webhook payloads above the Vercel Function request limit.
- Direct cloud access to private or localhost destinations.
- Multiple independent destinations per hook.
- WebSocket-dependent delivery.
- Vercel Queues as the source of truth.
- Provider-specific signature verification in the first vertical slice.
- Public billing, plan upgrades, and metered invoicing.
- Native desktop and mobile applications.
- A self-hosted edition.
Further Notes
The first vertical slice should prove the core invariant with minimal UI: create a hook, persist an event while offline, start the CLI, forward to localhost, ACK the delivery, and inspect the result. Dashboard polish, provider integrations, and realtime wake-ups should not precede that proof.
The generated public webhook URL is a bearer secret. Rotation, redaction, rate limits, retention, and clear warnings are product requirements rather than optional hardening.
Vercel's request-size limit should be enforced slightly below the platform maximum so Hooky can return a deliberate error where possible and avoid presenting unsupported large-payload behavior.
Problem Statement
Developers need to receive third-party webhooks in local development without exposing a local HTTP server to the public internet. Reverse tunnels make the developer machine part of the availability path: webhooks can be lost while the machine sleeps, disconnects, restarts, or changes networks. Teams also lack a durable, searchable record of incoming webhook requests and their local delivery attempts.
Solution
Build Hooky, a multi-tenant SaaS that generates public webhook URLs, durably records incoming requests in Neon Postgres, and lets an authenticated
hookyCLI claim and forward stored events to a localhost destination using outbound HTTPS only.A webhook sender receives success only after the event and initial delivery record have committed. The CLI leases pending deliveries, reconstructs the original HTTP request locally, and acknowledges or rejects each attempt. Expired leases become eligible for redelivery. The web dashboard provides hook management, event inspection, delivery history, replay, retention controls, and secret rotation.
User Stories
Implementation Decisions
Domain vocabulary
Platform
FOR UPDATE SKIP LOCKED.motionif UI animation is introduced; do not addframer-motion.Durability
Ingress
CLI protocol
listenand selecting an existing account hook.Host.Dashboard
motion, notframer-motion, for any UI motion.Security and operations
Delivery milestones
Testing Decisions
Tests should assert external behavior and invariants rather than private implementation details.
Unit tests
Database integration tests
End-to-end tests
Required continuous-integration gates
bun test.bun run test:e2e.Every code change must add or update tests at the appropriate layer.
Out of Scope
Further Notes
The first vertical slice should prove the core invariant with minimal UI: create a hook, persist an event while offline, start the CLI, forward to localhost, ACK the delivery, and inspect the result. Dashboard polish, provider integrations, and realtime wake-ups should not precede that proof.
The generated public webhook URL is a bearer secret. Rotation, redaction, rate limits, retention, and clear warnings are product requirements rather than optional hardening.
Vercel's request-size limit should be enforced slightly below the platform maximum so Hooky can return a deliberate error where possible and avoid presenting unsupported large-payload behavior.