Skip to content

Add signed Coinbase webhook example endpoint with HMAC verification#296

Merged
GsCommand merged 1 commit into
mainfrom
codex/create-coinbase-cdp-webhook-signed-receipt-endpoint
May 23, 2026
Merged

Add signed Coinbase webhook example endpoint with HMAC verification#296
GsCommand merged 1 commit into
mainfrom
codex/create-coinbase-cdp-webhook-signed-receipt-endpoint

Conversation

@GsCommand
Copy link
Copy Markdown
Contributor

Motivation

  • Provide a server-side example that verifies Coinbase CDP X-Hook0-Signature HMAC before parsing JSON and only signs normalized CLAS receipts after successful verification.
  • Enable portable, Ed25519-signed receipts that downstream systems can verify using existing verifier logic and ENS-distributed public keys.

Description

  • Added api/examples/coinbase-webhook.js implementing a POST-only endpoint that uses lib/coinbaseWebhook.js to parse t, h, v1, reconstruct the signed payload (${timestamp}.${signedHeaderNames}.${signedHeaderValues}.${rawBody}), enforce freshness (COINBASE_WEBHOOK_MAX_AGE_SECONDS, default 300), verify HMAC-SHA256 with crypto.timingSafeEqual, and parse JSON only after HMAC passes.
  • Added lib/receiptSigning.js to canonicalize receipts with json.sorted_keys.v1, compute SHA-256 of the canonical payload, and produce an Ed25519 signature placed in metadata.proof with hash, signature (alg: "Ed25519", kid, value, role: "runtime"), canonicalization, and signer_id fields consistent with lib/verifyReceipt.js expectations.
  • Normalization builds receipts with receipt_id: rcpt:coinbase_cdp:<event_id>, verb: observe, source: coinbase.cdp.webhook, metadata.trace, and metadata.proof; the endpoint deduplicates by Coinbase event id using an in-memory Map for the example.
  • Documentation updated at docs/integrations/coinbase-cdp-webhook-receipts.md with a new "Signed example endpoint" section and required env vars: COINBASE_WEBHOOK_SECRET, COINBASE_WEBHOOK_MAX_AGE_SECONDS (optional), CL_RECEIPT_SIGNER_ID, CL_RECEIPT_SIGNING_PRIVATE_KEY_PEM, and CL_RECEIPT_SIGNING_KID.

Testing

  • Added tests/api-coinbase-webhook.test.js covering: non-POST 405, missing Coinbase secret 503, missing signature 400, malformed signature 400, stale timestamp 400, invalid HMAC 400, malformed JSON after valid HMAC 400, missing signing env 503, valid signed payload 200 with duplicate replay returning the same receipt; tests exercise local verification via an injected ENS text resolver.
  • Ran npm test (node --test tests/*.test.js) and all tests passed (62 passing, 0 failing).
  • Verified rg -n "erc8211\.merkle\.v1" returned no matches.

Codex Task

@vercel
Copy link
Copy Markdown

vercel Bot commented May 23, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
commandlayer-commandlayer-org Ready Ready Preview, Comment May 23, 2026 12:24am
commandlayer-org Ready Ready Preview, Comment May 23, 2026 12:24am
commandlayer-org111 Ready Ready Preview, Comment May 23, 2026 12:24am

Request Review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant