Docs

CLI & Tunneling

CLI & Tunneling is the local development and automation path for FastHook. The CLI can run the same resource operations as the public REST API, and it can keep a CLI destination connected to a receiver running on your machine.

The tunnel is outbound from your terminal to FastHook. You do not expose localhost directly, you do not need a temporary public URL, and your normal source, connection, filter, transformation, retry, and event inspection workflows still apply.

Use fasthook.exe on Windows or build fasthook-cli from source. Use fasthook-log when you want a simple local HTTP receiver that prints method, URL, headers, and body before you point traffic at your real app.

When To Use It

Use CLI tunneling when you need real FastHook delivery behavior against a service running on localhost. It is useful for provider webhook setup, local receiver development, replay debugging, and validating transformations before a destination is deployed.

  • Test real inbound source traffic against a local app.
  • Replay failed requests or events to localhost without changing the provider webhook URL.
  • Debug request headers, body shape, method, and path before deploying the receiver.
  • Keep production destinations separate while a developer runs a local CLI destination.

How The Pieces Fit

A CLI destination is configured in FastHook, but the local host and port are supplied at runtime by fasthook-cli. This keeps localhost details out of the cloud destination config and lets each developer choose their own local port.

fasthook-cli opens a WebSocket connection to the FastHook tunnel service. The tunnel service authenticates the project API key, verifies that the destination exists, belongs to the same team, is a CLI destination, and is enabled, then attaches that socket to the destination.

When FastHook sends an event to that CLI destination, the send worker posts a delivery job to the destination tunnel. The CLI receives the job, forwards it to your local URL, captures the local HTTP response, and sends that response back so the event attempt can be marked successful or failed.

  • FastHook source: stable public URL that receives provider or curl traffic.
  • Connection: routes accepted source traffic to the CLI destination.
  • CLI destination: cloud destination record with type CLI and an optional path.
  • Tunnel service: authenticated WebSocket endpoint at tunnel.fasthook.io.
  • fasthook-cli: local tunnel client that forwards each delivery to localhost.
  • fasthook-log or your app: local HTTP receiver that handles the request.

Delivery Flow

FastHook routes a source event to a CLI destination, then fasthook-cli forwards it to localhost for fasthook-log or your local app.
Flow
Provider or curl
  -> FastHook source
  -> request and event records
  -> connection rules, filters, transformations, retry policy
  -> CLI destination
  -> FastHook tunnel Durable Object for that destination
  -> fasthook-cli WebSocket
  -> http://localhost:8080
  -> fasthook-log or your local app
  -> local response returned to FastHook
  -> event attempt status updated

Install fasthook.exe Or Build From Source

The FastHook CLI is available as a standalone Windows executable on the fasthook-cli releases page. Download fasthook.exe when you want the quickest setup on Windows.

You can also clone the fasthook-cli repository and build it from source with Node.js 20 or newer. Source builds expose the same fasthook command through npx . inside the repository.

The examples below use fasthook. If fasthook.exe is in your current Windows directory, run the same commands as .\fasthook.exe or add the executable to PATH.

fasthook-log remains optional, but it is useful as a known-good receiver while you are proving the tunnel path.

Install
# Windows standalone executable
Release: https://github.com/alencmanis/fasthook-cli/releases/latest/download/fasthook.exe

Invoke-WebRequest `
  -Uri "https://github.com/alencmanis/fasthook-cli/releases/latest/download/fasthook.exe" `
  -OutFile ".\fasthook.exe"

.\fasthook.exe --help

# Source build
git clone https://github.com/alencmanis/fasthook-cli.git
cd fasthook-cli
npm install
npm run build
npx . --help

fasthook-log
Repository: https://github.com/alencmanis/fasthook-log
Clone:      git clone https://github.com/alencmanis/fasthook-log.git
Download:   https://github.com/alencmanis/fasthook-log/archive/refs/heads/main.zip

Create A CLI Destination

Create a destination in the dashboard and choose CLI as the destination type. For CLI destinations, FastHook stores the destination id, name, status, method, rate limits, and optional path. It does not store localhost host, localhost port, tunnel token, or local URL.

The destination path is normalized to start with /. Empty paths become /. That path is appended to the local target selected by fasthook-cli at runtime.

  • Open Destinations in the dashboard.
  • Create a new destination and choose CLI.
  • Set a name that identifies the local receiver, such as local-billing-api.
  • Set the path only if your local app expects a route, such as /webhooks/orders.
  • Keep the local port in the CLI command, not in the destination config.
  • Save the destination and copy its des_* id.
