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.md before they go live.
  • Cloudflare WAF applies the DocuSign IP allowlist (90-day conceptual expiry — security-audit-funnel.js flags 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_events in Supabase; providers may replay webhooks on failure. Duplicate processing is prevented here, not at the handler.
  • webhook_audit_log write is non-blocking — it must not delay handler execution. A local file fallback exists for audit writes when Supabase is unreachable.
  • 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