Some checks failed
CI / Unit & Component Tests (pull_request) Successful in 3m4s
CI / OCR Service Tests (pull_request) Successful in 18s
CI / Backend Unit Tests (pull_request) Successful in 2m39s
CI / fail2ban Regex (pull_request) Successful in 40s
CI / Compose Bucket Idempotency (pull_request) Successful in 59s
CI / Unit & Component Tests (push) Successful in 5m46s
CI / OCR Service Tests (push) Successful in 45s
CI / Backend Unit Tests (push) Failing after 10m32s
CI / fail2ban Regex (push) Successful in 3m7s
CI / Compose Bucket Idempotency (push) Successful in 2m26s
Documents the decision to use the Sentry SDK with self-hosted GlitchTip, sendDefaultPii:false rationale, errorId surfacing to users, and alternatives considered (Sentry SaaS rejected for data-minimisation reasons). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
87 lines
3.9 KiB
Markdown
87 lines
3.9 KiB
Markdown
# ADR-018: GlitchTip frontend error tracking via @sentry/sveltekit
|
|
|
|
**Date:** 2026-05-17
|
|
**Status:** Accepted
|
|
**Deciders:** Marcel Raddatz
|
|
|
|
---
|
|
|
|
## Context
|
|
|
|
The Familienarchiv had no client-side error reporting. When a user encountered a crash
|
|
or unhandled error in the SvelteKit frontend, there was no way for the operator to
|
|
observe it — errors were invisible until a user manually reported them. A GlitchTip
|
|
instance (self-hosted, Sentry-compatible) was already running as part of the
|
|
observability stack (`docker-compose.observability.yml`). The backend already reported
|
|
server-side errors to it.
|
|
|
|
We needed a way to:
|
|
1. Capture frontend errors automatically and route them to GlitchTip.
|
|
2. Give users a visible error identifier they can include in a support message.
|
|
3. Do this without leaking personally identifiable information (PII) from the family
|
|
archive — documents contain personal histories, names, and relationships.
|
|
|
|
---
|
|
|
|
## Decision
|
|
|
|
Use `@sentry/sveltekit` (the official Sentry SDK for SvelteKit) to:
|
|
|
|
- Initialise with `sendDefaultPii: false` on both `hooks.server.ts` and `hooks.client.ts`.
|
|
- Pass a callback to `Sentry.handleErrorWithSentry()` that returns
|
|
`{ message, errorId }` where `errorId` is `Sentry.lastEventId()` when Sentry
|
|
captured the event, or a fresh `crypto.randomUUID()` as fallback.
|
|
- Display the `errorId` on the `+error.svelte` page so users can include it in a
|
|
report to the operator.
|
|
|
|
The SDK is initialised with `enabled: !!import.meta.env.VITE_SENTRY_DSN` so that
|
|
development and CI builds without a DSN configured do not send any events.
|
|
|
|
`VITE_SENTRY_DSN` is a write-only ingest key — it can POST events to GlitchTip but
|
|
cannot read them. It is safe to include in the client bundle per the Sentry security
|
|
model; it does not require rotation like a password.
|
|
|
|
---
|
|
|
|
## Alternatives considered
|
|
|
|
**Sentry SaaS** — rejected. The archive contains private family documents and personal
|
|
history. Sending error events with stack traces to a US-hosted third party is
|
|
inconsistent with the project's data-minimisation posture. Self-hosted GlitchTip on
|
|
the same Hetzner VPS keeps all data on infrastructure the operator controls.
|
|
|
|
**Custom error logging endpoint** — rejected. The @sentry/sveltekit SDK handles
|
|
SvelteKit's hook lifecycle, source-map upload, and event grouping automatically.
|
|
Reimplementing this would cost significant engineering time for no benefit.
|
|
|
|
**Log-only (no user-visible errorId)** — rejected. Without a visible error ID, users
|
|
can only describe what happened in natural language, making it hard to correlate a
|
|
report with a specific GlitchTip event. The `errorId` closes this gap at negligible UI
|
|
cost.
|
|
|
|
---
|
|
|
|
## Consequences
|
|
|
|
**Positive:**
|
|
- Frontend errors are now observable without requiring user reports.
|
|
- Users can provide an `errorId` that maps directly to a GlitchTip event.
|
|
- `sendDefaultPii: false` ensures names, IPs, and cookie values are not included in
|
|
captured events.
|
|
- `tracesSampleRate: 0.1` limits trace volume to 10% of transactions, keeping
|
|
GlitchTip load low on the shared VPS.
|
|
|
|
**Negative / trade-offs:**
|
|
- The `@sentry/sveltekit` SDK is now a production dependency. SDK updates must be
|
|
reviewed for changes to the default PII scrubbing behaviour.
|
|
- The `handleError` callback in both hooks returns a hardcoded English message
|
|
(`'An unexpected error occurred'`). This bypasses Paraglide i18n — the error page
|
|
will always show English text when the hooks are active, regardless of the user's
|
|
locale. This is acceptable because: (a) the error page is a last-resort fallback
|
|
not part of normal UX, (b) the `errorId` is the actionable information, not the
|
|
message text. A future ADR may address this if internationalised error messages
|
|
become a requirement.
|
|
- `Sentry.lastEventId()` returns `undefined` when Sentry did not capture the event
|
|
(e.g. DSN not configured). The `crypto.randomUUID()` fallback guarantees an `errorId`
|
|
is always present, but that UUID will not appear in GlitchTip.
|