Path Joining
CLI destination path: /webhooks/orders
fasthook-cli target:   http://localhost:8080
final local URL:       http://localhost:8080/webhooks/orders

CLI destination path: /
fasthook-cli target:   http://localhost:9090/api
final local URL:       http://localhost:9090/api

Connect A Source

A CLI destination receives events only when a connection routes traffic to it. Create or edit a connection from your source to the CLI destination just like you would for an HTTP destination.

Filters, transformations, retries, bulk retry, delivery rate limits, and event inspection still apply. The only difference is the final transport: delivery goes through the tunnel instead of directly to a public HTTP URL.

  • Create or choose a source.
  • Create a connection from that source to the CLI destination.
  • Keep the connection enabled while the CLI tunnel is running.
  • Send a provider webhook, a curl request, or replay an existing request or event.

Get A Project API Key

fasthook-cli authenticates with a project API key. Use the API Key from Project Secrets in the dashboard, not the signing secret. The tunnel service accepts the key as x-api-key or as a Bearer token and checks that the key belongs to the same team as the CLI destination.

  • Open Settings, then Project Secrets.
  • Copy the API Key.
  • Keep it out of screenshots, logs, and committed files.
  • Rotate the key if it was exposed.
fasthook-cli
fasthook login --api-key fhp_xxx

Start fasthook-log

Start fasthook-log first when you want to verify the tunnel path before running your real local app. It listens on http://localhost:8080 by default, prints the request method, URL, headers, and body, then responds with JSON.

Keep this terminal open while the tunnel is running. If fasthook-log is stopped, not yet started, or listening on a different port, fasthook-cli can still connect to FastHook but each local delivery will fail with 502 Local Fetch Failed.

fasthook-log
npm install
npm start

# response from fasthook-log
{ "ok": true }

Verify The Local Receiver

Before debugging the tunnel, prove that the same machine can reach the local receiver directly. This separates FastHook tunnel issues from a local server that is not actually listening.

On Windows, use 127.0.0.1 explicitly if localhost resolution behaves differently in your shell or Node runtime. The CLI accepts either localhost or 127.0.0.1, but using the numeric loopback address removes ambiguity.

PowerShell
# PowerShell: confirm something is listening on 8080
Get-NetTCPConnection -LocalPort 8080

# PowerShell: direct local POST should return {"ok":true}
Invoke-WebRequest -Uri "http://127.0.0.1:8080" -Method POST -Body "probe" -UseBasicParsing

# If this direct POST fails, fix fasthook-log or your app before starting the tunnel.

Start fasthook-cli

In another terminal, log in with your project API key, save the default destination id, and start the tunnel. With a saved destination, fasthook tunnel uses http://localhost:8080 by default, matching fasthook-log. If you built from source, replace fasthook with npx . in these examples.

If you are troubleshooting Windows localhost behavior, pass the target explicitly as http://127.0.0.1:8080.

fasthook-cli
fasthook --help

fasthook login --api-key fhp_xxx
fasthook config --destination des_xxx
fasthook tunnel

# explicit local target
fasthook tunnel --to http://127.0.0.1:8080

One-Off Tunnel Commands

You can pass the destination and local target directly instead of saving them. The first positional argument can be a local target, or a destination id when it does not look like a port or URL.

fasthook-cli
fasthook tunnel --destination des_xxx --to 8080
fasthook tunnel --destination des_xxx --to http://localhost:8080
fasthook tunnel --destination des_xxx --to http://127.0.0.1:8080
fasthook tunnel des_xxx 8080
fasthook tunnel 8080
fasthook tunnel localhost:8080/webhooks

Command Reference

fasthook-cli stores only credentials and the default destination id. The local target is runtime-only. This is intentional: a destination path belongs to the cloud destination, while the host and port belong to the developer session.

Commands
fasthook login --api-key fhp_xxx [--team tm_xxx] [--destination des_xxx]
fasthook config [--team tm_xxx] [--destination des_xxx]
fasthook config
fasthook tunnel
fasthook tunnel 8080
fasthook tunnel --destination des_xxx --to http://localhost:8080
fasthook logout

