{
  "schema": "leumas.docs.page/1",
  "id": "pkg:@leumas/adapter-webhook-forge",
  "slug": "adapters/adapter-webhook-forge",
  "kind": "capabilities",
  "bucket": "package",
  "title": "webhook-forge — HMAC webhook signing & verification",
  "name": "webhook-forge",
  "eyebrow": "HMAC webhook signing & verification",
  "chip": null,
  "summary": "Webhook signing + verification adapter pack — generic HMAC (sha256/384/512, hex/base64) with a timing-safe compare, Stripe's t=/v1= scheme with timestamp tolerance, GitHub's X-Hub-Signature-256, a...",
  "keywords": [
    "adapter-webhook-forge",
    "timing-safe",
    "x-hub-signature-256",
    "scheme",
    "tolerance",
    "leumas adapter webhook forge",
    "webhooks",
    "hmac"
  ],
  "audience": "both",
  "funnel": {
    "product": null,
    "cta": null
  },
  "body": "# webhook-forge — HMAC webhook signing & verification\n\nSign outgoing webhooks and verify incoming ones with `node:crypto` HMAC, across the schemes real\nproviders use (generic HMAC, Stripe, GitHub). Pure ESM, Node built-ins only, zero npm deps.\nSelf-contained and deterministic — no network, no stored secrets, timing-safe comparisons.\n\n## Tools\n\n| Tool | Input | Output |\n|---|---|---|\n| `sign` | `{ payload, secret, scheme?, encoding?, options?.header }` | Generic HMAC signature + ready-to-send header. |\n| `verify` | `{ payload, secret, signature, scheme?, encoding? }` | `{ valid, reason }` via timing-safe compare (accepts a `sha256=` prefix). |\n| `stripeSignature` | `{ payload, secret, timestamp? }` | `Stripe-Signature: t=<ts>,v1=<hex>` header. |\n| `stripeVerify` | `{ payload, secret, signature, toleranceSeconds? }` | Recomputes `v1`, timing-safe compare **and** timestamp tolerance (anti-replay). |\n| `githubVerify` | `{ payload, secret, signature }` | Verifies GitHub's `sha256=<hex>` X-Hub-Signature-256. |\n| `timestampToleranceCheck` | `{ timestamp, toleranceSeconds? }` | Is a unix timestamp fresh (within ± tolerance of now)? |\n| `replayGuard` | `{ id, options?.ttlSeconds }` | In-memory seen-id guard: first sight passes, repeats inside the TTL are rejected. |\n\n## Usage\n\n```js\nimport pack from './index.js';\n\n// outgoing\nconst s = pack.adapters.sign({ payload: '{\"a\":1}', secret: 'whsec_x', scheme: 'hmac-sha256' });\n// s.header -> { name:'X-Signature', value:'<hex>' }\n\n// incoming (Stripe)\nconst r = pack.adapters.stripeVerify({ payload: rawBody, secret: 'whsec_x', signature: header, toleranceSeconds: 300 });\n// r.valid, r.reason, r.ageSeconds\n```\n\n**Important:** for verification, pass the **exact raw body string** you received. Passing an object\nre-stringifies it and can reorder keys, breaking the signature — the tools warn via `warnObjectPayload`.\n\n## AI mode\n\nVerification is always decided deterministically. With `options.ai: true` and a reachable model, a\n**failed** verify additionally returns an `aiHint` explaining the likely cause (the promise resolves\nto the same result object with the hint attached). A model never changes the valid/invalid verdict.\n\n## DRY boundary\n\nHMAC signing/verification of webhook payloads. Not general hashing/encoding (see `crypto`) and it\nmakes no HTTP requests. Dockerfile tooling lives in `dockerfile`; dependency auditing in\n`dependency-auditor`.\n",
  "source": {
    "path": "shared/engines/adapters/domain/webhook-forge/README.md",
    "blobSha": "",
    "commit": "",
    "committedAt": "",
    "provenance": "no-git",
    "bytes": 2651,
    "hash": "fd2aef738cfab78e24d2e1f70c5130bf0499c3a9"
  },
  "urls": {
    "html": "/p/adapters/adapter-webhook-forge",
    "json": "/docs/adapters/adapter-webhook-forge.json",
    "md": "/docs/adapters/adapter-webhook-forge.md"
  },
  "links": {
    "composes": [],
    "usedBy": [
      "pkg:@leumas/webhooks"
    ],
    "product": [],
    "howTo": [],
    "skills": []
  },
  "exports": null
}
