Compliance

DSGVO

Login-lose Adresseingabe — der Sonderfall

Auch ohne User-Konto ist die Adresse ein personenbezogenes Datum im Sinne der DSGVO, sobald sie mit Kontextdaten (Kataster, Leerstand, Förderhinweise) verknüpft wird — sie lässt indirekt Rückschlüsse auf den Eigentümer zu.

Pflichten vor Phase-2-Launch:

  • Privacy-Notice klar sichtbar vor Eingabe
  • Opt-in-Checkbox („Ich verstehe, dass die Auswertung keine rechtsverbindliche Vermessung ersetzt")
  • AVV mit allen Sub-Prozessoren (Hosting, LLM, Geo-APIs)
  • Klarer Speicher-/Lösch­zyklus (z. B. anonymisierte Aggregate, Roh-Adresse max 30 Tage)
  • Rate-Limit zur Verhinderung von Mass-Scraping (10 / Tag pro IP)

Anonyme Sessions (anonymous_sessions, ADR-0026 / Issue #105)

/report ist login-los, der Anonymous→Registriert-Funnel braucht trotzdem eine wiedererkennbare Pseudo-Session, um beim späteren Login den bisherigen Report-Verlauf zum User zu migrieren. Stand 2026-05-21 aktiv (Issue #105 — vormals dokumentierter Folge-Schritt aus ADR-0026).

AspektUmsetzung
Cookieln_anon, HttpOnly, SameSite=Lax, Secure (in Prod), Path=/, Max-Age=90 Tage; gesetzt in proxy.ts beim ersten /report-Aufruf, wenn der Cookie fehlt
Token-Format32 Bytes Web-Crypto → 64 Hex-Zeichen (passt zu anonymous_sessions.cookie_token varchar(64) unique)
DB-Zeileanonymous_sessions(cookie_token, ip_hash, user_agent_hash, last_seen_at); UPSERT über cookie_token via POST /api/anon/touch (Node), Folge-Aufrufe aktualisieren nur last_seen_at
Pseudonyme Felderip_hash/user_agent_hash = SHA-256(value + AUTH_SECRET) (kein Klartext, dasselbe Muster wie rate_limits.ip_hash)
Conversionbeim ersten Post-Login (/go) setzt convertAnonymousByEmail converted_to_user_id; danach gehört die Zeile dem User
Retention (DSGVO)Hard-Delete nach 12 Monaten ohne Conversion (created_at < now() - interval '12 months' AND converted_to_user_id IS NULL). Konvertierte Zeilen bleiben — sie sind Teil der User-Historie und unterliegen der allgemeinen Account-Retention. Automatisiert via täglichem Vercel-Cron 30 3 * * * (/api/cron/anon-cleanup, ADR-0026 §3 / Issue #105) — analog zum reports-cleanup-Cron (runCron-Wrapper, cron_runs-Telemetry, Failure-Alert). Der Cron löscht ausschließlich converted_to_user_id IS NULL → konvertierte Zeilen bleiben unberührt.
Fail-openDB-Fehler beim Touch crashen /report nie (POST /api/anon/touch antwortet 204, console.error-Log); manipulierte Cookies (≠ 64-Hex) werden ignoriert, der nächste GET stellt einen frischen Cookie aus

API-Sicherheits-Hardening (ADR-0023)

MaßnahmeUmsetzung
Eingabe-CapsAdresse max 180 Zeichen + Control-Char-Strip; lat/lon nur innerhalb Österreich-BBox; JSON-Body max 2 KB (lib/api/guard.ts)
Scraping-Schutz GeoJSONKarten-Endpunkte (parcel-/widmung-geojson) jetzt rate-limitiert (eigener map:-Bucket, Default 300/Tag)
CORSbewusst offen für /api/v1/* (öffentliche, KI-nutzbare API) ohne Credentials; Schutz = Rate-Limit/Key, nicht Origin
HeaderX-Content-Type-Options: nosniff, X-Frame-Options: DENY global; Report-Antworten Cache-Control: private, no-store (keine Adress-Zwischenspeicher)
Pseudonyme LimitsRate-Limit speichert nur SHA-256(ip+secret), kein Klar-IP. Tagesbasierte Zähler (rate_limits(ip_hash, day), Migration 0006); alte Zeilen werden automatisiert via wöchentlichem Vercel-Cron 30 2 * * 0 (/api/cron/ratelimit-prune, DELETE … WHERE day < (now() - interval '90 days')::date) entfernt. Keine Compliance-Frist — pseudonyme Zähler, reine Wachstums-Begrenzung der Tabelle (runCron-Wrapper, cron_runs-Telemetry).
Permalink-IDs (für Report-Persistenz, ADR-0022)CSPRNG ≥128 Bit base62 — nicht enumerierbar (IDOR-Schutz); /r/{id} noindex; Retention expires_at Default 90 Tage (anonyme Reports), Hard-Delete automatisiert via täglichem Vercel-Cron 0 3 * * * (/api/cron/reports-cleanup, Issue #107). Konto-gebundene Reports (reports.user_id gesetzt — eingeloggt erstellt oder per ?claim=1 beansprucht, Migration 0021) haben expires_at = NULL und laufen nicht ab: sie sind Teil der User-Daten (Speicherung auf Wunsch des Nutzers, abrufbar unter „Meine Reports") und unterliegen der allgemeinen Account-Retention, nicht der 90-Tage-Frist. Der Cron löscht ausschließlich expires_at < now() → NULL-Zeilen bleiben unberührt. Anonyme Reports tragen optional reports.anon_token (= ln_anon-Cookie, Migration 0022) für den Funnel-Auto-Claim beim Login; der Token wird beim Claim auf NULL gesetzt (Datenminimierung) und verschwindet spätestens mit dem 90-Tage-Hard-Delete. Granulares Einzel-Löschen (DSGVO Art. 17 / Datenminimierung): Nutzer können einen einzelnen Konto-Report owner-scoped hard-löschen (DELETE /api/v1/me/reports/{id} bzw. „Löschen" auf /account/reports, … WHERE id AND user_id) — unabhängig von der kompletten Konto­löschung; ebenso Label/Notiz setzen (PATCH, Migration 0024). Beim Konto-Löschen werden die zugehörigen Reports mitentfernt (siehe nächste Zeile).
Recht auf Vergessenwerden (DSGVO Art. 17)Konto-Löschung über /account/delete (UI, Server-Action) und DELETE /api/v1/me (headless, AI-Parität) — Single Source lib/account/delete.ts. Erasure-Kaskade in FK-sicherer Reihenfolge: reports, memberships (nur eigene Zeilen löschen; auf fremden Zeilen nur invited_by nullen — sonst verlöre ein eingeladener Nutzer seinen Org-Zugang), invitations (selbst versendet), anonymous_sessions (konvertiert), verification_tokens (per E-Mail), users; accounts + sessions cascaden per DB-FK (ON DELETE CASCADE). Bestätigung durch erneute Eingabe der eigenen E-Mail (Schutz vor versehentlicher/automatischer Löschung). Guard: blockiert (409), solange der Nutzer alleiniger owner einer Organisation ist (Eigentum erst übertragen) — fail-closed bei DB-Fehler (im Zweifel NICHT löschen). Bewusst aufbewahrt (Art. 17 Abs. 3 lit. b, Rechtsansprüche): Betriebs-/Audit-Logs (z. B. impersonation_log, cron_runs) — keine FK auf users, enthalten keine Report-Inhalte.
Legacy /api/reportDeprecation/Sunset-Header gesetzt; Entfernung nach UI-Umzug auf v1

Datenminimierung im LLM-Pipeline

PflichtUmsetzung
Keine PII im LLM-PromptAdresse → Koordinate → Hash; Eigentümer-Name nie an LLM
Anonymisierung der GemeinderatsprotokolleNER-basierte Klarnamen-Entfernung vor RAG-Indexing
Retention der LogsAnthropic 7 Tage Abuse-Monitoring; eigene Logs ≤ 30 Tage
Aggregat-Daten dürfen nicht re-identifizierbar seinBenchmark-Daten nie auf Einzeladresse runter­brechen (z. B. „14 Brachflächen in dieser Gemeinde", nicht „diese eine Adresse")

Verzeichnis von Verarbeitungs­tätigkeiten (VVT)

Pflicht gemäß Art. 30 DSGVO. Eintrag pro Sub-Prozessor:

Sub-ProzessorZweckRechtsgrundlageRechenzentrum
Vercel (Hosting + Edge + AI Gateway)Hosting, Edge Functions, LLM-RoutingArt. 6 (1) (b) Vertragserfüllungglobal, EU-Region wählbar; AI Gateway routet pro Provider auf EU-Region
Neon (Postgres-Hosting)PostgreSQL + PostGIS + pgvectorArt. 6 (1) (b)EU (Frankfurt)
Cloudflare R2 (Object Storage)PDF-Reports, Förderrichtlinien, Fassaden-UploadsArt. 6 (1) (b)EU-Jurisdiction-Buckets (Western Europe). SCCs als Annex zum AVV — US-Muttergesellschaft, daher Schrems-II-Argumentation dokumentieren
Upstash RedisCacheArt. 6 (1) (b)EU-Region
Inngest (Background-Jobs)Workflow-Orchestrierung, Job-QueuesArt. 6 (1) (b)EU-Cloud-Region (eu.app.inngest.com); SCCs als Annex zum AVV
Sentry (Error-Tracking + APM)Error-Reporting, Performance-MonitoringArt. 6 (1) (f) berechtigtes InteresseEU-Region Frankfurt; PII-Scrubbing über beforeSend-Hook
BetterStack (Phase 3+ Logs + Uptime)zentrale Log-Aggregation, Status-PageArt. 6 (1) (f)EU-Region; AVV vor Aktivierung
Posthog (Phase 3+ Product Analytics)Funnel-Analyse, Cohort-TrackingArt. 6 (1) (a) Einwilligung (Cookie-Banner)EU-Cloud (eu.posthog.com); Session-Replay nur opt-in
StripePaymentsArt. 6 (1) (b)EU + global
Anthropic (LLM-Modelle)Standard- und Premium-Reports, Tool-UseArt. 6 (1) (b)über AWS Bedrock EU (Frankfurt) oder Vertex AI EU; SCCs
OpenAI (LLM-Modelle)Klassifikation, Embeddings, Standard-Reports (Alternative)Art. 6 (1) (b)EU-Region (direkte OpenAI EU-API); SCCs
Google (LLM-Modelle, Gemini)PDF-/Bild-Extraktion, KlassifikationArt. 6 (1) (b)Vertex AI EU-Region; SCCs
AWS (falls Bedrock aktiv für Anthropic)Hosting für Anthropic-ModelleArt. 6 (1) (b)Frankfurt
Komoot (Photon) (Geo-API)Adress-Autovervollständigung (Type-Ahead, login-loser Free-Pfad)Art. 6 (1) (b)Komoot GmbH (DE/EU); öffentliche Instanz photon.komoot.io. Eingabe wird nicht serverseitig gespeichert (no-store). Für das Ranking wird ein grober Standort-Bias mitgesendet (IP-Geo stadtgenau bzw. Browser-Geolocation auf ~1 km gerundet — keine hausgenaue Position), nicht gespeichert. AVV vor Launch zu klären, alternativ Self-Hosting (PHOTON_BASE_URL) für volle Datenkontrolle
OpenStreetMap Foundation (Nominatim) (Geo-API)Einzel-Geocoding (Report) + Suggest-FallbackArt. 6 (1) (b)OSMF (UK; EU-Angemessenheitsbeschluss UK). Nur Einzel-Suche — Autocomplete laut Usage-Policy untersagt; Eingabe nicht serverseitig gespeichert (no-store)

EU Data Residency (Schrems II)

Multi-Provider über Vercel AI Gateway

Mit Multi-Provider-Strategie (ADR-0004) muss jeder aktiv genutzte Provider einzeln EU-konform konfiguriert sein:

ProviderEU-Daten-ResidencyWie konfiguriert
Anthropic Claudeüber AWS Bedrock EU (Frankfurt) oder Vertex AI EUAI Gateway routet, Region pro Provider in Vercel-Settings
OpenAIdirekte EU-Region-API (verfügbar seit 2024)bei API-Aufruf EU-Region setzen; Gateway routet
Google GeminiVertex AI EU-Regionenüber AI Gateway oder direkt Vertex

Vercel AI Gateway sitzt davor und garantiert Zero Data Retention: Daten werden nicht protokolliert, nicht für Training verwendet, nicht länger als für die Antwort nötig zwischengespeichert.

Standardvertragsklauseln (SCCs)

Trotz EU Data Residency formaler Abschluss von:

  • AVV / DPA mit Vercel gemäß Art. 28 DSGVO (deckt Gateway ab)
  • AVV mit jedem aktiv genutzten Provider (Anthropic, OpenAI, Google) — auch wenn nur über Gateway aufgerufen
  • AVV mit AWS (falls Bedrock für Anthropic-Modelle verwendet)
  • SCCs als Annex zu allen AVVs (EU-Kommissions-Beschluss 2021/914)
  • Subprocessor-Liste aktuell halten (alle drei Provider publizieren das)

Training-Opt-out

Nicht nötig — API-Daten aller drei Provider (anders als Consumer-Pläne) werden default nicht für Modelltraining verwendet. Im AVV bestätigen lassen. Vercel AI Gateway leitet ohne Logging an die jeweilige API weiter.

Lizenz-Attribution

Pflichten pro CC-BY-4.0-Quelle

PflichtWo
Quellen-Block im Frontend (Footer-Komponente)Persistent, sichtbar auf jeder Seite
Datenquellen-Block in PDF-ReportsLetzte Seite oder Footer
/datenquellen-SeiteVollständige Liste mit Lizenz, Version, Last-Update
docs/DATA-SOURCES.md als Quelle der WahrheitMaintenance-Pflicht beim Einbinden neuer Quellen
Link zur Lizenzhttps://creativecommons.org/licenses/by/4.0/ mindestens auf der Datenquellen-Seite
Hinweis bei Änderungen„Abgeleitete Karte basierend auf basemap.at Orthofoto" o. Ä.

Was wir explizit NICHT dürfen

  • Endorsement implizieren (so tun, als ob basemap.at uns empfiehlt)
  • Attribution entfernen / verstecken (auch in Cache, API, PDF)
  • Strengere Lizenz darüberlegen („all rights reserved" auf einem CC-BY-Bündel)
  • DRM verwenden, das Lizenz­rechten widerspricht

Quellen-spezifische Lizenz-Gates

BEV Kataster

  • Anzeige und Lookup okay
  • Kommerzieller Planer-Export lizenzrechtlich kritisch — vor Phase 3 klären
  • Speichern, Anzeigen, Exportieren, kommerzielle Weitergabe sind getrennte Fragen

HORA / LFRZ

  • Reseller-Vertrag möglicherweise nötig für kommerzielle Nutzung
  • Workaround: WFS-Vektoren ziehen, eigene Intersection berechnen, keine WMS-Bilddaten reproduzieren
  • Status klären vor Phase-3-Launch

Google Street View · Bing Streetside

  • Maps Platform ToS verbieten ML-Training, Bulk-Download, dauerhaftes Caching > 30 Tage, Datenbank-Aufbau
  • Verstoß = API-Konto-Sperre + zivilrechtliche Schritte
  • Nicht für AI-Auswertung verwenden
  • Saubere Alternative: Mapillary (CC BY-SA) + eigene Fassaden-Erfassung

Microsoft Building Footprints (ODbL)

  • Kommerziell erlaubt
  • Share-Alike-Klausel auf abgeleitete Datensätze beachten — wenn wir aus Building Footprints eine eigene DB ableiten, muss die unter ODbL stehen

KPC / LEADER / Förderungen

  • Keine offizielle API
  • Web-Scraping verstößt gegen ToS mancher Portale
  • Sauberer Weg: RAG mit kuratierten PDF-Richtlinien, manuelle quartalsweise Validierung

Output-Disclaimer (in jedem Report)

Diese Auswertung dient ausschließlich der Information und ersetzt keine
rechtsverbindliche Vermessung, behördliche Auskunft oder fachliche
Prüfung durch Ziviltechniker oder Sachverständige. Datengrundlage:
[Auflistung der verwendeten Quellen mit Lizenz und Stand].

Diese Disclaimer-Zeile ist technisch erzwingbar über System-Prompt der LLM-Pipeline und HTML-Template — nicht optional.

Compliance-Tasks vor Live-Schaltung

  • AVV mit allen Sub-Prozessoren unterschrieben (Anthropic via Bedrock, AWS, Vercel, Neon, Cloudflare R2, Upstash, Stripe)
  • Cloudflare R2: EU-Jurisdiction-Bucket konfiguriert, SCCs als Annex zum AVV dokumentiert
  • Neon: EU-Region (Frankfurt), Backup-Strategie definiert
  • VVT-Eintrag vollständig
  • Geo-APIs: AVV/DPA mit Komoot (Photon) geklärt oder Self-Hosting (PHOTON_BASE_URL); Nominatim/OSMF-Nutzungsbedingungen dokumentiert (Autocomplete-Verbot eingehalten — läuft über Photon)
  • AWS Bedrock EU-Region konfiguriert
  • Privacy-Notice und Opt-in-Checkbox im Frontend
  • Attribution-Footer + /datenquellen-Seite live
  • Output-Disclaimer in jedem Report
  • Rate-Limit-Mechanismus aktiv
  • Logging mit Retention ≤ 30 Tage konfiguriert
  • HORA-Lizenz-Status geklärt
  • BEV-Export-Rechts­prüfung für Planer-Tier