Options:
  -d, --destination   CLI destination id
  -t, --to            Local target port or localhost URL
      --local-url     Alias for --to
      --api-key       Project API key, also accepted through FASTHOOK_API_KEY
      --team          Team id, also accepted through FASTHOOK_TEAM_ID
      --json          JSON body for API write commands
      --json-file     Read JSON body for API write commands from a file
  -q, --quiet         Print only connect, disconnect, and fatal errors
  -v, --verbose       Print per-delivery logs
  -h, --help          Show help

API Commands In The CLI

The CLI mirrors the FastHook REST API. Use resource commands for the common API groups, or use fasthook api when you want to call a route directly by method and path.

List and read commands accept query parameters as flags. Create, update, run, retry, bulk, and rotate commands accept JSON through --json or --json-file. The CLI prints the API response as formatted JSON.

API Commands
fasthook sources <list|get|create|update|delete|enable|disable|upsert>
fasthook destinations <list|get|create|update|delete|enable|disable|upsert>
fasthook connections <list|get|create|update|delete|pause|unpause|enable|disable|latest-input>
fasthook transformations <list|get|create|update|delete|upsert|run|executions|execution>
fasthook requests <list|count|get|retry|events|ignored-events|bulk-operations>
fasthook events <list|count|get|retry|bulk-operations>
fasthook attempts <list|get>
fasthook metrics <requests|events>
fasthook project-secrets <get|update|rotate>
fasthook auth <me|logout>
fasthook api <method> <path> [--json '{...}' | --json-file file.json]

# examples
fasthook sources list --limit 20 --dir desc
fasthook sources get src_xxx
fasthook destinations create --json-file destination.json
fasthook connections pause web_xxx
fasthook requests retry req_xxx
fasthook events bulk-operations create --json-file replay.json
fasthook transformations execution trs_xxx txe_xxx
fasthook metrics requests --from 2026-05-25T00:00:00Z --to 2026-05-25T23:59:59Z
fasthook api GET /sources --limit 20

Configuration And Environment Variables

By default, fasthook-cli writes credentials to ~/.fasthook/config.json. The file stores the project API key and optional default destination id. On platforms that support POSIX permissions, the CLI tries to write it with 0600 permissions.

Use FASTHOOK_CONFIG to choose a different config path, or use environment variables when you do not want to persist values locally.

Config
# stored config
~/.fasthook/config.json

# optional environment variables
FASTHOOK_CONFIG=C:\Users\you\.fasthook\dev-config.json
FASTHOOK_API_KEY=fhp_xxx
FASTHOOK_TEAM_ID=tm_xxx
FASTHOOK_DESTINATION_ID=des_xxx
FASTHOOK_LOCAL_URL=http://localhost:8080

Use A Custom Port

If your local receiver is not on 8080, run fasthook-log or your app on another port and pass the same port to fasthook-cli. The tunnel and the local receiver must agree on the port.

PowerShell
# terminal 1, in fasthook-log
npm start -- 9090

# terminal 2, in fasthook-cli
fasthook tunnel 9090

Use A Real Local App

fasthook-log is optional. When your application is ready, stop fasthook-log and run your app on the same port. The CLI command does not change unless the port changes.

fasthook-cli only forwards to local HTTP URLs. Bare ports, localhost, 127.0.0.1, and ::1 are accepted. Non-local URLs are rejected by the CLI with a 502 Invalid Local URL result so a CLI destination cannot be used as an arbitrary public proxy.

If your app listens on a route like /webhooks/provider, set the CLI destination path to that route or include a base path in the local target.

fasthook-cli
fasthook tunnel
fasthook tunnel 9090
fasthook tunnel --to http://localhost:8080
fasthook tunnel --to http://127.0.0.1:3000/api
fasthook tunnel --to http://[::1]:3000

What The CLI Forwards

The send worker sends the destination method, destination path, outbound headers, body text, request id, event id, connection id, and event data id to the tunnel. The CLI then performs a local HTTP request to the joined local URL.

Hop-by-hop headers such as host, connection, transfer-encoding, upgrade, and content-length are removed before forwarding. The CLI adds x-fasthook-tunnel: cli so your local receiver can tell that the request came through the tunnel. GET and HEAD requests are forwarded without a body.

Tunnel Job
Incoming tunnel job:
{
  "type": "delivery",
  "jobId": "...",
  "request": {
    "destinationId": "des_xxx",
    "localUrl": "http://localhost:8080",
    "path": "/webhooks/orders",
    "method": "POST",
    "headers": { "content-type": "application/json" },
    "body": "{...}",
    "requestId": "req_xxx",
    "eventId": "evt_xxx",
    "connectionId": "web_xxx",
    "eventDataId": "evd_xxx"
  }
}

