From e349089b02d3877b66ddc7c4c661458b0c73e40d Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Wed, 20 May 2026 12:14:39 +0000 Subject: [PATCH] docs(compliance): add anchored policy templates (incident-runbook, dsr-procedure) Co-Authored-By: Claude Sonnet 4.6 --- .../templates/dsr-procedure.template.md | 312 ++++++++++++++++++ .../templates/incident-runbook.template.md | 264 +++++++++++++++ 2 files changed, 576 insertions(+) create mode 100644 docs/compliance/templates/dsr-procedure.template.md create mode 100644 docs/compliance/templates/incident-runbook.template.md diff --git a/docs/compliance/templates/dsr-procedure.template.md b/docs/compliance/templates/dsr-procedure.template.md new file mode 100644 index 0000000..91604fb --- /dev/null +++ b/docs/compliance/templates/dsr-procedure.template.md @@ -0,0 +1,312 @@ +--- +status: template +playbook-section: 10 +title: "Data Subject Rights (DSR) Fulfilment Procedure" +gdpr-articles: [Art. 15, Art. 16, Art. 17, Art. 18, Art. 20] +last-reviewed: "[FILL IN: YYYY-MM-DD]" +--- + +# Data Subject Rights (DSR) Fulfilment Procedure + +> **Template status** — fill every `[FILL IN: …]` marker before use. +> Cross-references below point to shipped interfaces, endpoints, and ADRs; do not change them. + +--- + +## 1. Purpose & Scope + +This procedure governs how `[FILL IN: organisation name]` receives, validates, fulfils, and records requests from data subjects exercising rights under GDPR Articles 15–20. + +**Covered rights:** + +| GDPR Article | Right | Shipped interface | +| ------------ | --------------------------------- | ----------------------------------------- | +| Art. 15 + 20 | Access + Portability | `IDataExport` (`dsr.export`) | +| Art. 16 | Rectification | `IDataRectify` (`dsr.rectify`) | +| Art. 17 | Erasure ("right to be forgotten") | `IDataDelete` (`dsr.delete`) | +| Art. 18 | Restriction of processing | `IProcessingRestriction` (`dsr.restrict`) | + +See [`docs/guides/dsr.md`](../../guides/dsr.md) for the engineering cookbook and [`docs/decisions/adr-025-eu-compliance-baseline.md`](../../decisions/adr-025-eu-compliance-baseline.md) § Epic B for the design rationale. + +--- + +## 2. Roles & Contacts + +| Role | Name | Contact | +| -------------------------------- | ------------------------------ | ------------------ | +| Data Protection Officer | [FILL IN: name / DPO provider] | [FILL IN: contact] | +| DSR Coordinator | [FILL IN: name] | [FILL IN: contact] | +| Engineering contact (fulfilment) | [FILL IN: name] | [FILL IN: contact] | + +**Intake channel:** `[FILL IN: e.g. privacy@example.com / online form URL]` + +--- + +## 3. Statutory Deadlines + +| Stage | Deadline | Extension | +| ------------------------- | --------------------------------------------------- | ------------------------------------------------------------------------------------------- | +| Acknowledge receipt | `[FILL IN: e.g. 5 business days]` | — | +| Fulfil or refuse | **1 calendar month** from receipt (GDPR Art. 12(3)) | Up to 2 further months for complex/numerous requests; notify subject within the first month | +| Notify subject of refusal | 1 month | — | + +Deadline clock starts when the organisation receives the request — not when identity is verified. + +--- + +## 4. Phase 1 — Receipt & Intake + +### 4.1 Intake channels + +Accept DSR requests via: + +- `[FILL IN: primary channel, e.g. privacy form at https://example.com/privacy/request]` +- `[FILL IN: secondary channel, e.g. email to privacy@example.com]` +- Any written format (GDPR does not mandate a specific form) + +### 4.2 Logging the request + +Log each incoming request immediately in `[FILL IN: DSR register / issue tracker]` with: + +| Field | Value | +| --------------- | --------------------------- | +| Reference ID | `DSR-YYYY-NNN` (sequential) | +| Right requested | Art. 15 / 16 / 17 / 18 / 20 | +| Channel | email / form / letter | +| Received at | ISO 8601 timestamp | +| Deadline | received + 1 month | +| Status | `pending-verification` | + +### 4.3 Acknowledgement + +Send acknowledgement to the requester within `[FILL IN: e.g. 5 business days]` confirming: + +- Receipt of the request +- Reference ID +- Identity verification requirement (§ 5) +- Statutory deadline + +Template: `[FILL IN: path to acknowledgement email template]` + +--- + +## 5. Phase 2 — Identity Validation + +### 5.1 Verification requirement + +GDPR Art. 12(6) permits identity verification when there is reasonable doubt. Always verify for Art. 17 (erasure) and Art. 18 (restriction). For Art. 15 (access) of clearly authenticated users, verification may be satisfied by existing session. + +### 5.2 Verification methods + +| Method | Acceptable for | +| ---------------------------------------------- | ---------------------------------- | +| Authenticated session in the app | Art. 15, Art. 16 (low-risk fields) | +| Email confirmation to registered address | All rights | +| `[FILL IN: government ID / identity provider]` | Art. 17 cascade-hard, Art. 18 | + +### 5.3 Rejected or suspicious requests + +If identity cannot be verified after `[FILL IN: e.g. 14 days]`: + +1. Close the request with status `identity-unverified`. +2. Notify the requester in writing of the outcome. +3. Log closure in the DSR register. + +--- + +## 6. Phase 3 — Fulfilment + +### 6.1 Identify the subject record + +Map the verified identity to the system `subjectId` (Payload `users.id` or equivalent auth collection primary key). Cross-reference `compliance/data-map.yml` (generated by `pnpm compliance:data-map`) to enumerate which collections hold personal data for this subject. + +```bash +# Regenerate data map to ensure it reflects current schema +pnpm compliance:data-map +``` + +Review `compliance/data-map.yml` fields tagged `exportable: true` (Art. 15/20) and `restrictable: true` (Art. 18). + +### 6.2 Art. 15 + 20 — Access and Portability + +**Interface:** `IDataExport.exportSubjectData(subjectId, format)` +**tRPC procedure:** `dsr.export` (query — no mutation) +**GDPR deadline:** respond within 1 month; provide data without charge for first copy. + +Invoke via the admin tRPC client or the REST endpoint wired in `apps/web-next`: + +``` +GET /api/gdpr/export?subjectId=&format=json +GET /api/gdpr/export?subjectId=&format=json-ld # machine-readable JSON-LD +``` + +The response is a `UserDataBundle`: + +- `data` — keyed by Payload collection slug; each bucket contains `asSelf` (rows the subject owns) and `asReference` (rows merely referencing the subject) +- `auditLog` — the subject's audit trail (optional; include for transparency) +- `exportedAt` — ISO 8601 export timestamp for the certificate of fulfilment + +**Audit entry emitted:** `EXPORT` action, `resource.type: "data-subject"`, `resource.id: subjectId`. + +Deliver the export to the subject via `[FILL IN: secure delivery method, e.g. encrypted download link / secure email]`. + +### 6.3 Art. 16 — Rectification + +**Interface:** `IDataRectify.updateSubjectField(subjectId, collection, field, value)` +**tRPC procedure:** `dsr.rectify` (mutation) +**Scope:** only fields tagged `custom.pii` in the Payload collection config (i.e. fields indexed in `compliance/data-map.yml`). + +``` +POST /api/gdpr/rectify +Body: { subjectId, collection, field, value } +``` + +Steps: + +1. Confirm the field is `custom.pii`-tagged in `compliance/data-map.yml`. +2. Confirm the new value passes Payload field validation. +3. Invoke the procedure; the implementation emits a `RESTRICT` audit entry with `reason: "art-16-request"`. +4. Notify the subject that rectification is complete and, if data was shared with third parties, notify them per Art. 19. + +Third-party notification log: `[FILL IN: log location]` + +### 6.4 Art. 17 — Erasure + +**Interface:** `IDataDelete.deleteSubjectData(subjectId, mode)` +**tRPC procedure:** `dsr.delete` (mutation — requires authenticated admin session for `cascade-hard`) +**Returned:** `DeletionCertificate` — retain this as evidence of fulfilment. + +**Mode selection:** + +| Mode | Effect | Who can invoke | +| ---------------- | ------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------- | +| `"soft"` | Redacts PII fields in owned rows; NULLs reference fields; row structure preserved | Standard authenticated user or DSR coordinator | +| `"cascade-hard"` | Hard-deletes owned rows where `postDeletion.action === "hard-delete"` per retention policy; NULLs reference fields | Admin role required | + +``` +POST /api/gdpr/delete +Body: { subjectId, mode: "soft" | "cascade-hard" } +``` + +**Exemptions** (GDPR Art. 17(3)) — erasure must be refused if data is required for: + +- Legal obligation compliance (document refusal in the DSR register) +- Establishment, exercise, or defence of legal claims + +**Audit trail pseudonymization:** after deletion, invoke `IAuditLog.eraseSubject(actorId, "pseudonymize")` via the admin tRPC to replace the subject identifier in the audit log with `erased-{hash[0:16]}` (sha256-salted pseudonym per ADR-018). This preserves the event record for regulatory purposes while removing the identifier. + +**Retention-policy overrides:** check `compliance/retention-policy.yml` for `postDeletion` rules — some collections may have a grace period (`postDeletion.duration: P30D`) before hard deletion executes. + +```bash +# View current retention policy +cat compliance/retention-policy.yml + +# Regenerate from Payload field tags +pnpm compliance:retention-policy +``` + +**DeletionCertificate fields to retain:** + +- `subjectId` (or pseudonymized form) +- `mode`, `timestamp`, `reason: "art-17-request"` +- `affected[]` — collections and rows modified +- `auditEntryId` — links to the audit log + +### 6.5 Art. 18 — Restriction of Processing + +**Interface:** `IProcessingRestriction.setRestriction(subjectId, granted: true)` +**tRPC procedure:** `dsr.restrict` (mutation) + +``` +POST /api/gdpr/restrict +Body: { subjectId, granted: true } +``` + +The implementation sets `processingRestrictedAt` on the user record. Downstream use cases check `isRestricted(subjectId)` before processing personal data. The audit channel records a `RESTRICT` audit entry on every state change. + +**Grounds for restriction (Art. 18(1)):** + +- Accuracy of data is contested (pending Art. 16 rectification) +- Processing is unlawful but erasure is opposed by the subject +- Organisation no longer needs the data but subject requires it for legal claims +- Subject has objected (pending verification of legitimate grounds) + +Notify the subject when restriction is lifted: invoke `dsr.restrict` with `granted: false` and send written notice per Art. 18(3). + +**Audit entry emitted:** `RESTRICT` (on restriction) / `UNRESTRICT` (on lift). + +--- + +## 7. Phase 4 — Recording & Closure + +### 7.1 DSR register update + +Update the DSR register entry (§ 4.2) with: + +| Field | Value | +| -------------- | ------------------------------------------------- | +| Fulfilled at | ISO 8601 timestamp | +| Outcome | `fulfilled` / `refused` / `partially-fulfilled` | +| Evidence | DeletionCertificate ID or export reference | +| Audit entry ID | From `AuditEntry.correlationId` or `auditEntryId` | +| Status | `closed` | + +### 7.2 Subject notification + +Notify the data subject of the outcome within the Art. 12(3) deadline: + +- Fulfilment: summary of actions taken, delivery method for exports. +- Refusal: grounds for refusal (Art. 12(4)), right to lodge complaint with SA, right to seek judicial remedy. + +Template: `[FILL IN: path to outcome notification template]` + +### 7.3 Third-party notification log (Art. 19) + +If the corrected, erased, or restricted data was previously shared with third parties (sub-processors listed in `compliance/sub-processors.yml`), notify each processor of the subject's request. Record each notification in `[FILL IN: third-party notification log]`. + +--- + +## 8. Consent-Related DSR Interactions + +Consent grant and withdrawal are distinct from Art. 17 erasure but often accompany DSR requests. The consent channel ([`docs/guides/consent.md`](../../guides/consent.md)) records: + +| Audit action | Trigger | +| ------------------ | -------------------------------------------------------- | +| `CONSENT_GRANT` | Subject grants consent for a category (e.g. `marketing`) | +| `CONSENT_WITHDRAW` | Subject withdraws consent | +| `RESTRICT` | Art. 18 restriction set | +| `UNRESTRICT` | Art. 18 restriction lifted | + +If a DSR request also includes consent withdrawal, invoke `IConsent.withdraw(subjectId, categories)` in addition to the relevant DSR procedure. Consent withdrawal does not automatically trigger erasure — only an explicit Art. 17 request does. + +--- + +## 9. Appendix — Command Reference + +```bash +# Regenerate PII data map (enumerate affected collections and fields) +pnpm compliance:data-map + +# Regenerate retention policy (check postDeletion rules before Art. 17) +pnpm compliance:retention-policy + +# Regenerate sub-processor list (for Art. 19 third-party notification) +pnpm compliance:sub-processors + +# Regenerate all compliance artifacts in one pass +pnpm compliance:emit-all + +# Run fallow audit before closing a DSR-related PR +pnpm fallow:audit +``` + +--- + +## 10. Document Control + +| Field | Value | +| ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Owner | [FILL IN: DPO or DSR Coordinator] | +| Review cycle | [FILL IN: e.g. annual or on schema change] | +| Next review date | [FILL IN: YYYY-MM-DD] | +| Cross-references | [`docs/decisions/adr-025-eu-compliance-baseline.md`](../../decisions/adr-025-eu-compliance-baseline.md), [`docs/decisions/adr-018-audit-and-compliance.md`](../../decisions/adr-018-audit-and-compliance.md), [`docs/guides/dsr.md`](../../guides/dsr.md), [`docs/guides/audit-and-compliance.md`](../../guides/audit-and-compliance.md), [`docs/guides/consent.md`](../../guides/consent.md), [`../data-map.example.yml`](../data-map.example.yml) | diff --git a/docs/compliance/templates/incident-runbook.template.md b/docs/compliance/templates/incident-runbook.template.md new file mode 100644 index 0000000..3821377 --- /dev/null +++ b/docs/compliance/templates/incident-runbook.template.md @@ -0,0 +1,264 @@ +--- +status: template +playbook-section: 20 +title: "Security Incident & Personal Data Breach Runbook" +gdpr-articles: [Art. 33, Art. 34] +last-reviewed: "[FILL IN: YYYY-MM-DD]" +--- + +# Security Incident & Personal Data Breach Runbook + +> **Template status** — fill every `[FILL IN: …]` marker before use. +> Cross-references below point to shipped code and ADRs; do not change them. + +--- + +## 1. Purpose & Scope + +This runbook governs the detection, triage, containment, notification, and post-mortem of security incidents and personal data breaches affecting `[FILL IN: product / organisation name]`. + +**In scope:** any event that may have compromised the confidentiality, integrity, or availability of personal data processed by this system, including unauthorized access, exfiltration, accidental exposure, and ransomware. + +**Out of scope:** non-PII service disruptions (handle via standard on-call runbook). Escalate to this runbook when PII impact cannot be ruled out. + +--- + +## 2. Severity Classification + +| Level | Definition | Example | +| ----------------- | ------------------------------------------------- | ---------------------------------------------------- | +| **P0 — Critical** | Confirmed exfiltration or mass exposure of PII | Database dump on public host | +| **P1 — High** | Suspected breach; blast radius unknown | Anomalous `EXPORT` audit entries for non-admin actor | +| **P2 — Medium** | Contained misconfiguration; no confirmed PII leak | Stale presigned URL exposed to wrong tenant | +| **P3 — Low** | Near-miss; no PII accessed | Failed injection attempt blocked by rate-limit | + +--- + +## 3. Roles & Contacts + +| Role | Name | Contact | +| ----------------------- | ------------------------------ | ------------------ | +| Incident Commander | [FILL IN: name] | [FILL IN: contact] | +| Data Protection Officer | [FILL IN: name / DPO provider] | [FILL IN: contact] | +| Legal / Counsel | [FILL IN: name] | [FILL IN: contact] | +| Engineering Lead | [FILL IN: name] | [FILL IN: contact] | +| Communications Lead | [FILL IN: name] | [FILL IN: contact] | + +**Supervisory Authority (SA):** + +- Authority name: `[FILL IN: e.g. ICO / CNIL / BfDI]` +- Online notification portal: `[FILL IN: URL]` +- Emergency phone: `[FILL IN: number]` + +--- + +## 4. Phase 1 — Detection + +### 4.1 Automated signals + +This template ships two automated detection surfaces. Check both when an alert fires. + +#### Sentry error alerting (ADR-014) + +Every unhandled exception in `apps/web-next`, `apps/cms`, and `apps/web-tanstack` is captured via `ILogger.captureException` and routed to Sentry ([`docs/decisions/adr-014-instrumentation-sentry.md`](../../decisions/adr-014-instrumentation-sentry.md)). Configure alert rules in Sentry for: + +- Spike in `4xx` / `5xx` on auth or DSR routes +- New issue fingerprints in production environment +- Volume anomaly on `[FILL IN: Sentry project slug]` + +**Verify DSNs are configured:** + +```bash +# Each app has its own Sentry project +echo $WEB_NEXT_SENTRY_DSN +echo $CMS_SENTRY_DSN +echo $WEB_TANSTACK_SENTRY_DSN # if TanStack Start app is in use +``` + +All three must be non-empty in production. A missing DSN silently disables capture for that app. + +#### Audit log anomalies (ADR-018) + +The audit channel ([`docs/decisions/adr-018-audit-and-compliance.md`](../../decisions/adr-018-audit-and-compliance.md)) records every `AuditAction` emitted by feature use cases via `AuditLogProtocol.record()`. Actions relevant to breach detection: + +| `AuditAction` | Breach signal | +| ------------------- | ----------------------------------------------------- | +| `EXPORT` | Mass export by non-admin actor or from unexpected IP | +| `VIEW` | High-frequency reads on a single `actorId` | +| `PERMISSION_CHANGE` | Privilege escalation outside change-management window | +| `DELETE` | Bulk delete with no corresponding DSR request | + +Query the Payload audit logs collection or the aggregated cold archive (`[FILL IN: log aggregation URL, e.g. Vector / Fluent Bit sink]`) for anomalies. Correlate with OTel `correlationId` (trace ID auto-populated by `TraceIdEnrichingAuditLog`). + +#### Rate-limit exhaustion (ADR-025 § Epic C) + +The rate-limit primitive (`IRateLimit` in `core-shared/rate-limit`, see [`docs/guides/rate-limiting.md`](../../guides/rate-limiting.md)) emits `{ allowed: false, remaining: 0, resetAt }` when a budget is exhausted. Repeated exhaustion on auth or DSR endpoints may indicate credential-stuffing or automated exfiltration. + +Check budget tables declared in feature manifests (`rateLimit: { window, budget }`) against observed traffic in `[FILL IN: metrics dashboard URL]`. + +#### Security header violations + +HSTS, `X-Frame-Options`, and CSP headers are emitted by the security-headers middleware ([`docs/guides/security-headers.md`](../../guides/security-headers.md)). CSP violation reports (if `report-uri` is configured) surface attempted XSS. Check `[FILL IN: CSP report endpoint / dashboard]`. + +### 4.2 Manual discovery + +If detection was by human observation (e.g., responsible-disclosure report, darknet alert): + +1. Log the reporter's contact and discovery timestamp. +2. Do not confirm or deny breach details to the reporter until DPO is engaged. +3. Proceed to Phase 2. + +--- + +## 5. Phase 2 — Triage + +**Target time: within [FILL IN: e.g. 2 hours] of detection.** + +### 5.1 Triage checklist + +- [ ] Page Incident Commander and DPO. +- [ ] Open incident channel: `[FILL IN: e.g. #incident-YYYYMMDD in Slack / Teams]`. +- [ ] Document initial facts: detected at, detected by, affected system(s). +- [ ] Classify severity (§ 2). +- [ ] Determine if personal data is involved (see § 5.2). +- [ ] Initiate 72-hour DPA notification clock if P0 or P1 (§ 7.1). + +### 5.2 PII impact assessment + +Use the generated data map (`compliance/data-map.yml`, generated by `pnpm compliance:data-map` from Payload `custom.pii` field tags) to enumerate which collections and fields may be affected. Cross-reference with the scope of the incident (endpoint, DB table, S3 bucket, etc.). + +Key questions: + +- Which `piiCategory` values are in the affected scope? (e.g. `contact-email`, `identification-name`) +- Are any `restrictable: true` subjects affected (i.e. users who invoked Art. 18)? +- Was the `auditLogs` collection itself compromised? (Audit trail integrity is required for Art. 33 notification.) + +--- + +## 6. Phase 3 — Containment + +**Target time: within [FILL IN: e.g. 4 hours] of confirmation for P0/P1.** + +### 6.1 Immediate actions + +- [ ] Revoke or rotate compromised credentials (`[FILL IN: credential management system]`). +- [ ] Block actor at network/WAF level if attack is ongoing (`[FILL IN: WAF / CDN control panel URL]`). +- [ ] If a specific `actorId` is confirmed malicious: suspend account in Payload admin (`apps/cms`, port 3001). +- [ ] If a tRPC or REST route is the attack vector: deploy an emergency rate-limit tightening or disable the route (`[FILL IN: deployment method]`). +- [ ] Preserve evidence: take read-only snapshots of affected tables, CloudWatch / log streams, and network flow logs before any cleanup. + +### 6.2 Audit trail preservation + +The audit log collection is append-only (`update: () => false` Payload access rule per ADR-018). Do not attempt to delete or modify audit entries. If the audit collection itself is a target, freeze DB-level access and take a snapshot: + +```bash +# [FILL IN: adapt to your database provider] +pg_dump --schema-only --table=audit_logs $DATABASE_URL > audit_logs_schema_snapshot.sql +pg_dump --data-only --table=audit_logs $DATABASE_URL > audit_logs_data_snapshot.sql +``` + +--- + +## 7. Phase 4 — Notification + +### 7.1 Regulatory notification deadlines + +| Obligation | Deadline | Trigger | +| -------------------------------------------- | -------------------------------- | -------------------------------------------------------------------------- | +| GDPR Art. 33 — notify Supervisory Authority | **72 hours** from becoming aware | Any breach involving personal data unless unlikely to risk rights/freedoms | +| `[FILL IN: national DPA requirement]` | **[FILL IN: e.g. 24 hours]** | `[FILL IN: condition]` | +| GDPR Art. 34 — notify affected data subjects | Without undue delay | High risk to rights and freedoms | + +**Clock start:** the moment the organisation "becomes aware" — typically when the Incident Commander or DPO confirms the incident in § 5.1, not when it was first detected. + +### 7.2 Supervisory Authority notification (Art. 33) + +Prepare the notification using the SA's online portal (`[FILL IN: URL]`). Required fields per Art. 33(3): + +- Nature of the breach (categories and approximate number of records / data subjects) +- Name and contact details of DPO: `[FILL IN: DPO name, email, phone]` +- Likely consequences of the breach +- Measures taken or proposed + +Attach: + +- Incident timeline (from detection to containment) +- Affected `piiCategories` from `compliance/data-map.yml` +- Audit log extract covering the incident window (redact unrelated subjects) + +**If full facts are not available within 72 hours:** submit an initial notification marked "partial" and follow up without delay. GDPR Art. 33(4) explicitly permits phased notification. + +### 7.3 Data subject notification (Art. 34) + +Threshold: notification is required when the breach is likely to result in **high risk** to the rights and freedoms of individuals (e.g. identity theft, financial loss, discrimination). + +- Drafting: `[FILL IN: communications lead]` drafts notice in plain language. +- Channel: `[FILL IN: e.g. in-app banner + email via transactional provider]` +- Content: nature of breach, DPO contact, likely consequences, mitigation steps. +- Timing: `[FILL IN: target send window, e.g. within 48 hours of Art. 33 notification]` + +### 7.4 Internal stakeholder notification + +| Stakeholder | When | Channel | +| ----------------------- | ---------------------------- | ----------- | +| Executive team | P0/P1: immediately | `[FILL IN]` | +| Customer success | Before customer-facing comms | `[FILL IN]` | +| Board / audit committee | Within `[FILL IN]` hours | `[FILL IN]` | + +--- + +## 8. Phase 5 — Post-mortem + +**Target: within [FILL IN: e.g. 5 business days] of containment.** + +### 8.1 Post-mortem checklist + +- [ ] Timeline reconstruction (detection → triage → containment → notification). +- [ ] Root cause analysis (five-whys or equivalent). +- [ ] Blast radius: confirmed data subjects affected, `piiCategories` exposed, duration of exposure. +- [ ] Audit log review: were anomalous `AuditAction` entries present before detection? Were `from.ipTruncated` values suspicious? +- [ ] Control gaps identified. +- [ ] Remediation actions with owner and deadline. +- [ ] Lessons learned. + +### 8.2 Remediation tracking + +Document each action in `[FILL IN: issue tracker, e.g. Linear / GitHub Issues]` with: + +- Linked ADR or guide (e.g. ADR-018, ADR-014) +- Owner +- Target date +- Verification step (e.g. `pnpm fallow:audit` green, new test added) + +### 8.3 Mandatory follow-up with Supervisory Authority + +If the Art. 33 notification was submitted as "partial", file the complete notification with the SA by `[FILL IN: agreed follow-up date]`. + +--- + +## 9. Appendix — Command Reference + +```bash +# Inspect running apps and their Sentry DSN status +echo $WEB_NEXT_SENTRY_DSN && echo $CMS_SENTRY_DSN + +# Run fallow audit to surface dead exports / drift before post-mortem PR +pnpm fallow:audit + +# Regenerate data map to confirm affected PII fields +pnpm compliance:data-map + +# Re-emit all compliance artifacts (data-map + retention-policy + sub-processors) +pnpm compliance:emit-all +``` + +--- + +## 10. Document Control + +| Field | Value | +| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Owner | [FILL IN: DPO or Engineering Lead] | +| Review cycle | [FILL IN: e.g. annual or after every P0/P1] | +| Next review date | [FILL IN: YYYY-MM-DD] | +| Cross-references | [`docs/decisions/adr-018-audit-and-compliance.md`](../../decisions/adr-018-audit-and-compliance.md), [`docs/decisions/adr-014-instrumentation-sentry.md`](../../decisions/adr-014-instrumentation-sentry.md), [`docs/decisions/adr-025-eu-compliance-baseline.md`](../../decisions/adr-025-eu-compliance-baseline.md), [`docs/guides/audit-and-compliance.md`](../../guides/audit-and-compliance.md), [`docs/guides/rate-limiting.md`](../../guides/rate-limiting.md), [`docs/guides/security-headers.md`](../../guides/security-headers.md) |