Error monitoring for the Sea Saba website, reporting to the sea-saba-web
Sentry project. This document is the operational reference: what is captured,
when it is active, how releases and source maps work, how privacy is
enforced, and how to verify the integration in production.
- Uncaught browser errors (React render errors, event handlers, async
failures) via the browser SDK in
instrumentation-client.ts. - Uncaught server/Node.js errors — Server Components, route handlers,
server actions — via
sentry.server.config.tsplus the framework hookonRequestErrorininstrumentation.ts. - React errors that escape the locale root layouts via
app/global-error.tsx. - Readable stack traces: production builds upload browser and server
source maps under the release the deploy is tagged with (#130), so events
resolve to original repository source instead of
_next/static/_next/serverchunks.
Explicitly not enabled: performance tracing, Session Replay, profiling,
user feedback, logs/metrics. Those are not omitted by accident — the init
files are pinned by unit tests (tests/unit/sentry.test.ts).
One centralized decision lives in lib/sentry.ts:
isSentryActive() ===
sentryDeploymentEnv() === "production" && NEXT_PUBLIC_SENTRY_DSN is set
sentryDeploymentEnv()readsNEXT_PUBLIC_VERCEL_ENV(browser-inlined by Vercel's system env-var auto-exposure) withVERCEL_ENVas the server-side fallback.NODE_ENVis deliberately not part of the decision: Vercel Preview deployments run production-mode Next.js builds, soNODE_ENV=productioncannot distinguish Preview from Production.- Both SDKs always call
Sentry.init(...)but passenabled: isSentryActive(). Outside a real Vercel Production deployment the client exists but drops every event — Preview, local dev, unit/E2E/CI cannot emit Sentry traffic. - A missing/blank DSN fails closed the same way.
If Vercel's "Automatically expose System Environment Variables" option were
ever disabled, NEXT_PUBLIC_VERCEL_ENV would stop reaching the browser and
browser capture would silently deactivate (fail closed). The server side
uses VERCEL_ENV, which Vercel always sets, and would keep working.
| Variable | Scope | Secret? | Purpose |
|---|---|---|---|
NEXT_PUBLIC_SENTRY_DSN |
Vercel Production only | No — a DSN only permits event ingestion | Browser + server event destination. Set only in the Production env so Preview inherits nothing. |
SENTRY_AUTH_TOKEN |
Vercel Production only | Yes — build-time secret | Authenticates release creation + source-map upload during next build. Never NEXT_PUBLIC_*; never needed locally. Use an internal-integration or org auth token with org:read + project:releases scopes. |
SENTRY_ORG |
Vercel Production only | No — identifier | Org slug for the build plugin. Not needed if the token is org-scoped (sntrys_…). |
SENTRY_PROJECT |
not required | No | The project slug is pinned in code — project: "sea-saba-web" in next.config.ts — so no env var is needed. |
VERCEL_ENV / NEXT_PUBLIC_VERCEL_ENV |
platform-supplied | No | Deployment context; no manual setup on Vercel. |
VERCEL_GIT_COMMIT_SHA |
platform-supplied | No | The release identity (below); no manual setup. |
All four SENTRY_*/NEXT_PUBLIC_SENTRY_* variables belong to the Vercel
Production environment only — do not set them on Preview, or PR builds
would create releases and upload maps.
Releases use the Vercel/Git commit SHA, auto-detected by the SDK — there
is intentionally no explicit release.name config:
- During
next build,withSentryConfigresolves the release name fromSENTRY_RELEASE→ CI commit env vars (on Vercel:VERCEL_GIT_COMMIT_SHA) →git rev-parse HEAD, then injects it intonextConfig.envas_sentryRelease, which Next inlines into both the client and server bundles. - Both SDK inits read
process.env._sentryRelease→ events carry the SHA. - The post-compile upload step creates the Sentry release and uploads
source maps under that same name, and on Vercel also associates commits
(
setCommits) and records avercel-productiondeploy.
So event.release == uploaded source-map release == deployed Vercel commit
== Git SHA. Verified on #129's deploys (d26e83a…, 08da3607…). Do not add
human version numbers.
- Mechanism:
@sentry/nextjs@10.74.0+ Turbopack uses Next'scompiler.runAfterProductionCompilehook (Next ≥ 15.4.1). After the build the SDK creates the release, relies on Turbopack native debug IDs (turbopack.debugIds: true, auto-set on Next ≥ 16), then uploads.next/server/**+.next/static/chunks/**(+static/immutable/chunks) viasentry-cli's debug-id artifact-bundle protocol. - Coverage: browser maps are generated via
productionBrowserSourceMaps(auto-set by the SDK for Turbopack); server maps are emitted by Turbopack production builds by default. Both upload under the release above. - Production-only:
lib/sentry-build.tsenables upload + release creation only whenVERCEL_ENV === "production"during the build. Preview/local/test/CI builds run the same code path fully disabled — no maps generated, no releases created, no token required. - Public exposure:
sourcemaps.deleteSourcemapsAfterUpload: truedeletes.next/static/**/*.mapafter upload and strips the//# sourceMappingURL=comments, so/_next/static/…/*.mapis never publicly fetchable and browsers don't even request it. Server maps stay inside.next/server/— never a public path — where they also serve Next's own runtime stack formatting. - Failure behavior:
errorHandlerinlib/sentry-build.tsrethrows — the plugin's default only logs upload errors, which would silently ship an unsymbolicated deploy. Ours fails the build instead. Additionally, a production build missingSENTRY_AUTH_TOKEN,SENTRY_ORG(non-sntrys_tokens), or a resolvable release SHA fails at config load before compiling. Tradeoff, deliberately accepted: a Sentry outage can delay a deploy until retry — it can never take the live site down, because a failed Vercel build leaves the previous deployment serving. - Turbopack patch: the
patch-packagepatch forasset-prefix.js(#162) modifies a client runtime file; Turbopack compiles the patched source and its source map reflects the patch. Nothing extra needed — a unit test asserts the patch is applied tonode_modulesafter install.
On VERCEL_ENV=production builds, assertSentryProductionBuildConfig
throws before compilation unless all of these hold:
SENTRY_AUTH_TOKENset (non-empty)SENTRY_ORGset, unless the token is org-scoped (sntrys_…)- a release identity is resolvable (
VERCEL_GIT_COMMIT_SHA,SENTRY_RELEASE, orgit rev-parse HEAD)
The site handles customer inquiries, booking hand-offs (Checkfront), WhatsApp links and Respond.io chat. Sentry must not become a customer-data collection system. Three layers enforce that:
- Init options (browser + server):
sendDefaultPii: falseand adataCollectionblock that disablesuserInfo,cookies, request and responsehttpHeaders, allhttpBodies, andurlQueryParams. - Centralized sanitizer —
sanitizeSentryEventinlib/sentry.tsruns asbeforeSendon both SDKs for every event regardless of source: it strips query strings and fragments from request URLs, breadcrumb URLs and stack-frame URLs, and deletesrequest.data(bodies),request.cookies,request.headers,request.query_stringandevent.user. - No first-party data attached: nothing in the app adds Sentry context, tags with customer values, or form state.
Source maps do not change this posture: they reveal code structure (already shipped publicly in minified form), never runtime data. Auth token and upload traffic stay inside the build — nothing secret reaches the browser bundle.
connect-src gains exactly one origin: the https: origin parsed out of
NEXT_PUBLIC_SENTRY_DSN at config load (next.config.ts). No DSN → no
Sentry origin at all. No *.sentry.io wildcard is used anywhere. Unit tests
pin both behaviors.
- Sentry → Settings → Auth Tokens (or the internal integration) → create a
replacement token with
org:read+project:releases. - Vercel → project → Settings → Environment Variables → update
SENTRY_AUTH_TOKEN(Production scope only). - Trigger a production redeploy; confirm the build log shows the release creation + "Successfully uploaded source maps to Sentry".
- Revoke the old token in Sentry.
- Failed upload / invalid token: the production build fails — the live
site keeps the previous deployment. Check the Vercel build log for the
[sentry] release/source-map upload failedline; fix the token/org and redeploy. Transient Sentry outage → just redeploy once it recovers. - Missing release: if events show a SHA with no matching release in
Sentry, the build's upload step didn't run for that commit (check the
build log). Redeploy that commit, or upload manually with
sentry-cli releases …using the same SHA. - Unsymbolicated event: confirm
event.releasematches a Sentry release whose artifacts were uploaded (Releases → SHA → Source Maps). If the release exists but frames stay minified, checkjs_no_sourcevs missing debug IDs — the build log shows which artifacts were uploaded. - Source map accidentally public: fetch
https://www.seasaba.com/_next/static/chunks/<name>.js.map— must 404. If a map is reachable, verifydeleteSourcemapsAfterUploadwasn't overridden and the deploy was a production build (Preview/local don't generate maps at all).
Initial production verification ran against the first production deploy
carrying #129 via the temporary /sentry-check page (removed once
verification succeeded — it was never a permanent surface):
- Browser capture verified: controlled exception tagged
source: sentry-check,surface: browserarrived insea-saba-webwithenvironment: production. - Server capture verified: controlled exception tagged
source: sentry-check,surface: serverarrived separately (platform: node, Node 24, Vercel server environment). - Production-only operation verified: no events from Preview or other environments.
- Sanitization manually inspected on both events: no unexpected customer PII, no query strings/cookies/request bodies/user fields.
- Tracing remained unsampled; both stacks were intentionally
unsymbolicated until #130 — Sentry reported
js_no_sourcefor the_next/static/...browser chunk and_next/server/...server chunk.
After the first production deploy carrying this change:
- Build log: Vercel production build log shows release creation and
"Successfully uploaded source maps to Sentry" under the deploy SHA; no
[sentry] release/source-map upload failed. - Release: Sentry → Releases shows the deploy SHA; it has uploaded
source-map artifacts (debug-id bundles) covering both
_next/static/chunks/…and_next/server/…. - Symbolication: any new production event (or a recurrence of the
#161
TypeError: WeakMap keys must be objects or non-registered symbols) must show originalapp//components//lib/source frames for both browser and server stacks — that is what makes the WeakMap error attributable to Sentry internals, Next/Turbopack, Web Vitals, app code, or a third-party library. - No public maps:
curl -I https://www.seasaba.com/_next/static/chunks/<chunk>.js.map→ 404 for every emitted chunk; the deployed.jsfiles contain nosourceMappingURLcomment. - No secret leakage: grep the deployed client chunks for the token —
SENTRY_AUTH_TOKENmust appear nowhere in/_next/static/output. - Preview stays dark: a PR preview build creates no release, uploads nothing, and emits no events.
If an existing real error (e.g. the WeakMap recurrence) provides coverage of
steps 1–3, no temporary diagnostic route is needed. Only if production stays
clean should a minimal throw-on-demand check be added temporarily and removed
immediately after — same pattern as #129's /sentry-check.
@sentry/nextjs@10.74.0 pulls in @sentry/cli@2.58.6 (plus eight optional
platform-binary packages, same version) as a regular transitive dependency
of @sentry/bundler-plugin-core / @sentry/bundler-plugins, which power
withSentryConfig. As of #130 it is invoked at build time for its intended
purpose — release creation and source-map upload inside production next build runs only.
License: FSL-1.1-MIT (Functional Source License v1.1, MIT future license),
per the LICENSE shipped in the package and getsentry/sentry-cli. It grants
use, copy, modification and redistribution for any purpose other than a
"Competing Use" (offering the software itself, or substantially similar
functionality, as a commercial product/service), and converts to plain MIT two
years after each release. Sentry explains the model at
https://open.sentry.io/licensing/ and in its "Introducing the Functional
Source License" blog post.
Our use — Sentry's own build tooling inside the vendor-supported SDK
integration, on a commercial marketing site — is a Permitted Purpose: Sea Saba
does not sell, redistribute, or host the CLI or anything like it. Trivy flags
FSL-1.1-MIT only because its license database does not classify it; the
license was manually reviewed and accepted here rather than suppressed in
scanner config. The #130 activation does not change the usage class — no
re-review needed unless @sentry/cli changes version or licensing upstream.
- #131 — alert policies, triage runbook, ownership/escalation tuning.