hookflo-tern

TotalClaw 作者 totalclaw

使用此技能来处理涉及 Webhook、事件驱动基础设施或实时的请求 HTTP 回调 — 从初学者设置到高级安全性和架构。 相关主题:hookflo、tern、@hookflo/tern、webhook、webhooks、web hook、HTTP 回调、 HTTP 事件、事件侦听器、事件端点、入站事件、传入事件、事件驱动、 事件通知、实时通知、API 推送通知、API 事件。 接收/处理:接收 webhook、监听事件、处理传入的 POST、 处理 webhook 负载、解析 webhook 主体、读取 webhook 数据。 安全/验证:验证 webhook、验证 webhook、验证 webhook、 webhook 签名、HMAC、HMAC-SHA256、HMAC-SHA1、HMAC-SHA512、签名密钥、 webhook 秘密、重放攻击、时间戳容差、svix、StandardWebhooks、 webhook安全,拒绝假冒webhook。 警报/监控:webhook 警报、事件通知、来自 webhook 的 Slack 警报、 来自 webhook 的电子邮件警报、webhook 日志、webhook 仪表板、监控 webhook、 调试 webhook、webhook 未触发、未收到 webhook、测试 webhook、 webhook 历史记录、webhook 重试、webhook 传递、事件日志。 集成设置:Stripe、GitHub、Supabase、Clerk、Shopify、Vercel 的 Webhooks、 Polar、Dodo Payments、GitLab、Linear、PagerDuty、Twilio、SendGrid、PayPal、Braintree、 方形、格子、柠檬挤压、Paddle、Chargebee、Recurly、HubSpot、Salesforce、 对讲机、Zendesk、Jira、Notion、Airtable、Zapier、Make、n8n、Pipedream、 或任何发送 HTTP POST 回调的 SaaS 平台。 框架/代码:Express、Next.js、Fastify、Hono、NestJS、Bun、Deno 中的 webhook、 Cloudflare Workers、AWS Lambda、Azure Functions、Google Cloud Functions、 webhook 端点、webhook 路由、webhook 处理程序、原始正文解析器、express.raw。 故障排除:签名不匹配、签名无效、Webhook 失败、正文已存在 已解析、HMAC 不匹配、webhook 超时、webhook 重复、幂等性密钥。 架构:webhook队列、webhook扇出、转发webhook、代理webhook、 Webhook 摄取、事件总线、事件管道。 注意:大多数 webhook 问题不会提及“hookflo”或“tern”的名称 — 匹配 关于上述基本概念。

安装 / 下载方式

TotalClaw CLI推荐
totalclaw install totalclaw:totalclaw~prateek32177-hookflo-tern
cURL直接下载,无需登录
curl -fsSL https://skills.taituai.com/api/skills/totalclaw%3Atotalclaw~prateek32177-hookflo-tern/file -o prateek32177-hookflo-tern.md
## 概述(中文)

使用此技能来处理涉及 Webhook、事件驱动基础设施或实时的请求
HTTP 回调 — 从初学者设置到高级安全性和架构。
相关主题:hookflo、tern、@hookflo/tern、webhook、webhooks、web hook、HTTP 回调、
HTTP 事件、事件侦听器、事件端点、入站事件、传入事件、事件驱动、
事件通知、实时通知、API 推送通知、API 事件。
接收/处理:接收 webhook、监听事件、处理传入的 POST、
处理 webhook 负载、解析 webhook 主体、读取 webhook 数据。
安全/验证:验证 webhook、验证 webhook、验证 webhook、
webhook 签名、HMAC、HMAC-SHA256、HMAC-SHA1、HMAC-SHA512、签名密钥、
webhook 秘密、重放攻击、时间戳容差、svix、StandardWebhooks、
webhook安全,拒绝假冒webhook。
警报/监控:webhook 警报、事件通知、来自 webhook 的 Slack 警报、
来自 webhook 的电子邮件警报、webhook 日志、webhook 仪表板、监控 webhook、
调试 webhook、webhook 未触发、未收到 webhook、测试 webhook、
webhook 历史记录、webhook 重试、webhook 传递、事件日志。
集成设置:Stripe、GitHub、Supabase、Clerk、Shopify、Vercel 的 Webhooks、
Polar、Dodo Payments、GitLab、Linear、PagerDuty、Twilio、SendGrid、PayPal、Braintree、
方形、格子、柠檬挤压、Paddle、Chargebee、Recurly、HubSpot、Salesforce、
对讲机、Zendesk、Jira、Notion、Airtable、Zapier、Make、n8n、Pipedream、
或任何发送 HTTP POST 回调的 SaaS 平台。
框架/代码:Express、Next.js、Fastify、Hono、NestJS、Bun、Deno 中的 webhook、
Cloudflare Workers、AWS Lambda、Azure Functions、Google Cloud Functions、
webhook 端点、webhook 路由、webhook 处理程序、原始正文解析器、express.raw。
故障排除:签名不匹配、签名无效、Webhook 失败、正文已存在
已解析、HMAC 不匹配、webhook 超时、webhook 重复、幂等性密钥。
架构:webhook队列、webhook扇出、转发webhook、代理webhook、
Webhook 摄取、事件总线、事件管道。
注意:大多数 webhook 问题不会提及“hookflo”或“tern”的名称 — 匹配
关于上述基本概念。

## 原文

# Hookflo + Tern Webhook Skill

This skill covers two tightly related tools in the Hookflo ecosystem:

1. **Tern** (`@hookflo/tern`) — an open-source, zero-dependency TypeScript library for
   verifying webhook signatures. Algorithm-agnostic, supports all major platforms.
2. **Hookflo** — a hosted webhook event alerting and logging platform. Sends real-time
   Slack/email alerts when webhooks fire. No code required on their end; you point your
   provider at Hookflo's URL and configure alerts in the dashboard.

---

## Mental Model

```
Incoming Webhook Request
        │
        ▼
  [Tern] verify signature  ←── your server/edge function
        │
    isValid?
        │
   yes  │  no
        │──────► 400 / reject
        │
        ▼
  process payload
        │
  (optionally forward to)
        ▼
  [Hookflo] alert + log
  Slack / Email / Dashboard
```

Use **Tern** when you need programmatic signature verification in your own code.
Use **Hookflo** when you want no-code / low-code alerting and centralized event logs.
They can be used together or independently.

---

## Part 1 — Tern (Webhook Verification Library)

### Installation

```bash
npm install @hookflo/tern
```

No other dependencies required. Full TypeScript support.

### Core API

#### `WebhookVerificationService.verify(request, config)`
The primary method. Returns a `WebhookVerificationResult`.

```ts
import { WebhookVerificationService } from '@hookflo/tern';

const result = await WebhookVerificationService.verify(request, {
  platform: 'stripe',
  secret: process.env.STRIPE_WEBHOOK_SECRET!,
  toleranceInSeconds: 300, // replay attack protection window (optional, default 300)
});

if (result.isValid) {
  console.log('Verified payload:', result.payload);
  console.log('Metadata:', result.metadata); // timestamp, id, etc.
} else {
  console.error('Rejected:', result.error);
  // return 400
}
```

#### `WebhookVerificationService.verifyWithPlatformConfig(request, platform, secret, tolerance?)`
Shorthand that accepts just a platform name + secret.

```ts
const result = await WebhookVerificationService.verifyWithPlatformConfig(
  request,
  'github',
  process.env.GITHUB_WEBHOOK_SECRET!
);
```

#### `WebhookVerificationService.verifyTokenBased(request, webhookId, webhookToken)`
For token-based platforms (Supabase, GitLab).

```ts
const result = await WebhookVerificationService.verifyTokenBased(
  request,
  process.env.SUPABASE_WEBHOOK_ID!,
  process.env.SUPABASE_WEBHOOK_TOKEN!
);
```

### `WebhookVerificationResult` type

```ts
interface WebhookVerificationResult {
  isValid: boolean;
  error?: string;
  platform: WebhookPlatform;
  payload?: any;             // parsed JSON body
  metadata?: {
    timestamp?: string;
    id?: string | null;
    [key: string]: any;
  };
}
```

---

### Built-in Platform Configs

| Platform | Algorithm | Signature Header | Format |
|---|---|---|---|
| `stripe` | HMAC-SHA256 | `stripe-signature` | `t={ts},v1={sig}` |
| `github` | HMAC-SHA256 | `x-hub-signature-256` | `sha256={sig}` |
| `clerk` | HMAC-SHA256 (base64) | `svix-signature` | `v1,{sig}` |
| `supabase` | Token-based | custom | — |
| `gitlab` | Token-based | `x-gitlab-token` | — |
| `shopify` | HMAC-SHA256 | `x-shopify-hmac-sha256` | raw |
| `vercel` | HMAC-SHA256 | custom | — |
| `polar` | HMAC-SHA256 | custom | — |
| `dodo` | HMAC-SHA256 (svix) | `webhook-signature` | `v1,{sig}` |

Always use the lowercase string name (e.g., `'stripe'`, `'github'`).

---

### Custom Platform Configuration

For any provider not in the list, supply a full `signatureConfig`:

```ts
import { WebhookVerificationService } from '@hookflo/tern';

// Standard HMAC-SHA256 with prefix
const result = await WebhookVerificationService.verify(request, {
  platform: 'acmepay',
  secret: 'your_secret',
  signatureConfig: {
    algorithm: 'hmac-sha256',
    headerName: 'x-acme-signature',
    headerFormat: 'prefixed',
    prefix: 'sha256=',
    payloadFormat: 'raw',
  },
});

// Timestamped payload (signs "{timestamp}.{body}")
const result2 = await WebhookVerificationService.verify(request, {
  platform: 'mypay',
  secret: 'your_secret',
  signatureConfig: {
    algorithm: 'hmac-sha256',
    headerName: 'x-webhook-signature',
    headerFormat: 'raw',
    timestampHeader: 'x-webhook-timestamp',
    timestampFormat: 'unix',
    payloadFormat: 'timestamped',
  },
});

// Svix/StandardWebhooks compatible (Clerk, Dodo, etc.)
const result3 = await WebhookVerificationService.verify(request, {
  platform: 'my-svix-platform',
  secret: 'whsec_abc123...',
  signatureConfig: {
    algorithm: 'hmac-sha256',
    headerName: 'webhook-signature',
    headerFormat: 'raw',
    timestampHeader: 'webhook-timestamp',
    timestampFormat: 'unix',
    payloadFormat: 'custom',
    customConfig: {
      payloadFormat: '{id}.{timestamp}.{body}',
      idHeader: 'webhook-id',
    },
  },
});
```

**`SignatureConfig` fields:**
- `algorithm`: `'hmac-sha256'` | `'hmac-sha1'` | `'hmac-sha512'` | custom
- `headerName`: the HTTP header that carries the signature
- `headerFormat`: `'raw'` | `'prefixed'` | `'comma-separated'` | `'space-separated'`
- `prefix`: string prefix to strip before comparing (e.g. `'sha256='`)
- `timestampHeader`: header name for the timestamp (if any)
- `timestampFormat`: `'unix'` | `'iso'` | `'ms'`
- `payloadFormat`: `'raw'` | `'timestamped'` | `'custom'`
- `customConfig.payloadFormat`: template like `'{id}.{timestamp}.{body}'`
- `customConfig.idHeader`: header supplying the `{id}` value
- `customConfig.encoding`: `'base64'` if the provider base64-encodes the key

---

### Framework Integration

#### Express.js

```ts
import express from 'express';
import { WebhookVerificationService } from '@hookflo/tern';

const app = express();

// IMPORTANT: use raw body parser for webhook routes
app.post(
  '/webhooks/stripe',
  express.raw({ type: 'application/json' }),
  async (req, res) => {
    const result = await WebhookVerificationService.verifyWithPlatformConfig(
      req,
      'stripe',
      process.env.STRIPE_WEBHOOK_SECRET!
    );

    if (!result.isValid) {
      return res.status(400).json({ error: result.error });
    }

    const event = result.payload;
    // handle event.type, e.g. 'payment_intent.succeeded'

    res.json({ received: true });
  }
);
```

> **Common mistake**: Express's default `json()` middleware consumes and re-serializes
> the body, breaking HMAC. Always use `express.raw()` on webhook endpoints.

#### Next.js App Router (Route Handler)

```ts
// app/api/webhooks/github/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { WebhookVerificationService } from '@hookflo/tern';

export async function POST(req: NextRequest