feat: distributed multi-node webhook delivery engine with HMAC signing and DLQ recovery - #450
Merged
jotel-dev merged 2 commits intoAug 30, 2026
Conversation
…g and DLQ recovery Adds a developer-facing webhook system distinct from the existing single-destination ops alert: developers register a target URL and receive signed trade-status events instead of nothing, or worse, an unsigned callback a third party could spoof. - webhook_endpoints / webhook_delivery_logs (migration 030), with a Pool-or-in-memory store mirroring SwapDisputeStore, including SELECT ... FOR UPDATE DLQ replay so a delivery is only ever re-enqueued once. - Every delivery is enqueued onto a Redis Stream (velo:webhook-delivery-queue) rather than sent inline, so a slow or dead client endpoint can never block an API response. - webhookDeliveryWorker drains the stream, signs with HMAC-SHA256 (x-velo-signature), and retries with exponential backoff up to 5 attempts before dead-lettering. - POST /webhooks/endpoints, GET /webhooks/endpoints, GET /webhooks/endpoints/:id/deliveries, POST /webhooks/dlq/replay. - cash.ts's refund flow now also notifies developer-registered webhooks for both trade participants, alongside the existing ops alert. - WebhookSettings.tsx: register an endpoint, view its secret, monitor recent deliveries, and manually replay anything dead-lettered. Closes Nullifier-Systems#445
|
@wheval is attempting to deploy a commit to the jotelfootball-tech's projects Team on Vercel. A member of the Team first needs to authorize it. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
|
@wheval The node CI is failing kindly fix that |
The repo's localization:check CI gate flags any raw user-facing JSX text/attribute as an error. Adds a webhookSettings namespace to both locale catalogs and switches the page to useTranslation()/t().
Contributor
|
All checks pass, no conflicts. Reviewed and looks good — merging now. Thanks for the contribution! |
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #445. Adds a developer-facing signed webhook delivery system, distinct from the existing single-destination ops alert in
lib/webhook.ts(Slack/Discord).030_add_distributed_webhook_pipeline.sql):webhook_endpoints(one row per developer-registered target URL + HMAC secret) andwebhook_delivery_logs(one row per delivery attempt, with status/attempt tracking).webhookDeliveryStore.ts):Pool-or-in-memory, mirroringSwapDisputeStore's shape so tests run with no database. DLQ replay usesSELECT ... FOR UPDATEso a delivery is only ever re-queued once even if an operator and a retry sweep race.velo:webhook-delivery-queue(Redis Stream) rather than sent inline — a slow or dead client endpoint can never block an API response.webhookDeliveryWorker.tsdrains it, signs the exact payload bytes with HMAC-SHA256 (x-velo-signature), and retries with exponential backoff + jitter up to 5 attempts before dead-lettering (mirrorssessionRotationWorker's retry shape).webhooks.ts):POST /webhooks/endpoints,GET /webhooks/endpoints,GET /webhooks/endpoints/:id/deliveries,POST /webhooks/dlq/replay. HTTPS is enforced fortarget_urlwhenNODE_ENV=production.cash.ts's refund flow now also callsnotifyDeveloperWebhooksfor both trade participants (buyer + seller), alongside the existingsendRefundAlertops notification — same trigger points, no behavior change to the ops alert itself.WebhookSettings.tsx— register an endpoint, view its secret once, monitor recent deliveries, and manually replay anything dead-lettered.Test plan
npx tsc -b/-p apps/api/tsconfig.json --noEmitclean (repo-wide pre-existing errors inbatch-auction-engine.test.ts/prekey-vault.tsare unrelated and untouched by this PR)webhookDeliveryStore.test.ts), store behavior including DLQ replay's single-claim guaranteeapps/apisuite: 562 passed, 0 regressions (had to updatecash.test.ts'swebhook.jsmock to include the newnotifyDeveloperWebhooksexport)mobile/frontendsuite: 77 passed, 0 regressions