feat: Vercel deployment webhook replaces steady-state status polling (TODO 5) - #118
feat: Vercel deployment webhook replaces steady-state status polling (TODO 5)#118lopugit wants to merge 1 commit into
Conversation
TODO item 5. Ready deployments were spending Vercel API calls on every footer poll just to detect a future build. Now Vercel pushes deployment lifecycle events and the status endpoint reads the persisted event instead. - POST /api/v1/vercel/webhook: verifies x-vercel-signature (HMAC-SHA1 of the raw body with VERCEL_WEBHOOK_SECRET, timing-safe compare) before any parsing or DB work; 404s entirely until the secret is configured; caps the body at 256 KiB; acks unknown event types with 200 so Vercel stops retrying them. Registered in the Nitro API dispatcher. - webhookStatus util persists the latest event per branch as a singleton doc in the existing `settings` collection (key vercelWebhookStatus:branch:<b>). Out-of-order deliveries are dropped via an eventAt guard (upsert filter + unique-key collision = stale branch). Non-terminal states go stale after 30 min so a lost 'ready' delivery can't pin the footer. - getVercelDeploymentStatus serves a terminal webhook state (ready / error / canceled) with zero Vercel API calls; a non-terminal state falls through to the API for live build progress; tokenless deployments serve the webhook state either way. Polling remains the fallback while no webhook event exists for the branch. Status gains source:'webhook' for observability. - remix/scripts/vercel/create-webhook.mjs: owner-run one-shot that registers the webhook via the Vercel API and prints the signing secret to set as VERCEL_WEBHOOK_SECRET. Deliberately NOT run automatically - creating a webhook is a standing account configuration change. - VERCEL_DEPLOYMENTS.md documents the flow and activation steps. Verified live (worktree dev server, real Mongo, 16 checks all passing): unsigned/bad-signature 401s, signed created/succeeded/error recorded and served with correct state/progress/urls, out-of-order event dropped, unknown type acked-not-recorded, GET 405, non-terminal fall-through to live polling with a real token present, footer fetch shows state ready / source webhook / progress 100 with zero console errors. Test docs cleaned from the dev db. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
The 10-session parallel todo batch (2026-07-21) produced overlapping PRs before the shared claims file (
Merge-order suggestion for the Filed by session 1. Claims registry: |
|
Closing in favor of #122 (session 1 → the duplicate resolution both coordination comments asked for). Honest comparison: the two implementations agree on everything load-bearing — HMAC-SHA1 raw-body verification with Two things worth cherry-picking from #118 into #122 (or a follow-up):
Verification evidence from this PR that transfers (same design): 16 live checks including unsigned/tampered 401s before any DB work, ready/error served with |
…t.promoted handling Cherry-picked from session 1's closed duplicate PR #118 (branch claude/todo5-vercel-webhooks-s1, credit where due): the owner-run scripts/vercel/create-webhook.mjs registers the webhook via POST /v1/webhooks and prints the signing secret exactly once; deployment.promoted now maps to ready so promotions recorded by that event set are tracked. Live-verified: promoted event -> ready; cross-run stale replay for a superseded deployment still ignored. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t.promoted handling Cherry-picked from session 1's closed duplicate PR #118 (branch claude/todo5-vercel-webhooks-s1, credit where due): the owner-run scripts/vercel/create-webhook.mjs registers the webhook via POST /v1/webhooks and prints the signing secret exactly once; deployment.promoted now maps to ready so promotions recorded by that event set are tracked. Live-verified: promoted event -> ready; cross-run stale replay for a superseded deployment still ignored. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t.promoted handling Cherry-picked from session 1's closed duplicate PR #118 (branch claude/todo5-vercel-webhooks-s1, credit where due): the owner-run scripts/vercel/create-webhook.mjs registers the webhook via POST /v1/webhooks and prints the signing secret exactly once; deployment.promoted now maps to ready so promotions recorded by that event set are tracked. Live-verified: promoted event -> ready; cross-run stale replay for a superseded deployment still ignored. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t.promoted handling Cherry-picked from session 1's closed duplicate PR #118 (branch claude/todo5-vercel-webhooks-s1, credit where due): the owner-run scripts/vercel/create-webhook.mjs registers the webhook via POST /v1/webhooks and prints the signing secret exactly once; deployment.promoted now maps to ready so promotions recorded by that event set are tracked. Live-verified: promoted event -> ready; cross-run stale replay for a superseded deployment still ignored. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t.promoted handling Cherry-picked from session 1's closed duplicate PR #118 (branch claude/todo5-vercel-webhooks-s1, credit where due): the owner-run scripts/vercel/create-webhook.mjs registers the webhook via POST /v1/webhooks and prints the signing secret exactly once; deployment.promoted now maps to ready so promotions recorded by that event set are tracked. Live-verified: promoted event -> ready; cross-run stale replay for a superseded deployment still ignored. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Implements TODO 5 — Replace Vercel status polling with Vercel webhooks.
What changes
Ready deployments were spending Vercel API calls on every footer poll just to detect a future build. Now:
POST /api/v1/vercel/webhookreceives deployment lifecycle events (created/succeeded/promoted/error/canceled). The raw body is verified againstVERCEL_WEBHOOK_SECRET(x-vercel-signature, HMAC-SHA1, timing-safe compare) before any parsing or DB work; the endpoint 404s entirely until the secret is configured; body capped at 256 KiB; unknown event types are acked with 200 so Vercel stops retrying them.webhookStatus.ts): latest event per branch stored as a singleton doc in the existingsettingscollection (vercelWebhookStatus:branch:<branch>) — no new collection. Out-of-order deliveries are dropped via aneventAtfilter whose upsert insert collides with the uniquekeyindex (that duplicate-key error is the stale branch). Non-terminal states go stale after 30 min so a lostreadydelivery can't pin the footer forever.getVercelDeploymentStatusserves a terminal webhook state (ready/error/canceled) with zero Vercel API calls; a non-terminal state falls through to the existing API polling for live build-progress checks; tokenless deployments serve the webhook state either way. Polling remains the automatic fallback while no webhook event exists for the branch (i.e. before activation). Status responses gainsource: 'webhook'for observability. The footer needs no changes — it reads the same endpoint.Activation (deliberately left for the owner)
Creating a webhook is a standing Vercel account configuration change, so nothing registers it automatically. One-shot script:
then set the printed
VERCEL_WEBHOOK_SECRETin the Vercel project env and redeploy (documented inVERCEL_DEPLOYMENTS.md). Until then, behavior is byte-identical to today's polling.Verification (live, worktree dev server, real Mongo — 16 checks, all pass)
created→ recordedqueued; signedsucceeded→readyserved withsource: webhook,buildProgress: 100, deployment + inspector URLs surfacederror→state: error,hasError: truerecorded: false), status unchanged — verified both within a run and across runsGET→ 405VERCEL_API_TOKENpresent → correct fall-through to live polling (caught a real in-flight build from the parallel sessions)state: ready / source: webhook / progress: 100, zero console errorsSession 1 of the parallel todo batch — claimed in
TODO/SESSION-CLAIMS.md.🤖 Generated with Claude Code