PR-4 commit 3 — closure-bookkeeping nu de implementation-PRs en de twee runbooks gemerged zijn. - RFC-WS-7-OBSERVABILITY.md: nieuwe §9 Implementation status (mei 2026) vat samen welke acceptance criteria via PR-1..PR-4 zijn voldaan en welke (1, 2, 7, 9, 10) op Bert's deploy-checklist resteren. Pointer naar ARCH-OBSERVABILITY.md als levende reference; de RFC blijft historisch document. - SECURITY_AUDIT.md: nieuwe sectie 'WS-7 Observability — finale audit (mei 2026)' tussen A13-10 en Positive Findings. Bevat (1) acceptance criteria checklist met status per criterium, (2) processing register entry voor GlitchTip (controller-not-processor, retention 90 dagen, TLS+full-disk-encryption+2FA), (3) zeven security controls die WS-7 introduceert (PII scrubbing, CSP whitelist, sourcemap upload-only, listener registration discipline, runtime portal-context-split, multi-tenant tag invariant, impersonation.active binary signal), (4) pointer naar runbooks/observability-erasure.md voor Art. 17. - BACKLOG.md: status-overzicht-tabel boven de OBS-entries. Toegevoegd als entry: OBS-2 (early-pipeline log context, ✅ Resolved), OBS-3 (sentry-context middleware coverage, ✅ Resolved — opgevouwen in AuthScopeContextListener), OBS-5 (Crewli render handlers report() invariant, ✅ Resolved via48f2a00+ ExceptionReportingTest), en OBS-9 (Active — staging environment GlitchTip CSP whitelist follow-up bij staging-introductie). Bestaande OBS-1, 4, 6, 7 ongewijzigd (Active); OBS-8 staat al op Resolved sindsdee1401. - .claude-sync.conf: drie nieuwe doc-paths toegevoegd (ARCH-OBSERVABILITY.md, runbooks/observability-triage.md, runbooks/observability-erasure.md). Post-commit sync-claude-docs hook regenereert SYNC_MANIFEST.md met deze entries. Closes WS-7 documentation acceptance criteria 8 (ARCH) en 14 (SECURITY_AUDIT). Resterende criteria (1, 2, 7, 9, 10) zijn deploy-checklist door Bert. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
281 lines
19 KiB
Markdown
281 lines
19 KiB
Markdown
# 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` — Laravel
|
|
- `crewli-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-binding gebeurt op twee plekken: route-scope tags via `BindSentryRouteContext` middleware (op de api-group), auth-scope tags via `AuthScopeContextListener` op `Illuminate\Auth\Events\Authenticated`. De split volgt de data-bron: route-context is alleen tijdens HTTP-handling beschikbaar, auth-context wordt door elke authenticator (Sanctum, portal-token) ge-emit via het Authenticated event.
|
|
|
|
| Tag | API | apps/app | Bron / locatie |
|
|
|---|---|---|---|
|
|
| `release` | altijd | altijd | env, sentry-laravel built-in |
|
|
| `environment` | altijd | altijd | env, sentry-laravel built-in |
|
|
| `app` | `api` | `app` | route-middleware |
|
|
| `route_name` | altijd | altijd | route-middleware |
|
|
| `http.method` | altijd | n.v.t. | route-middleware |
|
|
| `actor_scope` | `organisation`/`platform`/`user`/`anonymous` | mirror | auth-listener (zie hieronder) |
|
|
| `organisation_id` (ULID) | aanwezig wanneer `actor_scope = organisation` | uit auth store | auth-listener |
|
|
| `event_id` (ULID) | wanneer event-scoped | wanneer applicabel | auth-listener (via {event} route-param) |
|
|
| `user_id` (ULID) | wanneer authenticated | uit auth store, alleen session-mode | auth-listener |
|
|
| `actor_type` | `organizer_admin` / `super_admin` / `portal_token` / `org_member` / `unauthenticated` | mirror | auth-listener |
|
|
| `impersonation.active` | bool | n.v.t. | HandleImpersonation middleware (post-swap) |
|
|
| `impersonation.impersonator_user_id` | wanneer actief | n.v.t. | HandleImpersonation middleware |
|
|
| `impersonation.session_id` | wanneer actief | n.v.t. | HandleImpersonation middleware |
|
|
| `queue.attempt` | binnen job-context | n.v.t. | TagJobAttemptOnSentry listener |
|
|
|
|
**Nooit als tag:** email, telefoon, naam, IP-adres, raw form_value content, raw cookie content.
|
|
|
|
**Multi-tenant invariant (verfijnd na PR-2 live smoke test):**
|
|
|
|
`actor_scope` is altijd aanwezig op authenticated events. Wanneer `actor_scope = organisation`, MOET `organisation_id` aanwezig en valide ULID zijn. Wanneer `actor_scope = platform`, IS `organisation_id` afwezig — dat is correct gedrag voor super_admin platform-routes (geforceerde org-attribution zou misleidend zijn). Wanneer `actor_scope = user` (default authenticated zonder org-route-context), is `organisation_id` ook afwezig: Crewli's User↔Organisation is many-to-many, een single-org "current org" bestaat niet op user-niveau, en attribution aan een willekeurige org zou misleiden. Een unit-test in `AuthScopeContextListenerTest::test_organisation_id_present_when_actor_scope_is_organisation` verifieert deze invariant.
|
|
|
|
**`actor_scope`-waarden:**
|
|
|
|
| Waarde | Wanneer | Filtering use-case in GlitchTip |
|
|
|---|---|---|
|
|
| `organisation` | route met {organisation} of {event} param, of portal-token request | "Issues voor organisatie X" |
|
|
| `platform` | super_admin op `admin.*` named routes | "Platform-bugs (niet org-specifiek)" |
|
|
| `user` | authenticated user op routes zonder org-scope (`/me/*`, `/portal/my-shifts`, `/uploads/*` etc.) | "Issues op user-routes; geen org-attribution" |
|
|
| `anonymous` | unauthenticated requests | "Public-route issues" |
|
|
|
|
**Wijziging t.o.v. originele RFC:** de oorspronkelijke formulering "élke captured event uit een geauthenticeerde controller MOET `organisation_id` hebben" is verfijnd na bevinding dat super_admin platform-routes geen zinvolle org-context hebben en Crewli's many-to-many user-org model geen reliable single-org hint biedt. Het invariant is nu sterker: niet "altijd aanwezig" maar "altijd correct gerelateerd aan `actor_scope`."
|
|
|
|
### 3.7 PII scrubbing
|
|
|
|
**Backend (`before_send` in sentry-laravel):**
|
|
1. 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`.
|
|
2. Headers: `Authorization`, `Cookie`, `Set-Cookie`, `X-API-Key`, `X-Impersonation-Token`.
|
|
3. Form submissions: élke payload met `form_values` → recursive value-replace met `[scrubbed]`. Form values zijn definitionally PII.
|
|
4. URL query string: scrub `?token=...`, `?api_key=...`.
|
|
5. `send_default_pii = false` globaal (strips locals uit stack traces).
|
|
|
|
**Frontend (`beforeSend` in @sentry/vue):**
|
|
1. Cookies via `document.cookie` exposure: scrub.
|
|
2. localStorage / sessionStorage: nooit in event context (portal-state in sessionStorage MAG NIET lekken).
|
|
3. **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.
|
|
4. URL query string: zelfde patronen.
|
|
5. Console messages: `Sentry.consoleLoggingIntegration` uitgeschakeld 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:
|
|
|
|
```php
|
|
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.
|
|
|
|
**Status (mei 2026, na PR-2):** Bij implementatie bleek dat de Spatie activitylog default-migratie via `nullableMorphs('subject')` en `nullableMorphs('causer')` deze composite indexes al aanmaakt (`subject` op `(subject_type, subject_id)`, `causer` op `(causer_type, causer_id)`). Geen aparte migratie nodig — geverifieerd via `information_schema.STATISTICS`. Acceptance criterium 12 daarmee al voldaan vóór WS-7 begon. Regression-guard: `tests/Feature/Database/ActivityLogIndexesTest.php` faalt wanneer een toekomstige refactor deze indexes verwijdert.
|
|
|
|
---
|
|
|
|
## 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: `BindRequestLogContext` middleware in PR-2, geen call-site-wijzigingen.
|
|
- Mock transport voor scrubbing-tests (geen externe afhankelijkheid in CI).
|
|
- Source-map upload-token in deploy `.env` op productie-host (later naar 1Password wanneer CI komt).
|
|
|
|
---
|
|
|
|
## 6. Acceptance criteria
|
|
|
|
WS-7 is compleet wanneer:
|
|
|
|
1. GlitchTip draait op `monitoring.hausdesign.nl` met TLS, alleen toegankelijk voor Bert (2FA aan).
|
|
2. Twee projecten aangemaakt; DSNs in vault.
|
|
3. Laravel SDK geïntegreerd; errors uit prod-API verschijnen <60s.
|
|
4. ✅ apps/app SDK geïntegreerd (PR-3); errors verschijnen met org/user/release context. Token-based portal-routes (`/portal/advance/:token`, `/register/:public_token`) hebben strictere scrubbing en geen user-context. Detectie via `route.meta.public === true && route.meta.context === 'portal'`. Implementatie: `apps/app/src/observability/contextBinding.ts`.
|
|
5. ✅ Source-maps upload werkt (PR-3); `deploy.sh` exporteert `VITE_SENTRY_RELEASE` build-time, uploadt via `@sentry/cli` na `vite build`, en verwijdert daarna élke `*.map` uit `dist/` (RFC §3.5: no public-mapped sources). Soft-fail: deploy gaat door als upload faalt, maar de map-strip stap loopt altijd.
|
|
6. ✅ PII scrubbing-tests groen (PR-2 backend `PiiScrubbingTest` 20 cases; PR-3 frontend `scrubber.spec.ts` 18 cases mirror). Plus structurele coverage in `EventListenerRegistrationTest` + `AuthScopeContextListenerTest` + `AuthScopeBindingHttpFlowTest`.
|
|
7. Smoke test: induced 500 in staging, verifieer dat hij verschijnt met alle verwachte tags én geen PII lekt.
|
|
8. ARCH-OBSERVABILITY.md geschreven (WS-8b).
|
|
9. Email-alerting geconfigureerd; getest met sample issue.
|
|
10. Retention-policy (90 dagen) toegepast.
|
|
11. Daily postgres-backup-script in place.
|
|
12. ~~Activity_log indexes (addendum D-06) gemigreerd.~~ ✓ — al voldaan door Spatie's `nullableMorphs` default in de originele activitylog migratie; zie §3.14 status-note. Regression-guard: `tests/Feature/Database/ActivityLogIndexesTest.php`.
|
|
13. Structured logging conventie geïmplementeerd; `X-Request-Id` round-trip getest.
|
|
14. SECURITY_AUDIT.md bijgewerkt.
|
|
|
|
**Voortgang (mei 2026, na PR-3):**
|
|
|
|
- **PR-1 ✅**: criteria 1, 2, 11 — infra + projecten + backup-script.
|
|
- **PR-2 ✅**: criteria 3, 6 (backend), 12, 13 — sentry-laravel + scrubber + structured logging + listener-registration discipline (OBS-8).
|
|
- **PR-3 ✅**: criteria 4, 5, 6 (frontend) — `@sentry/vue` SDK + scrubber + Vue Router context-binding + sourcemap upload in `deploy.sh`.
|
|
- **Resterend voor WS-7-closure (PR-4)**: criteria 7 (live smoke staging), 8 (ARCH-OBSERVABILITY.md), 9 (email-alerting), 10 (retention 90d), 14 (SECURITY_AUDIT.md update).
|
|
|
|
---
|
|
|
|
## 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
|
|
|
|
---
|
|
|
|
## 9. Implementation status (mei 2026)
|
|
|
|
WS-7 implementation is voltooid. Vier PRs gemerged in `feat/ws-7-observability`:
|
|
|
|
- **PR-1** (Infra): GlitchTip Docker stack, lokale + productie compose, daily-backup script, [`GLITCHTIP.md`](./GLITCHTIP.md) runbook.
|
|
- **PR-2** (Backend SDK): sentry-laravel + scrubber + structured logging + `BindSentryRouteContext` + `AuthScopeContextListener` + tenant resolution + impersonation discipline + listener registration discipline + `ExceptionReportingTest` + `ActivityLogIndexesTest`.
|
|
- **PR-3** (Frontend SDK): `@sentry/vue` + scrubber + Vue Router context-binding + sourcemap upload + CSP `connect-src` whitelist.
|
|
- **PR-4** (Docs + WS-8b): [`ARCH-OBSERVABILITY.md`](./ARCH-OBSERVABILITY.md) + observability runbooks + [`SECURITY_AUDIT.md`](./SECURITY_AUDIT.md) update + [`BACKLOG.md`](./BACKLOG.md) cleanup.
|
|
|
|
**Code-implementation acceptance criteria voldaan:** 3, 4, 5, 6, 11, 12, 13.
|
|
|
|
**Documentatie acceptance criteria voldaan:** 8, 14.
|
|
|
|
**Resterende criteria — handmatige deploy-stappen door Bert:**
|
|
|
|
- 1: GlitchTip op `monitoring.hausdesign.nl` met TLS + 2FA
|
|
- 2: Twee projecten + DSNs in 1Password vault
|
|
- 7: Smoke test induced 500 in staging-omgeving
|
|
- 9: Email-alerting geconfigureerd + getest
|
|
- 10: Retention-policy 90 dagen toegepast in GlitchTip admin
|
|
|
|
Deze stappen zijn deel van WS-7 closure-checklist (door Bert handmatig uit te voeren), niet van toekomstige PRs.
|
|
|
|
**Volledige tag-taxonomie en implementation-details:** zie [`ARCH-OBSERVABILITY.md`](./ARCH-OBSERVABILITY.md) (post-implementation reference). Deze RFC blijft historisch document; ARCH is de levende referentie.
|
|
|
|
**Operationele procedures:** zie [`runbooks/observability-triage.md`](./runbooks/observability-triage.md) (triage incoming issues) en [`runbooks/observability-erasure.md`](./runbooks/observability-erasure.md) (GDPR Art. 17 procedure).
|