Inbound Webhook Auth Chain
This map traces the trust boundary validation every inbound webhook must pass before any business logic runs. Read this when auditing security posture, debugging rejected webhooks, or adding a new public endpoint. Every layer shown here is mandatory — skipping any layer is a governance violation per the Webhook Endpoint Governance rules in CLAUDE.md.
Diagram
sequenceDiagram actor Internet as Internet (Provider) participant TF as Tailscale Funnel / CF Tunnel participant WAF as Cloudflare WAF participant IP as IP Filter participant SIG as Signature Verify participant DEDUP as Dedup Check participant AUDIT as Audit Log participant HANDLER as Webhook Handler participant DB as Supabase Internet->>TF: HTTPS POST /webhook/* TF->>WAF: forward (TLS terminated) WAF->>WAF: DocuSign IP allowlist check WAF-->>Internet: 403 if not in allowlist (DocuSign only) WAF->>IP: pass if allowed IP->>IP: provider IP range filter IP-->>Internet: 403 if range mismatch IP->>SIG: forward to handler port SIG->>SIG: HMAC (HubSpot/OpenPhone) OR query-param token (SalesMsg) SIG-->>Internet: 401 if sig invalid SIG->>DEDUP: valid signature DEDUP->>DB: SELECT processed_webhook_events WHERE event_id = ? DB-->>DEDUP: found / not found DEDUP-->>HANDLER: skip if already processed DEDUP->>AUDIT: non-blocking write to webhook_audit_log AUDIT->>HANDLER: proceed with business logic HANDLER->>DB: write results
How to read this
- Tailscale Funnel / Cloudflare Tunnel is the only public ingress — no direct VPS port is exposed. All inbound routes must be registered in
workspace/FUNNEL-REGISTRY.mdbefore they go live. - Cloudflare WAF applies the DocuSign IP allowlist (90-day conceptual expiry —
security-audit-funnel.jsflags staleness). Other providers skip this layer unless ranges exist. - Signature verification uses HMAC for HubSpot and OpenPhone; SalesMsg uses a query-param token (
?secret=...) — NOT HMAC. This distinction is load-bearing — do not conflate them. - Dedup checks
processed_webhook_eventsin Supabase; providers may replay webhooks on failure. Duplicate processing is prevented here, not at the handler. webhook_audit_logwrite is non-blocking — it must not delay handler execution. A local file fallback exists for audit writes when Supabase is unreachable.
Related
- ports-topology — maps :18790, :18792, :18793, :18797 (all webhook handler ports)
- governance-gates-network — shows the governance gates that enforce this chain (weekly security audit)
- deal-lifecycle-map — shows what happens after auth succeeds for inbound deal webhooks
- cost-flow — LLM dispatch that follows successful webhook auth
See also
- CLAUDE.md — MANDATORY: Webhook Endpoint Governance section (all 7 rules)
- FUNNEL-REGISTRY.md — canonical registry of all approved public endpoints
- WEBHOOK-IP-RANGES.md — provider IP ranges for DocuSign WAF rule
- security-audit-funnel.js — weekly governance audit (Monday 06:00 PT)