Operational docs for the GlitchTip stack landed in the previous two commits. - dev-docs/GLITCHTIP.md: new runbook covering local dev, project provisioning + DSN-to-vault flow, production deploy on monitoring.hausdesign.nl (DNS, DirectAdmin Let's Encrypt, Apache reverse proxy with WS upgrade), backup install + restore drill, smoke tests, troubleshooting. - dev-docs/SETUP.md: services table now includes GlitchTip; new docker/glitchtip/.env subsection points at the runbook. - dev-docs/RFC-WS-7-OBSERVABILITY.md §3.1: amended to record that the same compose file drives local dev (Mailpit at bm_mailpit:1025), so prod and dev cannot drift. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
12 KiB
RFC-WS-7 — Observability foundation (GlitchTip)
Status: Approved — implementation-ready
Workstream: WS-7 (consolidatie-sprint mei 2026)
Voorgangers: ARCH-CONSOLIDATION-2026-04 §3 besluit 8, §6.7 — ARCH-CONSOLIDATION-ADDENDUM-2026-04-24 D-06
Opvolgers: ARCH-API-VALIDATION (geblokkeerd door WS-7), WS-8b (ARCH-OBSERVABILITY.md)
Doorlooptijd: 4-5 dagen (charter zei 2-3; revisie wegens PII-scrubbing scope)
1. Doel
Crewli draait in productie zonder geautomatiseerde error-detectie. Stack traces alleen via SSH+grep; frontend errors onzichtbaar tenzij gerapporteerd; geen release-correlatie of alerting. WS-7 levert die foundation.
2. Charter-amendementen
Twee afwijkingen van charter §3 besluit 8, beide bewust:
A. Sentry → GlitchTip. Self-hosted = data binnen perimeter (Crewli verwerkt special category data: dietary, medical, accreditation passport — geen externe processor toevoegen aan het processing register). GlitchTip implementeert het Sentry-event-protocol; sentry-laravel, @sentry/vue, @sentry/cli werken zonder modificatie. Switchen naar Sentry SaaS later is mogelijk zonder app-wijzigingen.
B. Performance monitoring uit scope. Charter zei "Sentry Performance in prod". Deze RFC scope = errors-only. Performance-tracing pakken we later op wanneer er een concrete vraag ontstaat.
3. Architectuur
3.1 Hosting
Self-hosted GlitchTip op productie VPS via Docker Compose (glitchtip-web, glitchtip-worker, glitchtip-postgres, glitchtip-redis). Reverse proxy via DirectAdmin Apache; SSL via DirectAdmin Let's Encrypt op monitoring.hausdesign.nl (consistent met bestaande subdomain-pattern).
Lokale ontwikkeling: dezelfde docker-compose.glitchtip.yml draait lokaal als make services (gecombineerd met de bestaande docker-compose.yml via -f). Web-UI op http://localhost:8200, e-mail naar Mailpit op bm_mailpit:1025. Dev-stack en prod-stack delen één compose-file zodat configuratie-drift uitgesloten is.
3.2 Twee projecten / DSNs
crewli-api— Laravelcrewli-app— apps/app SPA (single-SPA na WS-3)
Twee aparte projecten omdat issue-ownership (frontend vs backend) helder moet zijn én omdat scrubbing-rules per project verschillen.
Binnen crewli-app werken we met een runtime context-split via beforeSend-hook: routes onder /p/* (token-based, artist/supplier/press) krijgen strictere scrubbing en geen user-context. Organizer-routes en /platform/* krijgen volledige auth-context. Eén SDK, twee zones.
3.3 Environment gating
Lege DSN = SDK no-op (bevestigd voor zowel sentry-laravel als @sentry/vue). Geen runtime-check in app-code nodig.
# .env.development → DSNs leeg
# .env.staging → DSNs gevuld, GLITCHTIP_ENVIRONMENT=staging
# .env.production → DSNs gevuld, GLITCHTIP_ENVIRONMENT=production
3.4 Release identifier
Format <app>@<short-sha> (crewli-api@f41951a, crewli-app@f41951a). Bron: git rev-parse --short HEAD als build-time env var, geïnjecteerd in deploy.sh per app-build.
3.5 Source maps
vite build produceert sourcemaps → @sentry/cli sourcemaps upload push naar GlitchTip → .map bestanden verwijderd uit dist/ vóór nginx ze serveert. Geen public-mapped sources op productie. Per-project upload-only auth-token in deploy environment.
3.6 Context tagging
| Tag | API | apps/app |
|---|---|---|
release |
altijd | altijd |
environment |
altijd | altijd |
app |
api |
app |
route_name |
Route::currentRouteName() |
route.name |
http.method |
altijd | n.v.t. |
organisation_id (ULID) |
wanneer auth+scope gebound | uit auth store |
event_id (ULID) |
wanneer event-scoped | wanneer applicabel |
user_id (ULID) |
auth()?->id() |
uit auth store, alleen session-mode |
actor_type |
organizer_admin / super_admin / portal_token / volunteer / etc. |
mirror |
impersonation.active |
bool | n.v.t. |
impersonation.impersonator_user_id |
wanneer actief | n.v.t. |
queue.attempt |
binnen job-context | n.v.t. |
Nooit als tag: email, telefoon, naam, IP-adres, raw form_value content, raw cookie content.
Multi-tenant invariant: élke captured event uit een geauthenticeerde controller MOET organisation_id hebben. Een unit-test verifieert dit — als organisation_id ontbreekt op een geauthenticeerd path, faalt de test.
3.7 PII scrubbing
Backend (before_send in sentry-laravel):
- Request body keys (recursief, key-name match):
password,password_confirmation,current_password,token,api_key,secret,webhook_secret,dsn,signature,authorization,cookie,bearer,iban,bic,passport_number,bsn. - Headers:
Authorization,Cookie,Set-Cookie,X-API-Key,X-Impersonation-Token. - Form submissions: élke payload met
form_values→ recursive value-replace met[scrubbed]. Form values zijn definitionally PII. - URL query string: scrub
?token=...,?api_key=.... send_default_pii = falseglobaal (strips locals uit stack traces).
Frontend (beforeSend in @sentry/vue):
- Cookies via
document.cookieexposure: scrub. - localStorage / sessionStorage: nooit in event context (portal-state in sessionStorage MAG NIET lekken).
- Mask-all op input breadcrumbs. Sentry default maskt alleen passwords; wij masken alle
<input>values via breadcrumb-integratie config. Crewli heeft email/phone/IBAN/dietary/medical als plain<input>— selectief masken is niet veilig genoeg. - URL query string: zelfde patronen.
- Console messages:
Sentry.consoleLoggingIntegrationuitgeschakeld in productie.
Verificatie — verplicht in CI:
tests/Feature/Observability/PiiScrubbingTest.php— mock transport, assert dat sensitive keys gescrubd zijn.apps/app/src/__tests__/sentry-scrub.test.ts— assert form-input breadcrumb-scrubbing en/p/*route context-strip.
Tests blokkeren merge als scrubbing regresseert. Geen "manuele inspectie" als verificatie.
3.8 User context
Sentry::setUser([...]): id = ULID, username = ULID (duplicate, nooit email), ip_address = null. send_default_pii = false.
3.9 Sampling
Errors 100% (self-hosted, geen kostenreden). Performance/Profile niet van toepassing (uit scope per §2 amendement B). Wel een rate-limit op event-volume per project (GlitchTip default 10k/min/project) tegen runaway-errors.
3.10 Boundary met bestaande systemen
GlitchTip captureert programmer errors en infrastructure failures. Het captureert niet verwachte business-uitkomsten (failed payment, failed identity match, dead-letter webhook delivery — die hebben eigen audit-tabellen). Bestaande systemen blijven onaangeroerd: Telescope (dev), activity_log (audit trail), impersonation_audit_logs (security audit), form_webhook_deliveries (delivery audit), Laravel default log (operationeel).
Concreet voor webhooks: form_webhook_deliveries.dead_letter is de audit-record. GlitchTip vuurt alleen bij programmeerfouten (TypeError, missing config), niet wanneer receiver een 500 retourneert.
3.11 Queue-worker integratie
Laravel SDK auto-captureert vanuit queue jobs. Job-attempt-number als tag (queue.attempt) voor "issues die uiteindelijk slagen" vs "altijd falend". Stack-trace-grouping (default) is correct gedrag voor idempotente retries — niet per attempt fingerprinten.
3.12 Alerting
Initieel: email-naar-Bert op nieuwe issue / regression / spike (>10 events in 5min, tune na eerste week). Slack-integratie naar BACKLOG.
3.13 Structured logging conventie (charter §6.7)
BindRequestLogContext middleware op de api route group:
Log::withContext([
'request_id' => $request->header('X-Request-Id') ?? Str::ulid(),
'organisation_id' => $request->user()?->organisation_id,
'user_id' => $request->user()?->id,
'route' => $request->route()?->getName(),
]);
request_id retour als response header X-Request-Id. apps/app axios interceptor genereert client-side een ULID per request en stuurt mee als header; server respecteert headerwaarde. Dit geeft front-to-back correlation in één klik vanuit GlitchTip-UI naar log-segment op de host.
3.14 Activity_log indexes (addendum D-06)
(subject_type, subject_id) en (causer_type, causer_id) composite indexes op activity_log. Infrastructure-housekeeping; geen functionele wijziging.
4. Privacy / GDPR
Data na scrubbing: alleen ULIDs, stack traces, route-namen, gecureerde tags, request_id. Scrubbing per §3.7 is de primaire control; tests in §3.7 zijn merge-blockers.
Controller / processor: Self-hosted op Crewli-infra → Crewli is controller, niet processor. Geen DPA-uitbreiding.
Right to erasure (Art. 17): Zoek events op user.id = <ULID>, verwijder via GlitchTip-API of directe SQL op glitchtip-postgres. Initieel handmatig; geautomatiseerd erasure-script naar BACKLOG.
Retention: 90 dagen, daarna purged.
Processing register-entry: Crewli Error Tracking (GlitchTip) — defectdetectie en service-availability monitoring — pseudonieme identifiers + technische metadata — recipient: Bert — retention 90 dagen — TLS in transit, full-disk encryption at rest, SSH-key + 2FA op web-UI.
SECURITY_AUDIT.md update in PR-4: GlitchTip als processing entry, scrubbing-config als security control, erasure-procedure als GDPR-runbook.
5. Vastgelegde defaults
Geen open vragen meer; deze zijn vastgesteld:
- DirectAdmin Let's Encrypt voor
monitoring.hausdesign.nl(consistent met bestaande subdomains). - Retention: 90 dagen.
- Email-alerting initieel; Slack naar BACKLOG.
- Mask-all op frontend input breadcrumbs.
- 2FA verplicht op GlitchTip web-UI.
- Daily postgres-backup naar zelfde target als Crewli main-DB.
- Capture failures-to-write-activity-log (silent failures zijn slecht).
- Impersonation: zowel impersonator als target user_id getagd (per §3.6 tabel).
- Big-bang structured logging:
BindRequestLogContextmiddleware in PR-2, geen call-site-wijzigingen. - Mock transport voor scrubbing-tests (geen externe afhankelijkheid in CI).
- Source-map upload-token in deploy
.envop productie-host (later naar 1Password wanneer CI komt).
6. Acceptance criteria
WS-7 is compleet wanneer:
- GlitchTip draait op
monitoring.hausdesign.nlmet TLS, alleen toegankelijk voor Bert (2FA aan). - Twee projecten aangemaakt; DSNs in vault.
- Laravel SDK geïntegreerd; errors uit prod-API verschijnen <60s.
- apps/app SDK geïntegreerd; errors verschijnen met org/user/release context.
/p/*routes hebben strictere scrubbing en geen user-context. - Source-maps upload werkt; leesbare stack traces in UI;
.mapbestanden afwezig in publieke bundle. - PII scrubbing-tests groen (backend + frontend).
- Smoke test: induced 500 in staging, verifieer dat hij verschijnt met alle verwachte tags én geen PII lekt.
- ARCH-OBSERVABILITY.md geschreven (WS-8b).
- Email-alerting geconfigureerd; getest met sample issue.
- Retention-policy (90 dagen) toegepast.
- Daily postgres-backup-script in place.
- Activity_log indexes (addendum D-06) gemigreerd.
- Structured logging conventie geïmplementeerd;
X-Request-Idround-trip getest. - SECURITY_AUDIT.md bijgewerkt.
7. Deliverables (4 PRs, --no-ff per CLAUDE.md)
| PR | Inhoud |
|---|---|
| PR-1: Infra | docker-compose.glitchtip.yml, monitoring.hausdesign.nl DNS + TLS, twee projecten aangemaakt, DSNs in vault, daily-backup script |
| PR-2: Backend SDK + structured logging | sentry-laravel install + config + scrubbing + context tagging, BindRequestLogContext middleware, X-Request-Id round-trip, PII scrubbing test, activity_log indexes (D-06) |
| PR-3: Frontend SDK | @sentry/vue install + config + context tagging + scrubbing test + /p/* runtime-split + sourcemap upload-step in deploy.sh |
| PR-4: Docs + WS-8b | ARCH-OBSERVABILITY.md, runbook (triage + erasure + scrubbing-tuning), SECURITY_AUDIT.md update |
WS-7 closure = alle 4 PRs gemerged + acceptance criteria 1-14 afgevinkt.
8. Verwijzingen
- ARCH-CONSOLIDATION-2026-04 §3 besluit 8 — originele Sentry-keuze (deze RFC wijzigt naar GlitchTip).
- ARCH-CONSOLIDATION-2026-04 §6.7 — originele scope WS-7.
- ARCH-CONSOLIDATION-ADDENDUM-2026-04-24 D-06 — activity_log indexes.
- AUTH_ARCHITECTURE.md — per-app cookie naming, impersonation flow.
- SECURITY_AUDIT.md — bestaande audit-posture (te updaten in PR-4).
- BACKLOG.md — entries voor automated-erasure script, Slack alerting (post-WS-7).
- GlitchTip docs: https://glitchtip.com/documentation
- GlitchTip self-hosting: https://glitchtip.com/documentation/install