@octokit/webhooks-methods
Sign and verify GitHub webhook payload signatures with constant-time HMAC comparison, in Node.js and the browser.
Repository Health
Technical Analysis
@octokit/webhooks-methods is a small, focused utility from the Octokit family that implements the exact cryptographic contract GitHub uses to sign webhook deliveries: HMAC SHA-256 over the raw payload string, expressed as a sha256=<hex> signature header. It exposes three functions — sign() to produce a signature, verify() to check one against a secret, and verifyWithFallback() to check a signature against a primary secret and a list of additional secrets in turn, which is what makes zero-downtime webhook secret rotation possible without hand-rolling the loop yourself.
The library ships two implementations from one TypeScript source tree: a Node.js build using node:crypto (createHmac, timingSafeEqual) and a browser/Deno-compatible build using the Web Crypto SubtleCrypto API, built separately via esbuild so neither runtime pulls in APIs it doesn’t have. Signature comparison is always constant-time to prevent timing attacks, and every function throws a clearly-prefixed TypeError when required arguments are missing or the payload isn’t a raw string — a common webhook-handling mistake when payloads have already been JSON-parsed.
What You Get
sign(secret, payload)— produces asha256=<hex>HMAC signature string for a raw payload stringverify(secret, payload, signature)— constant-time comparison of a computed signature against the one GitHub sentverifyWithFallback(secret, payload, signature, additionalSecrets)— tries the primary secret then each fallback secret in turn, enabling zero-downtime secret rotation- Dual Node.js (
node:crypto) and browser/Deno (SubtleCrypto) builds compiled from one TypeScript source tree via esbuild - Strict input validation — every function throws a descriptive
TypeErrorfor missing arguments or a non-string payload instead of silently miscomparing
Common Use Cases
- Verifying inbound GitHub App or webhook deliveries before trusting the payload in an Express, Fastify, or serverless handler
- Rotating a webhook secret in production without downtime by passing the old secret into
verifyWithFallback’sadditionalSecretslist until every delivery source is updated - Signing synthetic webhook payloads in integration tests to exercise a service’s real signature-verification code path
- Running the same verification logic in an edge/browser runtime (e.g. a Cloudflare Worker) via the SubtleCrypto build, without pulling in Node’s
cryptomodule
Under The Hood
Architecture
The package is a stateless utility library, not a service or framework, so its architecture is about clean platform separation rather than layering: src/node/sign.ts and src/node/verify.ts implement the public API with node:crypto, src/web.ts re-implements the identical sign/verify/verifyWithFallback contract with the Web Crypto SubtleCrypto API for browser and Deno consumers, and src/index.ts is the Node-facing entry point that re-exports the node implementation plus a shared verifyWithFallback. scripts/build.mjs drives esbuild to produce three separate output bundles (dist-src, dist-node, dist-web) from these entry points, so a browser bundler never sees a node:crypto import and a Node consumer never pulls in SubtleCrypto polyfill code. There’s no dependency injection or complex data flow to speak of — correctness here means both implementations staying behaviorally identical, which the shared test fixtures in test/common.ts and duplicated test suites (Node via vitest, browser via Puppeteer, Deno via deno test) exist specifically to guarantee.
Tech Stack
Written in TypeScript targeting Node >= 20, using node:crypto’s createHmac/timingSafeEqual for the server build and the Web Crypto crypto.subtle API for the browser/Deno build. Built with esbuild (scripts/build.mjs) into ESM output, type-checked separately via tsc against @octokit/tsconfig. Tested with Vitest (@vitest/coverage-v8 for coverage, plus a vitest bench suite comparing sign/verify performance), Puppeteer for real-browser testing, and Deno’s built-in test runner for the Deno target. Formatting is enforced by Prettier (no ESLint) and releases are automated via semantic-release with the GitHub, npm, and commit-analyzer plugins, publishing from a pkg/ directory assembled by the build script.
Code Quality
Test coverage is thorough for a library this size: test/sign.test.ts and test/verify.test.ts cover the happy path, every missing-argument error branch, wrong-type payloads, and a named regression test (issue #71) for control-character encoding edge cases across escaped and literal ANSI sequences. Error handling is explicit and consistent — every public function validates its own arguments and throws a TypeError prefixed with [@octokit/webhooks-methods] rather than allowing a downstream crash or a silent false result. Naming is minimal and consistent across the Node and web implementations, and CI runs the full matrix (Node, browser via Puppeteer, Deno) on every change, which is unusually rigorous cross-runtime verification for a package this small.
API Design
The public surface is deliberately tiny — three functions, no configuration object, no class to instantiate — which matches exactly what a webhook consumer needs and nothing more. verifyWithFallback folds a whole secret-rotation pattern (try current secret, then try each previous one) into a single call instead of leaving every consumer to write that loop themselves, which is the one place this library adds real value beyond wrapping HMAC. VERSION is attached directly as a property on the exported sign/verify functions rather than requiring a separate import, a small ergonomic touch. It isn’t a novel cryptographic approach — HMAC-SHA256 with constant-time comparison is the standard, correct way to verify GitHub webhooks — but it packages that standard correctly and portably, which is exactly what a shared utility in this position should do.
Used by 2 apps in this directory
Botpress
AI Assistants · AI Development · Customer Support
The open-source hub for building and deploying LLM-powered AI agents with TypeScript-first tooling, 40+ integrations, and a revolutionary code-execution agent framework.
Mistle
AI Agents · Developer Tools
Self-hostable platform for running autonomous coding agents in isolated, credentialless sandboxes with brokered credentials, reusable snapshots, and event-driven triggers.