Primary finding
Missing OPTIN_HMAC_SECRET causes unhandled 500 in /api/opt-in (no graceful error)
- apps/web/lib/optin-token.ts:19-25
- apps/web/lib/optin-token.ts:66-76
- apps/web/app/api/opt-in/route.ts:45-46
verifyTokenDetailed calls getSecret(), which throws if OPTIN_HMAC_SECRET is unset. handleOptIn calls verifyTokenDetailed without a try/catch, so the route will throw and return a 500 on requests instead of returning a controlled error response. This is a brittle production-footgun if the env var is missing in any environment.
Recommendation
Defensively handle missing secret at the route boundary. Wrap verifyTokenDetailed in a try/catch; if the thrown error message matches the missing-secret case, log a high-severity event (e.g., optin.misconfigured) and return htmlResponse(500, errorPage("Server misconfigured","Opt-in service is temporarily unavailable.")) without leaking stack traces. Optionally validate OPTIN_HMAC_SECRET presence at process start and fail-fast with a clear error.