What changed, and why
Everwhen runs in production with no error or performance monitoring. When a turn fails, a checkout 500s, or a server route throws inside the AI seam, the only trace is the one-line [ai] / [mod] / [stripe] telemetry — nothing durable, nothing searchable. This PR wires Sentry into both halves of the app so those errors, and a sample of performance traces, get captured.
The change is deliberately small and self-contained. It adds @sentry/nuxt, two SDK init files (client and server), a shared constants file, and four small edits to existing config (nuxt.config.ts, Dockerfile, playwright.config.ts, .env.example). It changes no game logic — no store, no route, no mechanics. Read the eight files below and you can vouch for the whole thing.
Three design calls carry the weight, each with its own section: the DSN is a public identifier committed as a default; reporting is gated to production builds so local dev stays silent; and the server SDK loads via node --import for full instrumentation.
One source of truth: the DSN and sample rate
Both init files and nuxt.config need the same two values, so they're named once here. The whole file is 21 lines:
sentry.shared.ts · 22 lines
Client init: production-only, silent in dev
The client config runs before the Nuxt app mounts. It reads the DSN from public runtime config (so a deploy can override it at runtime without a rebuild) and inits the browser SDK — errors plus a sample of performance traces.
sentry.client.config.ts · 20 lines
Server init: read env before Nitro boots
The server config is the twin of the client one, with one difference that the comment calls out: it loads via node --import before the Nitro app starts, so useRuntimeConfig() doesn't exist yet — it can only read process.env. The DSN falls back to the committed default; the same production gate applies.
sentry.server.config.ts · 18 lines
Wiring it into Nuxt
Three edits to nuxt.config.ts. First, register the module so it picks up the two config files and instruments the build:
The Sentry module joins the existing four.
nuxt.config.ts · 137 lines
⋯ 22 lines hidden (lines 1–22)
⋯ 107 lines hidden (lines 31–137)
Second, expose the DSN and an environment tag to the client through public runtime config — the value the client init reads back:
Public runtime config: env-overridable, committed default.
nuxt.config.ts · 137 lines
⋯ 106 lines hidden (lines 1–106)
⋯ 20 lines hidden (lines 118–137)
Third, the module's own build-time options. Source-map upload is turned off:
Build-time module options.
nuxt.config.ts · 137 lines
⋯ 119 lines hidden (lines 1–119)
Full server instrumentation, the Sentry way
For the server SDK to instrument Nitro and the outbound AI / Supabase / Stripe calls, it has to load before the app's own modules. Sentry's recommended path for a container you control is Node's --import flag, so the Dockerfile's launch command changes:
The runtime CMD now preloads the emitted server config.
Dockerfile · 57 lines
⋯ 51 lines hidden (lines 1–51)
Never phone home from CI
The e2e suite runs the production build through nuxt preview, which would otherwise satisfy the production gate and start sending real events during CI. The Playwright web server blanks the DSN so it stays inert:
webServer.env merges over process.env; empty DSN disables Sentry.
playwright.config.ts · 67 lines
⋯ 52 lines hidden (lines 1–52)
The env knobs, and why v9 not v10
.env.example documents the two optional overrides, and reiterates that production works with no setup because the default DSN is committed: