Add SSL certificate management for custom domains (#2774) master
* fix(domains): surface and nudge pending SSL certificates The custom-domain status reported "Valid Configuration" as soon as Vercel verified the domain and DNS pointed at it, while Vercel itself could still sit on "Generating SSL" until someone hit Refresh in its dashboard. - getCertificateStatus probes the domain over TLS (only once Vercel says DNS points at us) and the dashboard shows a "Generating SSL Certificate" step until a trusted cert is served. - issueCertificate orders a cert via POST /v8/certs, guarded server-side so it is a no-op while DNS is misconfigured or a trusted cert already exists. The dashboard fires it once per visit while the domain is in that state. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YQ7XQzCpscUWYgFuvmEybu * refactor(domains): move certificate helpers to lib/vercel and mutation into useDomainStatus - fetchDomainConfig, getCertificateReadiness and domainConfigResponseSchema now live in lib/vercel.ts next to the other Vercel helpers. - useDomainStatus sets up and returns issueCertificateMutation, following the dashboard's `xxxMutation = useMutation(...)` convention. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YQ7XQzCpscUWYgFuvmEybu * fix(domains): pin probed IP, keep default TLS validation, surface Vercel errors - hasTrustedCertificate resolves the domain once, rejects private/internal addresses via assertSafeUrlSync, and connects to that pinned IP (SNI still uses the domain), closing the DNS-rebinding window after Vercel's check. - Drop rejectUnauthorized: false; a completed handshake under default validation already means the cert is trusted for the domain. - fetchDomainConfig throws a sanitized error on non-2xx instead of parsing Vercel's error body as `misconfigured`. - The dashboard no longer reads a failed config/certificate query as "Valid Configuration", and refetches certificate status after issuance. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YQ7XQzCpscUWYgFuvmEybu --------- Co-authored-by: Claude <noreply@anthropic.com>