Repository navigation
feat(webhooks): outbound subscription delivery (partial, flag-gated) - #571
Merged
Darkvader-ship-it merged 1 commit intoOct 17, 2026
Conversation
|
@zane1502 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
|
Hey @zane1502! 👋 It looks like this PR isn't linked to any issue. If this PR is for one of the issues assigned to you as part of a Wave, please link it to ensure your contribution is tracked properly. You can do this by adding a keyword to the PR description (e.g.,
|
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
Adds a working, flag-gated slice of outbound webhook delivery: third-party subscribers can register an endpoint, PHASE stores a per-endpoint signing secret, and events are delivered to that endpoint with an HMAC signature the subscriber can verify — the mirror image of the existing inbound NanoBanana webhook verifier.
Changes
lib/webhook-subscriptions.ts(new): CRUD for webhook subscriptions (create/list/get/delete/rotate-secret), persisted through the existinglib/json-store.tslocked JSON sidecar pattern. Includes a simple failure-count circuit breaker that deactivates a subscription after 10 consecutive delivery failures.lib/webhook-delivery.ts(new): signs and delivers an event payload to a subscriber. Signature isHMAC-SHA256(secret, "<timestamp>.<nonce>.<rawBody>"), sent asx-phase-signature/x-phase-timestamp/x-phase-nonce, mirroringapp/api/webhooks/nanobanana/route.ts's inbound verifier in reverse. Delivery retries with a fixed backoff schedule (immediate, +1s, +4s) and is fire-and-forget per subscriber.app/api/admin/webhook-subscriptions/route.ts+[id]/route.ts(new): admin-token-gated CRUD endpoints (list/create/delete/rotate), following the sameADMIN_API_TOKENbearer-auth convention used byapp/api/admin/test-webhooks/route.tsandapp/api/admin/quests/route.ts.app/api/admin/test-subscription-webhooks/route.ts(new): sends a synthetic event to all active subscribers of a given event type, so delivery/signing can be verified end-to-end without a real settle/offer/faucet event, mirroring the existingtest-webhooksadmin hook.lib/feature-flags.ts: addsphase-154(default off).lib/server-data-paths.ts: registers thewebhook-subscriptions.jsonsidecar file.PROJECT_ARCHITECTURE.md§10: adds thephase-154flag row with rollback note.Scope and residual risk
This is a partial slice of issue #527 (Spike 664), which is explicitly scoped in the issue itself as a 4–8 week, 16-file, high-risk spike still in "proposed, not yet implemented" status. The following acceptance criteria from the issue are not covered by this PR:
settle.verified/market.offer_accepted/faucet.claim_completedare defined as subscribable event types, but nothing inapp/api/market,app/api/faucet, or the settle path actually callsdispatchWebhookEventyet. The only caller is the synthetic test-dispatch route, so delivery/signing can be verified manually but no real PHASE event triggers it today.http(s)scheme check.lib/json-store.ts).lib/phase-copy.tsis untouched.docs/TECHNICAL.mdwrite-up ordocs/spikes/findings doc — only thePROJECT_ARCHITECTURE.md§10 flag row is added.The flag defaults off and every new route 404s when it is off, so there is no behavior change for existing users with the flag unset.
This PR touches only issue #527. Issues #528, #529, and #530 are separate spikes and are not addressed by this PR in any way.
Issues
Partial progress on issue 527 — see Scope above for what is and is not covered. This PR is not a closing PR for that issue; the issue should remain open after merge.
Verification
Manual code review only: read through the existing inbound webhook verifier (
app/api/webhooks/nanobanana/route.ts), the JSON sidecar locking pattern (lib/json-store.ts,lib/generation-job-store.ts), the admin auth convention (app/api/admin/test-webhooks/route.ts,app/api/admin/quests/route.ts), and the feature-flag table (lib/feature-flags.ts,PROJECT_ARCHITECTURE.md§10) before writing the new code, then re-read every new/changed file end to end against those patterns.tsc --noEmitandvitest runwere not run — per process, no build or test commands were executed for this change. No regression is expected since every new route is gated behindphase-154(default off) and no existing route or file was modified in a way that changes its behavior when the flag is off.