Skip to content

feat(webhooks): outbound subscription delivery (partial, flag-gated) - #571

Merged
Darkvader-ship-it merged 1 commit into
PHASE-STELLAR:mainfrom
zane1502:feat/spike-527-webhook-delivery-partial
Oct 17, 2026
Merged

Darkvader-ship-it merged 1 commit into
PHASE-STELLAR:mainfrom
zane1502:feat/spike-527-webhook-delivery-partial

Conversation

@zane1502

@zane1502 zane1502 commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

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 existing lib/json-store.ts locked 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 is HMAC-SHA256(secret, "<timestamp>.<nonce>.<rawBody>"), sent as x-phase-signature / x-phase-timestamp / x-phase-nonce, mirroring app/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 same ADMIN_API_TOKEN bearer-auth convention used by app/api/admin/test-webhooks/route.ts and app/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 existing test-webhooks admin hook.
  • lib/feature-flags.ts: adds phase-154 (default off).
  • lib/server-data-paths.ts: registers the webhook-subscriptions.json sidecar file.
  • PROJECT_ARCHITECTURE.md §10: adds the phase-154 flag 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:

  • No end-to-end testnet proof (no tx hashes/contract IDs recorded) — this slice has no on-chain component.
  • No production event wiring: settle.verified / market.offer_accepted / faucet.claim_completed are defined as subscribable event types, but nothing in app/api/market, app/api/faucet, or the settle path actually calls dispatchWebhookEvent yet. The only caller is the synthetic test-dispatch route, so delivery/signing can be verified manually but no real PHASE event triggers it today.
  • No concurrency proof (no overlapping-writer 409 test), abuse proof (no SSRF fencing, no endpoint-verification challenge-response, no self-deal/replay-abuse test), or resilience proof (no RPC/gateway failover demonstration) — none of those apply to this slice as built, and none are claimed here.
  • No endpoint-verification handshake; a malicious subscriber URL is not fenced against SSRF beyond a basic http(s) scheme check.
  • No durable cross-instance delivery ordering — delivery is per-process, fire-and-forget, same single-instance scope limit as every other JSON sidecar store in this repo (see lib/json-store.ts).
  • No i18n changes — this slice adds no user-facing UI text, so lib/phase-copy.ts is untouched.
  • No docs/TECHNICAL.md write-up or docs/spikes/ findings doc — only the PROJECT_ARCHITECTURE.md §10 flag row is added.
  • No redelivery UI; the circuit breaker just deactivates a subscription after repeated failures.

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 --noEmit and vitest run were not run — per process, no build or test commands were executed for this change. No regression is expected since every new route is gated behind phase-154 (default off) and no existing route or file was modified in a way that changes its behavior when the flag is off.

@drips-wave

drips-wave Bot commented Oct 17, 2026

Copy link
Copy Markdown

@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! 🚀

Learn more about application limits

@drips-wave

drips-wave Bot commented Oct 17, 2026

Copy link
Copy Markdown

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., Closes #123), or by clicking a button below:

Issue Title
#527 # Spike 664 — Third-party webhook delivery with signed event subscriptions Link to this issue
#530 # Spike 667 — Error-code catalog with remediation docs across contract, RPC and Horizon Link to this issue
#529 # Spike 666 — TypeScript codegen from Soroban contract metadata with types plus clients Link to this issue
#528 # Spike 665 — Sandbox and test harness for integrators with mock ledger plus fixtures Link to this issue

ℹ️ Learn more about linking PRs to issues

@Darkvader-ship-it
Darkvader-ship-it merged commit 74308a0 into PHASE-STELLAR:main Oct 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants