# Consent-Ledger-Runbook für 2mp.ch

2mp.ch: Consent-Ledger-Blueprint mit 14 Datenfeldern, 7 Ereignissen, 7 Log-Zeilen und 6 Kategorien für auditierbare Einwilligungsnachweise.

> Der Ledger-Blueprint ist eine technische Betreiber-Vorlage. Rechtsgrundlage, Speicherfrist, Zugriffsschutz und Integration in CMP/Backend müssen fachlich freigegeben werden.

## Ereignisse
- Banner angezeigt: Erstaufruf ohne gespeicherte Entscheidung Felder: domain, banner_version, policy_version, region, created_at
- Einwilligung gespeichert: Nutzer klickt Akzeptieren oder speichert Kategorien Felder: consent_id, categories, services, action, created_at
- Ablehnung gespeichert: Nutzer klickt Ablehnen Felder: consent_id, categories, action, created_at
- Widerruf gespeichert: Privacy-Trigger oder Preference Center ändert Entscheidung Felder: previous_consent_id, withdrawn_at, categories, source
- GPC-Signal berücksichtigt: Browser sendet Global Privacy Control Felder: gpc_signal, region, action, created_at
- Downstream-Sync: Tag Manager, Analytics, Ads, CRM oder CMP erhält Status Felder: destination, categories, sync_status, created_at
- Re-Scan-Nachweis: Nach Ihrer Änderung erneut prüfen Felder: scan_id, checked_at, result_url, evidence_hash

## Datenfelder
- consent_id (uuid/string): Eindeutiger Nachweisdatensatz Nicht als Klartext-Personenkennung verwenden.
- subject_key_hash (sha256/string): Wiedererkennung ohne Klartext-ID Salt/Rotation und Zugriffsschutz dokumentieren.
- domain (string): Betroffene Website Mandanten-/Domain-Trennung erzwingen.
- region (string): DE/EU/CH/AT oder CMP-Region Regelbasiertes Verhalten nachvollziehbar machen.
- banner_version (string): Version der Consent-Oberfläche Bei Text-/Layoutänderung erhöhen.
- policy_version (string): Datenschutz-/Cookie-Hinweis-Version Mit veröffentlichtem Hinweis und Datum verbinden.
- categories_json (json): Status je Kategorie Nur notwendige Kategorien speichern.
- services_json (json): Optionaler Status je Dienst Dienste aus Anbieterregister synchronisieren.
- action (enum): accept, reject, save, withdraw, gpc Ablehnung und Widerruf gleichwertig protokollieren.
- ip_hash (string/null): Missbrauchs-/Nachweisbezug Klartext-IP vermeiden; Speicherfrist begrenzen.
- user_agent_hash (string/null): Technischer Kontext Nicht für Tracking oder Profiling verwenden.
- evidence_ref (string): Verweis auf Scan, Screenshot oder Export Keine sensiblen Rohdaten in öffentliche Exporte schreiben.
- created_at (datetime): Zeitpunkt der Entscheidung Zeitzone und Serverzeit stabil halten.
- expires_at (datetime/null): Review-/Ablauffrist Regelmäßige Erneuerung und Löschung planen.

## Transaktionslog
- banner_shown: action=show, row_hash=149cf3cb022645ca60ad375ba3a2e232c765e9051d217a8f2db770220cf62a26
- consent_saved: action=accept_or_save, row_hash=ec6a7c1d198912bc5c61f4c24d9d1ecdc36a5ee759cd2b11ac9ab64aa14464ca
- consent_rejected: action=reject, row_hash=45f3f36631ad0223c612f886dc8ebfd7731eae4ad78b8df315031adb4ff078f3
- consent_withdrawn: action=withdraw, row_hash=3d62931b7f51b4c02ae3fe4e53833def1d3108febf05abb419c2499d7b0817bd
- gpc_seen: action=gpc, row_hash=ba47f578a0826f6bbd14b42ed0bafbbce78fb3a0635e20879986a25f7f078cb2
- vendor_sync: action=sync, row_hash=015878a4d848f1418216c0af4ea37ce8db45a46e02dec0be194049de0eb36ad2
- rescan_evidence: action=rescan, row_hash=02adfd0c12d5d6bb42fbf63c8471459a9a340fa952ea59151920554becb7703c

## Log-Export-Kontrollen
- **Domain und Zeitraum**: Export immer nach Domain, Zeitraum und Rechtsraum filtern; keine domainübergreifenden Rohlogs ohne Zweckfreigabe.
- **Pseudonymisierung**: Subject-, IP- und User-Agent-Bezug nur gehasht und mit Zugriffsschutz exportieren.
- **Hashkette**: Jede Zeile enthält previous_hash und row_hash, damit nachträgliche Änderungen auffallen.
- **Betroffenenrechte**: DSAR-Export trennt Nachweiszweck, technische Logs und Lösch-/Sperrentscheidungen.
- **Aufbewahrung**: Nachweisfrist und Löschfristen je Zweck dokumentieren; abgelaufene Rohdaten aggregieren oder löschen.

## Snippets
### SQL Ledger Tabelle

```
CREATE TABLE consent_ledger_2mp_ch (
  consent_id text PRIMARY KEY,
  subject_key_hash text NOT NULL,
  domain text NOT NULL,
  region text NOT NULL,
  banner_version text NOT NULL,
  policy_version text NOT NULL,
  action text NOT NULL CHECK (action IN ('accept','reject','save','withdraw','gpc')),
  categories_json jsonb NOT NULL,
  services_json jsonb NOT NULL DEFAULT '{}'::jsonb,
  gpc_signal boolean NOT NULL DEFAULT false,
  ip_hash text,
  user_agent_hash text,
  evidence_ref text,
  created_at timestamptz NOT NULL DEFAULT now(),
  expires_at timestamptz
);
CREATE INDEX consent_ledger_2mp_ch_subject_idx ON consent_ledger_2mp_ch (subject_key_hash, created_at DESC);
```

### Browser Event Bridge

```
window.addEventListener('saferpage-consent-update', function (event) {
  fetch('/privacy/consent-ledger', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    credentials: 'same-origin',
    body: JSON.stringify({
      domain: '2mp.ch',
      banner_version: 'v1',
      policy_version: 'privacy-notice-current',
      action: 'save',
      categories: event.detail,
      evidence_ref: window.location.href
    })
  });
});
```