Response And Attempt Status

FastHook treats the local response like a normal destination response. A 2xx local response marks the delivery attempt successful. A 4xx or 5xx local response marks the attempt failed and can trigger retry behavior according to the connection configuration.

The CLI sends the local response status, status text, response headers, and response body back through the WebSocket. FastHook stores those details on the event attempt just like it does for HTTP destinations.

Result
Local response -> tunnel result:
{
  "type": "delivery_result",
  "jobId": "...",
  "status": 200,
  "statusText": "OK",
  "headers": { "content-type": "application/json" },
  "body": "{\"ok\":true}"
}

Logging And Stats

fasthook-cli prints connection state, failed delivery lines, and aggregate stats by default. Stats include received, delivered, failed, and in-flight deliveries. The CLI also sends a WebSocket ping every 30 seconds and reconnects if the socket closes.

Use verbose mode when you want to see every delivery. Use quiet mode when you only want connection-level logs and fatal errors. fasthook-log prints every local HTTP request it receives and keeps up to 65536 bytes of body text by default.

fasthook-cli
fasthook tunnel --verbose
fasthook tunnel --quiet

# fasthook-log body logging limit
$env:MAX_BODY_BYTES="1048576"; npm start

Reconnects, Timeouts, And Multiple Clients

fasthook-cli reconnects automatically after disconnects. The reconnect delay starts at 500 ms and backs off up to 10 seconds. Stop the tunnel with Ctrl+C.

The tunnel service waits for the CLI to return a delivery result. The default delivery timeout is 30 seconds, with server-side clamping between 1 second and 60 seconds.

More than one CLI can connect to the same CLI destination. The tunnel Durable Object picks connected sockets in round-robin order. For deterministic development environments, create separate CLI destinations for separate developers or machines.

Troubleshooting

Most CLI tunnel issues fall into one of three groups: authentication, destination configuration, or local receiver behavior. Start by checking the CLI output, then inspect the event attempt in FastHook.

  • api_key_required or invalid_api_key: pass a valid project API key with login, --api-key, FASTHOOK_API_KEY, or x-api-key.
  • destination_not_found: the des_* id does not belong to the team for that API key.
  • destination_is_not_cli: the destination exists, but its type is not CLI.
  • destination_disabled: enable the CLI destination before connecting.
  • Tunnel Offline or cli_tunnel_offline: FastHook tried to deliver, but no CLI was connected for that destination.
  • Tunnel Timeout or cli_tunnel_timeout: the CLI did not return a local response before the delivery timeout.
  • Local URL Missing: the tunnel job did not include a local URL and the CLI did not have a runtime target.
  • Invalid Local URL: the target was not a localhost, 127.0.0.1, or ::1 HTTP URL.
  • Local Fetch Failed: the CLI connected, but your local server was not reachable, was not listening on that port, crashed, or closed the connection. First verify the receiver with a direct POST to http://127.0.0.1:8080.
  • Repeated 502 Local Fetch Failed lines: keep fasthook-log or your app running in one terminal, then start fasthook-cli in a second terminal. The tunnel can be connected even when the local receiver is down.
  • Deliveries that already failed with Local Fetch Failed are stored as failed attempts. After the receiver is running, send a new webhook or retry the failed event/request from FastHook.
  • Wrong path: check the destination path and any base path included in --to.
  • Nothing arrives locally: confirm the source is connected to the CLI destination and the connection is enabled.
  • Request arrives but fails: inspect fasthook-log or your app logs, then check the event attempt response body in FastHook.

Security Notes

CLI destinations are intended for local development and controlled debugging. For production delivery, use an HTTP destination that points at a deployed service.

The tunnel is authenticated with your project API key. Treat that key like a secret, avoid pasting it into public terminals or screenshots, and rotate it from Project Secrets if it is exposed.

The CLI only forwards to localhost-style HTTP targets. This reduces the risk of turning a CLI destination into a general-purpose outbound proxy.

Quick Checklist

  • Create a CLI destination and copy its des_* id.
  • Connect a source to the CLI destination.
  • Start fasthook-log or your app on localhost.
  • Install fasthook.exe or build fasthook-cli from source, then log in with the project API key.
  • Run fasthook tunnel with the destination id and local target.
  • Send a webhook to the source URL.
  • Check the local terminal output and the FastHook event attempt.

Next