This repository has been archived on 2026-02-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
breakpilot-pwa/docs/consent-banner/SPECIFICATION.md
Benjamin Admin 21a844cb8a fix: Restore all files lost during destructive rebase
A previous `git pull --rebase origin main` dropped 177 local commits,
losing 3400+ files across admin-v2, backend, studio-v2, website,
klausur-service, and many other services. The partial restore attempt
(660295e2) only recovered some files.

This commit restores all missing files from pre-rebase ref 98933f5e
while preserving post-rebase additions (night-scheduler, night-mode UI,
NightModeWidget dashboard integration).

Restored features include:
- AI Module Sidebar (FAB), OCR Labeling, OCR Compare
- GPU Dashboard, RAG Pipeline, Magic Help
- Klausur-Korrektur (8 files), Abitur-Archiv (5+ files)
- Companion, Zeugnisse-Crawler, Screen Flow
- Full backend, studio-v2, website, klausur-service
- All compliance SDKs, agent-core, voice-service
- CI/CD configs, documentation, scripts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 09:51:32 +01:00

1738 lines
48 KiB
Markdown

# Cookie & Consent Banner - Technische Spezifikation
**Version:** 1.0.0
**Status:** Draft
**Lizenz:** Apache 2.0 (Open Source, kommerziell nutzbar)
**Letzte Aktualisierung:** 2026-02-04
---
## Inhaltsverzeichnis
1. [Executive Summary](#1-executive-summary)
2. [Rechtliche Anforderungen](#2-rechtliche-anforderungen)
3. [Architektur-Übersicht](#3-architektur-übersicht)
4. [Consent-Kategorien](#4-consent-kategorien)
5. [SDK-Spezifikation](#5-sdk-spezifikation)
6. [API-Spezifikation](#6-api-spezifikation)
7. [UI/UX-Anforderungen](#7-uiux-anforderungen)
8. [Plattform-Integrationen](#8-plattform-integrationen)
9. [Datenspeicherung & Audit](#9-datenspeicherung--audit)
10. [Sicherheit & Privacy](#10-sicherheit--privacy)
11. [Implementierungs-Roadmap](#11-implementierungs-roadmap)
12. [Anhang](#12-anhang)
---
## 1. Executive Summary
### 1.1 Projektziel
Entwicklung eines **plattformübergreifenden Consent-Management-Systems** (CMP), das:
- DSGVO, TTDSG und ePrivacy-Richtlinie vollständig erfüllt
- In PWAs, Websites, Mobile Apps und Desktop-Anwendungen integrierbar ist
- Als Open-Source-Lösung kommerziell nutzbar ist (Apache 2.0)
- Über SDKs für alle gängigen Plattformen verfügbar ist
- Mit dem bestehenden BreakPilot Consent-Service integriert
### 1.2 Kernfunktionen
| Funktion | Beschreibung |
|----------|--------------|
| **Consent Collection** | Granulare Einwilligung pro Kategorie und Anbieter |
| **Consent Storage** | Revisionssichere Speicherung aller Einwilligungen |
| **Consent Withdrawal** | Jederzeitiger Widerruf mit gleicher Einfachheit |
| **Cross-Platform Sync** | Consent-Status über alle Plattformen synchronisiert |
| **TCF 2.2 Support** | IAB Transparency & Consent Framework Integration |
| **A/B Testing** | DSGVO-konforme Banner-Optimierung |
| **Analytics** | Anonymisierte Consent-Statistiken |
### 1.3 Zielplattformen
- **Web:** JavaScript SDK (Vanilla, React, Vue, Angular, Svelte)
- **PWA:** Service Worker Integration mit Offline-Support
- **iOS:** Swift SDK (SwiftUI & UIKit)
- **Android:** Kotlin SDK (Jetpack Compose & XML Views)
- **Flutter:** Cross-Platform SDK
- **React Native:** Cross-Platform SDK
---
## 2. Rechtliche Anforderungen
### 2.1 DSGVO (EU 2016/679)
| Artikel | Anforderung | Umsetzung |
|---------|-------------|-----------|
| **Art. 4 Nr. 11** | Einwilligung muss freiwillig, bestimmt, informiert und unmissverständlich sein | Granulare Opt-in-Buttons, keine vorausgewählten Checkboxen |
| **Art. 6 Abs. 1 lit. a** | Einwilligung als Rechtsgrundlage | Dokumentierte Consent-Erfassung mit Timestamp |
| **Art. 7 Abs. 1** | Nachweis der Einwilligung | Revisionssichere Speicherung, Audit-Log |
| **Art. 7 Abs. 2** | Abgrenzung von anderen Sachverhalten | Separate Consent-Abfrage, kein Bundling |
| **Art. 7 Abs. 3** | Widerruf jederzeit möglich | Persistenter "Einstellungen"-Button, gleichwertige Widerrufsmöglichkeit |
| **Art. 7 Abs. 4** | Koppelungsverbot | Keine Dienstverweigerung bei Ablehnung |
| **Art. 12** | Transparente Information | Klare, verständliche Sprache |
| **Art. 13/14** | Informationspflichten | Verlinkung zur Datenschutzerklärung |
| **Art. 17** | Recht auf Löschung | Löschung aller Consent-Daten auf Anfrage |
| **Art. 20** | Datenportabilität | Export aller Consent-Daten als JSON |
### 2.2 TTDSG (Deutschland)
| § | Anforderung | Umsetzung |
|---|-------------|-----------|
| **§ 25 Abs. 1** | Einwilligung für nicht-notwendige Cookies | Opt-in für alle nicht-essentiellen Technologien |
| **§ 25 Abs. 2** | Ausnahmen für technisch notwendige Speicherung | Klare Kategorisierung "Essentiell" ohne Einwilligung |
| **§ 25 Abs. 2 Nr. 2** | Ausnahme für vom Nutzer gewünschte Dienste | Dokumentation der Notwendigkeit |
### 2.3 ePrivacy-Richtlinie (2002/58/EG)
| Artikel | Anforderung | Umsetzung |
|---------|-------------|-----------|
| **Art. 5 Abs. 3** | Einwilligung vor Cookie-Setzung | Blockierung aller Skripte bis Consent |
| **Art. 5 Abs. 3** | Klare Information über Zweck | Transparente Kategoriebeschreibungen |
### 2.4 Planet49-Urteil (EuGH C-673/17)
**Kernaussagen:**
- Vorausgewählte Checkboxen sind KEINE gültige Einwilligung
- Cookie-Laufzeit muss angegeben werden
- Drittanbieter müssen benannt werden
**Umsetzung:**
- Alle Checkboxen standardmäßig NICHT ausgewählt
- Anzeige der Cookie-Laufzeit pro Anbieter
- Auflistung aller Drittanbieter mit Links zu deren Datenschutzerklärung
### 2.5 DSK-Orientierungshilfe Telemedien
**Anforderungen:**
1. "Ablehnen" muss genauso prominent sein wie "Akzeptieren"
2. Keine Dark Patterns (versteckte Ablehnungsoptionen)
3. Keine Nudging-Techniken (farbliche Hervorhebung von "Akzeptieren")
4. Direkter Zugang zu granularen Einstellungen
### 2.6 BGH-Urteil Cookie-Einwilligung II (2023)
**Kernaussagen:**
- "Alles akzeptieren" nur zulässig wenn gleichwertige "Alles ablehnen"-Option
- Keine versteckten Ablehungs-Flows
- Consent-Banner darf Seiteninhalt nicht vollständig verdecken
---
## 3. Architektur-Übersicht
### 3.1 System-Architektur
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Client-Plattformen │
├──────────┬──────────┬──────────┬──────────┬──────────┬─────────────────┤
│ Web │ PWA │ iOS │ Android │ Flutter │ React Native │
│ SDK │ SDK │ SDK │ SDK │ SDK │ SDK │
└────┬─────┴────┬─────┴────┬─────┴────┬─────┴────┬─────┴────────┬────────┘
│ │ │ │ │ │
└──────────┴──────────┴────┬─────┴──────────┴──────────────┘
┌───────────▼───────────┐
│ Consent Gateway │
│ (REST + WebSocket) │
└───────────┬───────────┘
┌──────────────────────────┼──────────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Consent │ │ Config │ │ Analytics │
│ Service │ │ Service │ │ Service │
│ (Go) │ │ (Go) │ │ (Go) │
└──────┬──────┘ └────────┬────────┘ └────────┬────────┘
│ │ │
└──────────────────────┼────────────────────────┘
┌─────────▼─────────┐
│ PostgreSQL │
│ (Consent Store) │
└───────────────────┘
```
### 3.2 Komponenten
| Komponente | Technologie | Beschreibung |
|------------|-------------|--------------|
| **Web SDK** | TypeScript | Vanilla JS + Framework-Wrapper |
| **Mobile SDKs** | Swift/Kotlin | Native Implementierungen |
| **Consent Gateway** | Go + Gin | API-Gateway mit Rate-Limiting |
| **Consent Service** | Go | Consent-Logik, bereits vorhanden |
| **Config Service** | Go | Banner-Konfiguration, TCF-Vendors |
| **Analytics Service** | Go | Anonymisierte Statistiken |
| **PostgreSQL** | PostgreSQL 16 | Revisionssichere Speicherung |
### 3.3 Datenfluss
```
1. Nutzer öffnet Website/App
2. SDK prüft lokalen Consent-Status
┌────┴────┐
│ Consent │
│ exists? │
└────┬────┘
No │ Yes
│ │ │
▼ │ ▼
3. Banner│ 5. Apply
anzeigen Consent
4. Nutzer wählt
Kategorien
6. SDK speichert lokal
+ sendet an Backend
7. Backend speichert
revisionssicher
8. SDK blockiert/erlaubt
entsprechende Skripte
```
---
## 4. Consent-Kategorien
### 4.1 Standard-Kategorien (IAB TCF 2.2 kompatibel)
| ID | Kategorie | Beschreibung | Rechtsgrundlage |
|----|-----------|--------------|-----------------|
| `essential` | **Essentiell** | Technisch notwendig für Grundfunktionen | TTDSG § 25 Abs. 2 (keine Einwilligung nötig) |
| `functional` | **Funktional** | Personalisierung, Spracheinstellungen | DSGVO Art. 6 Abs. 1 lit. a |
| `analytics` | **Statistik** | Anonyme Nutzungsanalyse | DSGVO Art. 6 Abs. 1 lit. a |
| `marketing` | **Marketing** | Werbung, Retargeting | DSGVO Art. 6 Abs. 1 lit. a |
| `social` | **Soziale Medien** | Social Plugins, Sharing | DSGVO Art. 6 Abs. 1 lit. a |
### 4.2 Kategorie-Details
#### 4.2.1 Essentiell (`essential`)
**Beschreibung:** Cookies und Technologien, die für den Betrieb der Website unbedingt erforderlich sind.
**Beispiele:**
- Session-Cookies
- Warenkorb-Cookies
- Authentifizierung
- Load-Balancing
- CSRF-Token
- Consent-Cookie selbst
**Rechtsgrundlage:** TTDSG § 25 Abs. 2 Nr. 2 - keine Einwilligung erforderlich
**UI:** Immer aktiv, nicht deaktivierbar, mit Erklärung warum
#### 4.2.2 Funktional (`functional`)
**Beschreibung:** Cookies, die die Website-Funktionalität verbessern, aber nicht essentiell sind.
**Beispiele:**
- Sprachpräferenzen
- Theme-Einstellungen (Dark Mode)
- Zuletzt angesehene Produkte
- Chat-Widget-Status
- Video-Player-Einstellungen
**Rechtsgrundlage:** DSGVO Art. 6 Abs. 1 lit. a - Einwilligung erforderlich
#### 4.2.3 Statistik (`analytics`)
**Beschreibung:** Cookies zur anonymen Analyse des Nutzerverhaltens.
**Beispiele:**
- Google Analytics
- Matomo/Piwik
- Hotjar (Heatmaps)
- Plausible Analytics
**Rechtsgrundlage:** DSGVO Art. 6 Abs. 1 lit. a - Einwilligung erforderlich
**Hinweis:** Auch bei Anonymisierung ist Einwilligung erforderlich (EuGH-Rechtsprechung)
#### 4.2.4 Marketing (`marketing`)
**Beschreibung:** Cookies für personalisierte Werbung und Retargeting.
**Beispiele:**
- Google Ads
- Facebook Pixel
- LinkedIn Insight Tag
- Criteo
- Affiliate-Tracking
**Rechtsgrundlage:** DSGVO Art. 6 Abs. 1 lit. a - Einwilligung erforderlich
#### 4.2.5 Soziale Medien (`social`)
**Beschreibung:** Cookies von Social-Media-Plattformen.
**Beispiele:**
- Facebook Like-Button
- Twitter Share
- Instagram Embed
- YouTube Embed
- LinkedIn Share
**Rechtsgrundlage:** DSGVO Art. 6 Abs. 1 lit. a - Einwilligung erforderlich
### 4.3 Anbieter-Granularität (TCF 2.2)
Zusätzlich zur Kategorie kann der Nutzer einzelne Anbieter aktivieren/deaktivieren:
```typescript
interface ConsentVendor {
id: string; // TCF Vendor ID oder custom ID
name: string; // "Google LLC"
category: ConsentCategory;
purposes: TCFPurpose[]; // IAB TCF Purposes
legitimateInterests?: TCFPurpose[];
cookies: CookieInfo[];
privacyPolicyUrl: string;
dataRetention: string; // "2 Jahre" / "Session"
dataTransfer?: string; // "USA (EU-US DPF)" / "EU"
}
interface CookieInfo {
name: string; // "_ga"
domain: string; // ".example.com"
expiration: string; // "2 Jahre"
type: "http" | "localStorage" | "sessionStorage" | "indexedDB";
description: string;
}
```
---
## 5. SDK-Spezifikation
### 5.1 Web SDK (JavaScript/TypeScript)
#### 5.1.1 Installation
```bash
# npm
npm install @breakpilot/consent-sdk
# yarn
yarn add @breakpilot/consent-sdk
# pnpm
pnpm add @breakpilot/consent-sdk
# CDN (für Vanilla JS)
<script src="https://cdn.breakpilot.eu/consent-sdk/v1/consent.min.js"></script>
```
#### 5.1.2 Basis-Konfiguration
```typescript
import { ConsentManager, ConsentConfig } from '@breakpilot/consent-sdk';
const config: ConsentConfig = {
// Pflichtfelder
apiEndpoint: 'https://consent.example.com/api/v1',
siteId: 'site_abc123',
// Sprache
language: 'de',
fallbackLanguage: 'en',
// UI-Optionen
ui: {
position: 'bottom', // 'bottom' | 'top' | 'center'
layout: 'bar', // 'bar' | 'modal' | 'floating'
theme: 'auto', // 'light' | 'dark' | 'auto'
customCss: '/css/consent.css',
zIndex: 999999,
blockScrollOnModal: true,
},
// Consent-Optionen
consent: {
required: true, // Muss Nutzer interagieren?
rejectAllVisible: true, // "Alle ablehnen" Button sichtbar
acceptAllVisible: true, // "Alle akzeptieren" Button sichtbar
granularControl: true, // Einzelne Kategorien wählbar
vendorControl: true, // Einzelne Anbieter wählbar
rememberChoice: true, // Auswahl speichern
rememberDays: 365, // Speicherdauer in Tagen
geoTargeting: true, // Nur in EU anzeigen
recheckAfterDays: 180, // Erneut nachfragen nach X Tagen
},
// Kategorien aktivieren
categories: ['essential', 'functional', 'analytics', 'marketing', 'social'],
// Callbacks
onConsentChange: (consent) => {
console.log('Consent changed:', consent);
},
onBannerShow: () => {
console.log('Banner shown');
},
onBannerHide: () => {
console.log('Banner hidden');
},
// Debug-Modus
debug: process.env.NODE_ENV === 'development',
};
// Manager initialisieren
const consent = new ConsentManager(config);
consent.init();
```
#### 5.1.3 Skript-Blocking
```html
<!-- Skripte mit data-consent blockieren -->
<script
data-consent="analytics"
data-src="https://www.googletagmanager.com/gtag/js?id=GA-XXXXX"
type="text/plain">
</script>
<!-- Inline-Skripte blockieren -->
<script data-consent="marketing" type="text/plain">
fbq('init', 'XXXXX');
</script>
<!-- iFrames blockieren (YouTube, etc.) -->
<iframe
data-consent="social"
data-src="https://www.youtube.com/embed/XXXXX"
title="YouTube Video">
<p>Bitte akzeptieren Sie Marketing-Cookies, um dieses Video zu sehen.</p>
</iframe>
```
#### 5.1.4 API-Methoden
```typescript
// Consent-Status prüfen
const hasAnalytics = consent.hasConsent('analytics');
const hasVendor = consent.hasVendorConsent('google-analytics');
// Consent programmatisch setzen
await consent.setConsent({
essential: true,
functional: true,
analytics: false,
marketing: false,
social: false,
});
// Alle Einwilligungen widerrufen
await consent.revokeAll();
// Banner erneut anzeigen
consent.showBanner();
// Einstellungs-Modal öffnen
consent.showSettings();
// Consent-Objekt abrufen
const currentConsent = consent.getConsent();
// {
// categories: { essential: true, analytics: false, ... },
// vendors: { 'google-analytics': false, ... },
// timestamp: '2026-02-04T12:00:00Z',
// version: '1.0.0',
// consentId: 'cns_abc123',
// }
// Consent exportieren (für Nutzeranfragen)
const exportData = consent.exportConsent();
// JSON mit allen historischen Consents
// Event-Listener
consent.on('change', (newConsent) => { ... });
consent.on('vendor:enable', (vendorId) => { ... });
consent.on('vendor:disable', (vendorId) => { ... });
```
#### 5.1.5 Framework-Integrationen
**React:**
```tsx
import { ConsentProvider, useConsent, ConsentBanner } from '@breakpilot/consent-sdk/react';
function App() {
return (
<ConsentProvider config={config}>
<ConsentBanner />
<MainContent />
</ConsentProvider>
);
}
function AnalyticsComponent() {
const { hasConsent, isLoading } = useConsent('analytics');
if (isLoading) return <Skeleton />;
if (!hasConsent) return <ConsentPlaceholder category="analytics" />;
return <GoogleAnalytics />;
}
```
**Vue 3:**
```vue
<template>
<ConsentBanner />
<AnalyticsComponent v-if="hasConsent('analytics')" />
<ConsentPlaceholder v-else category="analytics" />
</template>
<script setup>
import { useConsent, ConsentBanner, ConsentPlaceholder } from '@breakpilot/consent-sdk/vue';
const { hasConsent } = useConsent();
</script>
```
**Angular:**
```typescript
import { ConsentModule } from '@breakpilot/consent-sdk/angular';
@NgModule({
imports: [
ConsentModule.forRoot(config),
],
})
export class AppModule {}
// In Component:
@Component({...})
export class MyComponent {
constructor(private consent: ConsentService) {}
get hasAnalytics(): boolean {
return this.consent.hasConsent('analytics');
}
}
```
### 5.2 PWA-spezifische Features
#### 5.2.1 Offline-Support
```typescript
const pwaConfig: ConsentConfig = {
...baseConfig,
pwa: {
offlineSupport: true,
syncOnReconnect: true,
cacheStrategy: 'stale-while-revalidate',
},
};
// Service Worker Integration
// consent-sw.js wird automatisch registriert
```
#### 5.2.2 App-Banner Integration
```typescript
// Consent vor PWA-Install-Banner abfragen
consent.on('beforeInstallPrompt', (event) => {
if (!consent.hasConsent('functional')) {
event.preventDefault();
consent.showBanner({
message: 'Für die App-Installation benötigen wir Ihre Einwilligung.',
highlightCategory: 'functional',
});
}
});
```
### 5.3 iOS SDK (Swift)
#### 5.3.1 Installation
```swift
// Swift Package Manager
dependencies: [
.package(url: "https://github.com/breakpilot/consent-sdk-ios", from: "1.0.0")
]
// CocoaPods
pod 'BreakPilotConsent', '~> 1.0'
```
#### 5.3.2 Konfiguration
```swift
import BreakPilotConsent
let config = ConsentConfig(
apiEndpoint: "https://consent.example.com/api/v1",
siteId: "site_abc123",
language: .german,
categories: [.essential, .functional, .analytics, .marketing]
)
// AppDelegate oder SceneDelegate
ConsentManager.shared.configure(with: config)
```
#### 5.3.3 SwiftUI Integration
```swift
import SwiftUI
import BreakPilotConsent
struct ContentView: View {
@StateObject private var consent = ConsentManager.shared
var body: some View {
VStack {
if consent.needsConsent {
ConsentBannerView()
}
MainContent()
}
.consentGate(category: .analytics) {
AnalyticsView()
} placeholder: {
ConsentPlaceholder(category: .analytics)
}
}
}
```
#### 5.3.4 UIKit Integration
```swift
import BreakPilotConsent
class ViewController: UIViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if ConsentManager.shared.needsConsent {
let bannerVC = ConsentBannerViewController()
bannerVC.modalPresentationStyle = .overFullScreen
present(bannerVC, animated: true)
}
}
func loadAnalytics() {
guard ConsentManager.shared.hasConsent(.analytics) else {
showConsentRequired(for: .analytics)
return
}
// Analytics laden
}
}
```
#### 5.3.5 App Tracking Transparency (ATT) Integration
```swift
import AppTrackingTransparency
// Consent-SDK koordiniert mit ATT
ConsentManager.shared.configure(with: config) { manager in
// Nach Consent-Banner ATT-Dialog anzeigen
if manager.hasConsent(.marketing) {
ATTrackingManager.requestTrackingAuthorization { status in
switch status {
case .authorized:
manager.updateATTStatus(.authorized)
case .denied, .restricted:
manager.revokeConsent(.marketing)
case .notDetermined:
break
@unknown default:
break
}
}
}
}
```
### 5.4 Android SDK (Kotlin)
#### 5.4.1 Installation
```kotlin
// build.gradle.kts
dependencies {
implementation("eu.breakpilot:consent-sdk:1.0.0")
}
```
#### 5.4.2 Konfiguration
```kotlin
import eu.breakpilot.consent.ConsentManager
import eu.breakpilot.consent.ConsentConfig
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
val config = ConsentConfig.Builder()
.apiEndpoint("https://consent.example.com/api/v1")
.siteId("site_abc123")
.language(Language.GERMAN)
.categories(
Category.ESSENTIAL,
Category.FUNCTIONAL,
Category.ANALYTICS,
Category.MARKETING
)
.build()
ConsentManager.initialize(this, config)
}
}
```
#### 5.4.3 Jetpack Compose Integration
```kotlin
import eu.breakpilot.consent.compose.*
@Composable
fun MainScreen() {
val consent = rememberConsentState()
if (consent.needsConsent) {
ConsentBanner(
onAcceptAll = { consent.acceptAll() },
onRejectAll = { consent.rejectAll() },
onCustomize = { /* Öffne Einstellungen */ }
)
}
ConsentGate(
category = Category.ANALYTICS,
placeholder = { ConsentPlaceholder(Category.ANALYTICS) }
) {
AnalyticsContent()
}
}
```
#### 5.4.4 XML Views Integration
```kotlin
class MainActivity : AppCompatActivity() {
private val consent = ConsentManager.instance
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
consent.observe(this) { state ->
when {
state.needsConsent -> showConsentBanner()
state.hasConsent(Category.ANALYTICS) -> loadAnalytics()
}
}
}
private fun showConsentBanner() {
ConsentBannerFragment.show(supportFragmentManager)
}
}
```
### 5.5 Flutter SDK
```dart
import 'package:breakpilot_consent/breakpilot_consent.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await ConsentManager.initialize(
config: ConsentConfig(
apiEndpoint: 'https://consent.example.com/api/v1',
siteId: 'site_abc123',
language: Language.german,
categories: [
Category.essential,
Category.functional,
Category.analytics,
Category.marketing,
],
),
);
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ConsentProvider(
child: MaterialApp(
home: ConsentAwareHome(),
),
);
}
}
class ConsentAwareHome extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ConsentBuilder(
builder: (context, consent) {
if (consent.needsConsent) {
return ConsentBanner();
}
return Scaffold(
body: Column(
children: [
ConsentGate(
category: Category.analytics,
child: AnalyticsWidget(),
placeholder: ConsentPlaceholder(category: Category.analytics),
),
],
),
);
},
);
}
}
```
### 5.6 React Native SDK
```tsx
import {
ConsentProvider,
useConsent,
ConsentBanner,
ConsentGate,
} from '@breakpilot/consent-sdk-react-native';
const config = {
apiEndpoint: 'https://consent.example.com/api/v1',
siteId: 'site_abc123',
language: 'de',
categories: ['essential', 'functional', 'analytics', 'marketing'],
};
function App() {
return (
<ConsentProvider config={config}>
<NavigationContainer>
<MainStack />
</NavigationContainer>
<ConsentBanner />
</ConsentProvider>
);
}
function AnalyticsScreen() {
const { hasConsent } = useConsent();
if (!hasConsent('analytics')) {
return <ConsentRequired category="analytics" />;
}
return <AnalyticsContent />;
}
```
---
## 6. API-Spezifikation
### 6.1 Base URL
```
Production: https://consent.breakpilot.eu/api/v1
Staging: https://consent-staging.breakpilot.eu/api/v1
```
### 6.2 Authentifizierung
```http
# API-Key für Server-to-Server
Authorization: Bearer <api_key>
# HMAC für Client-SDKs (verhindert Manipulation)
X-Consent-Signature: sha256=<hmac_signature>
X-Consent-Timestamp: 1707000000
```
### 6.3 Endpoints
#### 6.3.1 Consent erstellen/aktualisieren
```http
POST /consent
Content-Type: application/json
{
"siteId": "site_abc123",
"userId": "user_xyz789", // Optional: Für eingeloggte Nutzer
"deviceFingerprint": "fp_...", // Anonymisierter Fingerprint
"consent": {
"categories": {
"essential": true,
"functional": true,
"analytics": false,
"marketing": false,
"social": false
},
"vendors": {
"google-analytics": false,
"facebook-pixel": false
}
},
"metadata": {
"userAgent": "Mozilla/5.0...",
"language": "de-DE",
"screenResolution": "1920x1080",
"platform": "web",
"appVersion": "1.0.0"
}
}
Response 201:
{
"consentId": "cns_abc123def456",
"timestamp": "2026-02-04T12:00:00Z",
"expiresAt": "2027-02-04T12:00:00Z",
"version": "1.0.0"
}
```
#### 6.3.2 Consent abrufen
```http
GET /consent?siteId=site_abc123&deviceFingerprint=fp_...
Response 200:
{
"consentId": "cns_abc123def456",
"consent": {
"categories": {
"essential": true,
"functional": true,
"analytics": false,
"marketing": false,
"social": false
},
"vendors": {}
},
"createdAt": "2026-02-04T12:00:00Z",
"updatedAt": "2026-02-04T12:00:00Z",
"expiresAt": "2027-02-04T12:00:00Z",
"version": "1.0.0"
}
Response 404:
{
"error": "consent_not_found",
"message": "No consent record found"
}
```
#### 6.3.3 Consent widerrufen
```http
DELETE /consent/{consentId}
Response 200:
{
"status": "revoked",
"revokedAt": "2026-02-04T13:00:00Z"
}
```
#### 6.3.4 Consent-Historie exportieren (DSGVO Art. 20)
```http
GET /consent/export?userId=user_xyz789
Response 200:
{
"userId": "user_xyz789",
"exportedAt": "2026-02-04T12:00:00Z",
"consents": [
{
"consentId": "cns_abc123",
"siteId": "site_abc123",
"consent": { ... },
"createdAt": "2026-01-01T12:00:00Z",
"revokedAt": null
},
{
"consentId": "cns_def456",
"siteId": "site_abc123",
"consent": { ... },
"createdAt": "2025-06-01T12:00:00Z",
"revokedAt": "2026-01-01T11:59:00Z"
}
]
}
```
#### 6.3.5 Site-Konfiguration abrufen
```http
GET /config/{siteId}
Response 200:
{
"siteId": "site_abc123",
"siteName": "Example Website",
"categories": [
{
"id": "essential",
"name": { "de": "Essentiell", "en": "Essential" },
"description": { "de": "...", "en": "..." },
"required": true,
"vendors": []
},
{
"id": "analytics",
"name": { "de": "Statistik", "en": "Analytics" },
"description": { "de": "...", "en": "..." },
"required": false,
"vendors": [
{
"id": "google-analytics",
"name": "Google Analytics",
"privacyPolicyUrl": "https://policies.google.com/privacy",
"cookies": [
{
"name": "_ga",
"expiration": "2 Jahre",
"description": "Unterscheidet Nutzer"
}
]
}
]
}
],
"ui": {
"theme": "light",
"position": "bottom",
"customCss": "..."
},
"legal": {
"privacyPolicyUrl": "https://example.com/datenschutz",
"imprintUrl": "https://example.com/impressum",
"dpo": {
"name": "Max Mustermann",
"email": "datenschutz@example.com"
}
},
"tcf": {
"enabled": true,
"cmpId": 123,
"cmpVersion": 1
}
}
```
#### 6.3.6 Consent-Statistiken (Admin)
```http
GET /admin/stats/{siteId}
Authorization: Bearer <admin_api_key>
Response 200:
{
"siteId": "site_abc123",
"period": {
"from": "2026-01-01",
"to": "2026-02-04"
},
"totalBannerViews": 150000,
"totalInteractions": 120000,
"interactionRate": 0.80,
"consentByCategory": {
"essential": { "accepted": 120000, "rate": 1.0 },
"functional": { "accepted": 95000, "rate": 0.79 },
"analytics": { "accepted": 45000, "rate": 0.38 },
"marketing": { "accepted": 25000, "rate": 0.21 },
"social": { "accepted": 30000, "rate": 0.25 }
},
"actions": {
"acceptAll": 35000,
"rejectAll": 40000,
"customize": 45000
},
"avgTimeToDecision": "4.2s",
"bounceAfterBanner": 0.05
}
```
### 6.4 Webhooks
```http
POST https://your-server.com/webhooks/consent
{
"event": "consent.updated",
"timestamp": "2026-02-04T12:00:00Z",
"data": {
"consentId": "cns_abc123",
"siteId": "site_abc123",
"userId": "user_xyz789",
"changes": {
"analytics": { "from": true, "to": false },
"marketing": { "from": false, "to": false }
}
}
}
// Event-Typen:
// - consent.created
// - consent.updated
// - consent.revoked
// - consent.expired
```
---
## 7. UI/UX-Anforderungen
### 7.1 Design-Prinzipien
1. **Gleichwertigkeit:** "Ablehnen" und "Akzeptieren" gleich prominent
2. **Transparenz:** Alle Informationen auf einen Blick
3. **Barrierefreiheit:** WCAG 2.1 AA konform
4. **Mobile-First:** Responsive Design
5. **Keine Dark Patterns:** Keine manipulativen Design-Elemente
### 7.2 Banner-Layouts
#### 7.2.1 Bottom Bar (Standard)
```
┌─────────────────────────────────────────────────────────────────────────┐
│ 🍪 Diese Website verwendet Cookies │
│ │
│ Wir nutzen Cookies und ähnliche Technologien, um Ihnen ein │
│ optimales Nutzererlebnis zu bieten. [Mehr erfahren] │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │
│ │ Alle ablehnen│ │ Einstellungen │ │ Alle akzeptieren │ │
│ └──────────────┘ └──────────────┘ └──────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
```
#### 7.2.2 Modal (DSGVO-empfohlen)
```
┌─────────────────────────────────────────────────────────────────────────┐
│ [X] │
│ 🍪 Datenschutzeinstellungen │
│ │
│ ───────────────────────────────────────────────────────────────────── │
│ │
│ Wir respektieren Ihre Privatsphäre. Bitte wählen Sie, welche │
│ Cookies Sie akzeptieren möchten. │
│ │
│ ☑ Essentiell (immer aktiv) ⓘ │
│ Notwendig für die Grundfunktionen der Website. │
│ │
│ ☐ Funktional ⓘ │
│ Personalisierung und Komfortfunktionen. │
│ │
│ ☐ Statistik ⓘ │
│ Hilft uns, die Website zu verbessern. │
│ │
│ ☐ Marketing ⓘ │
│ Personalisierte Werbung. │
│ │
│ ☐ Soziale Medien ⓘ │
│ Inhalte von sozialen Netzwerken. │
│ │
│ ───────────────────────────────────────────────────────────────────── │
│ │
│ [Datenschutzerklärung] [Impressum] [Cookie-Details] │
│ │
│ ┌──────────────────┐ ┌──────────────────────────┐ │
│ │ Auswahl speichern │ │ Alle akzeptieren │ │
│ └──────────────────┘ └──────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Alle ablehnen │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
### 7.3 Farbschema
```scss
// Light Theme
$consent-bg: #ffffff;
$consent-text: #1a1a1a;
$consent-secondary: #666666;
$consent-border: #e0e0e0;
$button-accept-bg: #2563eb; // Blau (neutral, nicht grün)
$button-accept-text: #ffffff;
$button-reject-bg: #ffffff; // Gleiche Prominenz!
$button-reject-text: #1a1a1a;
$button-reject-border: #2563eb;
$button-settings-bg: #f3f4f6;
$button-settings-text: #1a1a1a;
// Dark Theme
$consent-bg-dark: #1f2937;
$consent-text-dark: #f9fafb;
// ...
```
### 7.4 Barrierefreiheit (WCAG 2.1 AA)
| Anforderung | Umsetzung |
|-------------|-----------|
| **Kontrast** | Mindestens 4.5:1 für Text |
| **Fokus** | Sichtbare Fokus-Indikatoren |
| **Tastatur** | Vollständig per Tastatur bedienbar |
| **Screen Reader** | ARIA-Labels für alle Elemente |
| **Zoom** | Funktioniert bei 200% Zoom |
| **Motion** | Respektiert `prefers-reduced-motion` |
```html
<!-- Beispiel: Barrierefreier Button -->
<button
role="button"
aria-label="Alle Cookies akzeptieren"
aria-describedby="consent-description"
tabindex="0"
class="consent-accept-btn"
>
Alle akzeptieren
</button>
```
### 7.5 Animationen
```css
/* Respektiert Nutzereinstellung */
@media (prefers-reduced-motion: reduce) {
.consent-banner {
animation: none;
transition: none;
}
}
/* Dezente Einblendung */
@media (prefers-reduced-motion: no-preference) {
.consent-banner {
animation: slideUp 0.3s ease-out;
}
@keyframes slideUp {
from {
transform: translateY(100%);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
}
```
### 7.6 Mehrsprachigkeit
```typescript
const translations = {
de: {
title: 'Datenschutzeinstellungen',
description: 'Wir nutzen Cookies und ähnliche Technologien...',
acceptAll: 'Alle akzeptieren',
rejectAll: 'Alle ablehnen',
settings: 'Einstellungen',
saveSelection: 'Auswahl speichern',
categories: {
essential: {
name: 'Essentiell',
description: 'Notwendig für die Grundfunktionen.',
},
analytics: {
name: 'Statistik',
description: 'Hilft uns, die Website zu verbessern.',
},
// ...
},
footer: {
privacyPolicy: 'Datenschutzerklärung',
imprint: 'Impressum',
cookieDetails: 'Cookie-Details',
},
},
en: {
title: 'Privacy Settings',
// ...
},
fr: { ... },
es: { ... },
it: { ... },
nl: { ... },
pl: { ... },
// Alle EU-Sprachen
};
```
---
## 8. Plattform-Integrationen
### 8.1 Google Tag Manager
```javascript
// GTM-Template für Consent Mode v2
consent.on('change', (consentState) => {
gtag('consent', 'update', {
'ad_storage': consentState.marketing ? 'granted' : 'denied',
'ad_user_data': consentState.marketing ? 'granted' : 'denied',
'ad_personalization': consentState.marketing ? 'granted' : 'denied',
'analytics_storage': consentState.analytics ? 'granted' : 'denied',
'functionality_storage': consentState.functional ? 'granted' : 'denied',
'personalization_storage': consentState.functional ? 'granted' : 'denied',
'security_storage': 'granted', // Immer erlaubt
});
});
```
### 8.2 Meta (Facebook) Pixel
```javascript
consent.on('change', (consentState) => {
if (typeof fbq !== 'undefined') {
if (consentState.marketing) {
fbq('consent', 'grant');
} else {
fbq('consent', 'revoke');
}
}
});
```
### 8.3 Google Analytics 4
```javascript
// GA4 mit Consent Mode
consent.on('change', (consentState) => {
gtag('consent', 'update', {
'analytics_storage': consentState.analytics ? 'granted' : 'denied',
});
});
```
### 8.4 IAB TCF 2.2
```javascript
// TCF API Integration
window.__tcfapi = window.__tcfapi || function() {
(window.__tcfapi.a = window.__tcfapi.a || []).push(arguments);
};
consent.on('change', (consentState, tcfString) => {
// TC String an CMP-API übergeben
__tcfapi('setConsentInfo', 2, (success) => {
if (success) {
console.log('TCF Consent updated');
}
}, { tcString: tcfString });
});
```
### 8.5 WordPress Plugin
```php
<?php
/**
* Plugin Name: BreakPilot Consent
* Description: DSGVO-konformes Cookie-Banner
* Version: 1.0.0
* License: Apache-2.0
*/
function breakpilot_consent_enqueue() {
wp_enqueue_script(
'breakpilot-consent',
'https://cdn.breakpilot.eu/consent-sdk/v1/consent.min.js',
[],
'1.0.0',
true
);
wp_add_inline_script('breakpilot-consent', '
new ConsentManager({
apiEndpoint: "' . esc_js(get_option('breakpilot_api_endpoint')) . '",
siteId: "' . esc_js(get_option('breakpilot_site_id')) . '",
language: "' . esc_js(get_locale()) . '"
}).init();
');
}
add_action('wp_enqueue_scripts', 'breakpilot_consent_enqueue');
```
---
## 9. Datenspeicherung & Audit
### 9.1 Datenbank-Schema
```sql
-- Consent-Einträge
CREATE TABLE consent_records (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
site_id VARCHAR(50) NOT NULL,
user_id VARCHAR(100), -- Optional
device_fingerprint VARCHAR(64) NOT NULL, -- SHA-256 Hash
-- Consent-Daten
categories JSONB NOT NULL, -- { "analytics": true, ... }
vendors JSONB DEFAULT '{}', -- { "google-analytics": true }
tcf_string TEXT, -- IAB TCF String
-- Metadaten
ip_hash VARCHAR(64), -- Anonymisiert
user_agent TEXT,
language VARCHAR(10),
platform VARCHAR(20), -- web, ios, android
app_version VARCHAR(20),
-- Zeitstempel
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
expires_at TIMESTAMPTZ,
revoked_at TIMESTAMPTZ,
-- Versionierung
version VARCHAR(20) DEFAULT '1.0.0',
-- Indizes
CONSTRAINT fk_site FOREIGN KEY (site_id) REFERENCES sites(id)
);
CREATE INDEX idx_consent_site ON consent_records(site_id);
CREATE INDEX idx_consent_user ON consent_records(user_id) WHERE user_id IS NOT NULL;
CREATE INDEX idx_consent_device ON consent_records(device_fingerprint);
CREATE INDEX idx_consent_created ON consent_records(created_at);
-- Audit-Log (unveränderbar)
CREATE TABLE consent_audit_log (
id BIGSERIAL PRIMARY KEY,
consent_id UUID REFERENCES consent_records(id),
action VARCHAR(20) NOT NULL, -- created, updated, revoked
previous_state JSONB,
new_state JSONB,
ip_hash VARCHAR(64),
user_agent TEXT,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Kein UPDATE/DELETE auf Audit-Log
REVOKE UPDATE, DELETE ON consent_audit_log FROM PUBLIC;
```
### 9.2 Datenaufbewahrung
| Datentyp | Aufbewahrung | Begründung |
|----------|--------------|------------|
| **Consent-Record** | 3 Jahre | Nachweis der Einwilligung (Art. 7 Abs. 1 DSGVO) |
| **Audit-Log** | 6 Jahre | Steuerrechtliche Aufbewahrungspflicht |
| **Statistiken** | Unbegrenzt | Anonymisiert, kein Personenbezug |
| **Abgelaufene Consents** | 30 Tage | Grace Period für Sync-Konflikte |
### 9.3 Löschkonzept
```go
// Automatische Bereinigung
func (s *ConsentService) CleanupExpiredRecords(ctx context.Context) error {
// Soft-Delete für normale Records
_, err := s.db.ExecContext(ctx, `
UPDATE consent_records
SET deleted_at = NOW()
WHERE expires_at < NOW() - INTERVAL '30 days'
AND deleted_at IS NULL
`)
if err != nil {
return err
}
// Hard-Delete nach 3 Jahren
_, err = s.db.ExecContext(ctx, `
DELETE FROM consent_records
WHERE deleted_at < NOW() - INTERVAL '3 years'
`)
return err
}
```
### 9.4 Audit-Trail
Jede Consent-Änderung wird protokolliert:
```go
type AuditEntry struct {
ConsentID uuid.UUID `json:"consent_id"`
Action string `json:"action"` // created, updated, revoked
PreviousState *Consent `json:"previous_state"`
NewState *Consent `json:"new_state"`
IPHash string `json:"ip_hash"`
UserAgent string `json:"user_agent"`
Timestamp time.Time `json:"timestamp"`
}
func (s *ConsentService) logAudit(ctx context.Context, entry AuditEntry) error {
_, err := s.db.ExecContext(ctx, `
INSERT INTO consent_audit_log
(consent_id, action, previous_state, new_state, ip_hash, user_agent)
VALUES ($1, $2, $3, $4, $5, $6)
`, entry.ConsentID, entry.Action,
entry.PreviousState, entry.NewState,
entry.IPHash, entry.UserAgent)
return err
}
```
---
## 10. Sicherheit & Privacy
### 10.1 Datenschutz by Design
| Prinzip | Umsetzung |
|---------|-----------|
| **Datenminimierung** | Nur notwendige Daten speichern |
| **Pseudonymisierung** | Device-Fingerprint statt direkter Identifikation |
| **Zweckbindung** | Daten nur für Consent-Nachweis verwenden |
| **Transparenz** | Alle Datenverarbeitung dokumentiert |
### 10.2 Device Fingerprinting
```typescript
// Datenschutzkonformes Fingerprinting
function generateFingerprint(): string {
const components = [
navigator.userAgent,
navigator.language,
screen.colorDepth.toString(),
new Date().getTimezoneOffset().toString(),
// KEINE Canvas-Fingerprints oder Hardware-IDs
];
// SHA-256 Hash für Anonymisierung
return sha256(components.join('|'));
}
```
### 10.3 IP-Anonymisierung
```go
func anonymizeIP(ip string) string {
parsed := net.ParseIP(ip)
if parsed == nil {
return ""
}
if parsed.To4() != nil {
// IPv4: Letzte 8 Bit auf 0
// 192.168.1.123 -> 192.168.1.0
parsed[15] = 0
} else {
// IPv6: Letzte 80 Bit auf 0
for i := 6; i < 16; i++ {
parsed[i] = 0
}
}
// Hash für zusätzliche Sicherheit
return sha256(parsed.String())
}
```
### 10.4 Verschlüsselung
| Daten | Verschlüsselung |
|-------|-----------------|
| **In Transit** | TLS 1.3 |
| **At Rest** | AES-256-GCM |
| **Consent-Cookie** | HMAC-SHA256 signiert |
| **API-Keys** | bcrypt (Faktor 12) |
### 10.5 Rate Limiting
```go
// Pro IP-Adresse
var limiter = rate.NewLimiter(
rate.Every(time.Second), // 1 Request/Sekunde
10, // Burst von 10
)
// Pro Site
var siteLimiter = rate.NewLimiter(
rate.Every(time.Millisecond * 100), // 10 Requests/Sekunde
100, // Burst von 100
)
```
### 10.6 CORS-Konfiguration
```go
func corsMiddleware() gin.HandlerFunc {
return cors.New(cors.Config{
AllowOrigins: []string{"https://*.example.com"},
AllowMethods: []string{"GET", "POST", "DELETE"},
AllowHeaders: []string{"Content-Type", "X-Consent-Signature"},
AllowCredentials: true,
MaxAge: 12 * time.Hour,
})
}
```
---
## 11. Implementierungs-Roadmap
### Phase 1: Core SDK (8 Wochen)
| Woche | Aufgabe |
|-------|---------|
| 1-2 | Web SDK Basis (TypeScript) |
| 3-4 | Backend API (Go) |
| 5-6 | React/Vue Integration |
| 7-8 | Testing & Dokumentation |
**Deliverables:**
- Web SDK v1.0.0
- REST API v1.0.0
- React/Vue Wrapper
- Dokumentation
### Phase 2: Mobile SDKs (6 Wochen)
| Woche | Aufgabe |
|-------|---------|
| 1-2 | iOS SDK (Swift) |
| 3-4 | Android SDK (Kotlin) |
| 5-6 | Testing & Dokumentation |
**Deliverables:**
- iOS SDK v1.0.0
- Android SDK v1.0.0
- Beispiel-Apps
### Phase 3: Cross-Platform (4 Wochen)
| Woche | Aufgabe |
|-------|---------|
| 1-2 | Flutter SDK |
| 3-4 | React Native SDK |
**Deliverables:**
- Flutter SDK v1.0.0
- React Native SDK v1.0.0
### Phase 4: Enterprise Features (4 Wochen)
| Woche | Aufgabe |
|-------|---------|
| 1 | TCF 2.2 Integration |
| 2 | A/B Testing |
| 3 | Analytics Dashboard |
| 4 | White-Label UI |
**Deliverables:**
- TCF 2.2 Unterstützung
- Admin Dashboard
- A/B Testing Engine
### Phase 5: Integrationen (4 Wochen)
| Woche | Aufgabe |
|-------|---------|
| 1 | WordPress Plugin |
| 2 | Shopify App |
| 3 | GTM Template |
| 4 | Weitere CMS |
---
## 12. Anhang
### 12.1 Glossar
| Begriff | Beschreibung |
|---------|--------------|
| **CMP** | Consent Management Platform |
| **TCF** | IAB Transparency & Consent Framework |
| **DSGVO** | Datenschutz-Grundverordnung (EU 2016/679) |
| **TTDSG** | Telekommunikation-Telemedien-Datenschutz-Gesetz |
| **DSK** | Datenschutzkonferenz (Deutschland) |
| **PWA** | Progressive Web App |
| **ATT** | App Tracking Transparency (Apple) |
### 12.2 Referenzen
- [DSGVO Volltext](https://eur-lex.europa.eu/legal-content/DE/TXT/?uri=CELEX:32016R0679)
- [TTDSG Volltext](https://www.gesetze-im-internet.de/ttdsg/)
- [DSK Orientierungshilfe Telemedien](https://www.datenschutzkonferenz-online.de/media/oh/20211220_oh_telemedien.pdf)
- [IAB TCF 2.2 Specification](https://iabeurope.eu/tcf-2-0/)
- [EuGH Planet49 Urteil](https://curia.europa.eu/juris/liste.jsf?num=C-673/17)
- [BGH Cookie-Einwilligung II](https://www.bundesgerichtshof.de/SharedDocs/Pressemitteilungen/DE/2023/2023103.html)
- [WCAG 2.1](https://www.w3.org/TR/WCAG21/)
### 12.3 Lizenz
```
Copyright 2026 BreakPilot GmbH
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
```
### 12.4 Changelog
| Version | Datum | Änderungen |
|---------|-------|------------|
| 1.0.0 | 2026-02-04 | Initiale Spezifikation |
---
**Autor:** BreakPilot Engineering Team
**Review:** Datenschutzbeauftragter
**Freigabe:** Pending