Instant public tunnel
One command returns a public HTTPS URL. No account, no signup. Webhooks land on your machine in milliseconds.
tern-cli/Open source · MIT · npm
A public HTTPS URL for the webhooks you are building, with a live inspector on localhost. No ngrok account. Payloads stay in memory on your machine.
Zero setup
Zero infrastructure.
Getting started
No install step, no config file required, no account. Run npx and the tunnel is up.
# 1. Start the tunnel$ npx @hookflo/tern-dev --port 3000 ████████╗███████╗██████╗ ███╗ ██╗ ██║ ██╔════╝██╔══██╗ ████╗ ██║ ██║ █████╗ ██████╔╝ ██╔██╗██║ ██║ ██╔══╝ ██╔══██╗ ██║╚████║ ██║ ███████╗██║ ██║ ██║ ╚███║ ╚═╝ ╚══════╝╚═╝ ╚═╝ ╚═╝ ╚══╝ tunnel → https://abc123.relay.tern.hookflo.com dashboard → http://localhost:2019 forwarding → localhost:3000 # 2. Paste the tunnel URL into your webhook provider# 3. Open the dashboard and watch events arrive ✓ Tunnel live · Ctrl+C to end and erase the session
Core capabilities
Tunnel, inspect, replay, and verify — without a third-party account or a cloud log of your payloads.
One command returns a public HTTPS URL. No account, no signup. Webhooks land on your machine in milliseconds.
A local log at localhost:2019 with payloads, headers, response codes, and latency for every delivery.
Events stay in RAM. Nothing is written to disk, and nothing is indexed in the cloud. Ctrl+C clears the session.
Guidance for Stripe, GitHub, Clerk, and custom HMAC webhooks, so you can check integrity while you build.
Resend a captured webhook to your local server. Test failures without asking the provider to fire again.
Failed events sit in their own view. Inspect them, fix the handler, and replay without scrolling the full log.
Copy any event as a ready-to-run request. Share the exact payload with a teammate in one click.
Keep ports, rate limits, IP rules, path blocks, session TTL, and audit logs in tern.config.json.
Run hookflo/tern-relay on your own infrastructure and point the CLI at it with --relay.
Live dashboard
The dashboard at localhost:2019 shows each webhook as it arrives — body, headers, signature, and how long your handler took.
CLI reference
Every option the CLI accepts. The same keys can live in tern.config.json so you do not retype them each session.
| Flag | Type | Default | Description |
|---|---|---|---|
| --port | number | required | Local app port to forward requests to |
| --path | string | / | Path prefix for forwarded requests |
| --ui-port | number | 2019 | Dashboard HTTP port |
| --no-ui | boolean | false | Disable the dashboard and browser WebSocket server |
| --relay | string | wss://relay.tern.hookflo.com | Override the relay WebSocket URL for self-hosting |
| --max-events | number | 500 | Maximum events buffered in session memory |
Configuration
Drop tern.config.json in the project root. CLI flags merge over the file, so a one-off --port still wins.
{
// $schema: "schemastore.org/tern-dev"
"port": 3000,
"path": "/webhooks",
"uiPort": 2019,
"rateLimit": 100,
"maxEvents": 500,
"allowIp": [
"54.187.174.169",
"52.86.47.0/24"
],
"blockPaths": ["/health"],
"relay": "wss://relay.yourinfra.com",
"noUi": false
}Privacy by design
Captured events live in the process. A session does not write webhook bodies to disk.
The relay forwards payloads as they arrive. It does not store, index, or replay them later.
Ctrl+C ends the process. Events, headers, and the dashboard session go with it.
No account. No ngrok. No stored payloads. One command and you are debugging the real request.