# Konzept: Geteiltes Brand-Token-Matching Status: **Konzept abgenommen (2026-05-22) — im Backlog, Umsetzung später (§10)** Datum: 2026-05-22 Scope: Layer 1 (DNS-Sinkhole / PacketTunnel) + Mail-Scan (Mo). NICHT Layer 2. --- ## 1. Problem Glücksspiel-Marken umgehen exakte Blocklisten mit dem **Nummern-/Suffix-Trick**: dieselbe wiedererkennbare Marke, variierende Zahl/Suffix/TLD. ``` slotoro.bet → slotoro88.bet · slotoro2.com · slotoro-casino.net · slotorobet.io winrolla.com → winrolla57.com · winrolla-bet.net ``` - **Layer 1** (DNS-Hash-Set, 329k Hashes): matcht nur **exakt**. Hashes erlauben kein Substring-/Prefix-Matching — `slotoro88.bet` hasht zu etwas völlig anderem als `slotoro.bet`. Architektur-bedingt. - **Layer 2** (Apple `ManagedSettings`-`WebDomain`): matcht nur exakte Domain + Subdomains. Keine Substring-API. **Nicht erweiterbar** — out of scope. - **Mail-Scan (Mo)**: hat dasselbe Problem (`winrolla → winrolla57`). Die HaGeZi-Feed-Liste fängt viele Varianten mit **Latenz** — der Feed muss die neue Variante erst aufnehmen. Brand-Token-Matching schließt genau diese Latenzlücke und greift sofort bei jeder Variante einer **bekannten** Marke. --- ## 2. Ziel & Scope **Ziel:** Ein einziges, geteiltes Brand-Token-System — dieselbe kuratierte Token-Liste + derselbe Matching-Algorithmus für Layer 1 (DNS) und Mail-Scan. **Im Scope:** Varianten **bekannter** Marken abfangen. **Nicht im Scope:** - Brandneue Marken ohne Token (kein Token → kein Match). - Layer 2 (Apple-API kann es nicht). - Ersatz für Feed/Blocklist — das System **ergänzt** sie, ersetzt sie nicht. --- ## 3. Kernidee: der Brand-Token Ein **Brand-Token** = ein distinktiver, kleingeschriebener Marken-Kern, der eine Glücksspielmarke identifiziert und in legitimen Domains praktisch nicht vorkommt. ``` Gute Tokens: slotoro · winrolla · spinrollz · wazamba Schlechte Tokens: bet · win · play · casino · slot · vegas (Wörterbuch-/Generikwörter → False-Positive-Magneten) ``` Regel: **kuratiert, nie automatisch.** Mindestlänge **≥ 5 Zeichen**. Kürzere Marken bleiben bei der exakten Blocklist. --- ## 4. Der Matching-Algorithmus (das geteilte Stück) Der entscheidende Teil. **Kein freies Substring** (`host.contains(token)` ist gefährlich). Stattdessen **Entnummerierung + Segment-Exaktvergleich**: ### Ablauf (pro Host) ``` 1. Host in Labels splitten an '.' slotoro88.bet → ["slotoro88", "bet"] 2. Pro Label: in Segmente splitten an '-' play-slotoro → ["play", "slotoro"] 3. Pro Segment: a. Trailing-Ziffernlauf abschneiden → `core` slotoro88 → slotoro b. core EXAKT in tokenSet? → MATCH c. sonst: core beginnt mit einem Token UND der Rest ∈ genericSuffixSet? → MATCH d. sonst → kein Match 4. Kein Segment matcht → kein Block. ``` `genericSuffixSet` = generische Glücksspiel-Anhängsel, die nur als **Rest nach einem Token** zählen (nie standalone): `bet bets casino casinos slot slots spin spins win wins play vegas game games poker sport sports wetten lotto gambling`. ### Warum das robust ist Es ist **Exaktvergleich auf dem entnummerierten Segment**, kein Substring. Ein kurzes Token wie `win` würde `winter` NICHT treffen: `winter` → core `winter` → `winter` ≠ `win` → kein Match. False Positives entstehen nur, wenn ein legitimes Segment **exakt** ein Token ist — bei distinktiven Tokens ~null. ### Beispiele | Host | Segment-core | Ergebnis | |---|---|---| | `slotoro.bet` | `slotoro` | MATCH (exakt) | | `slotoro88.bet` | `slotoro88`→`slotoro` | MATCH (entnummeriert) | | `slotoro2.com` | `slotoro2`→`slotoro` | MATCH | | `play-slotoro.net` | Segment `slotoro` | MATCH | | `slotoro-casino.io` | Segment `slotoro` | MATCH | | `slotorobet.com` | `slotorobet` = `slotoro`+`bet` | MATCH (Token+Suffix) | | `stat.slotoro88.bet` | Label `slotoro88`→`slotoro` | MATCH | | `winter.com` | `winter` | kein Match (≠ Token) | | `slotorox.com` | `slotorox` = `slotoro`+`x`, `x`∉Suffixe | kein Match | ### Verbindlichkeit Der Algorithmus muss **bit-identisch** in Swift (PacketTunnel) und TS (Mo-Scan + Backend-Validierung) implementiert sein — analog zu `domainHash.ts` ↔ `DomainHasher.swift`. Ein gemeinsamer Spec-Testvektor-Satz (Input-Host → erwartetes Ergebnis) wird in beiden Test-Suites geprüft. --- ## 5. Token-Liste: Kuration & Quelle - **DB-Tabelle** `brand_tokens` (token, source, addedAt, isActive, note). - **Kuratiert von ReBreak-Admins** — nie auto-generiert (FP-Risiko). - **Bootstrap:** die existierende 329k-Blocklist nach gemeinsamen SLD-Präfix- Clustern minen → Token-Vorschläge → menschliche Freigabe. - **Laufende Quelle:** wenn ein User über die Custom-Domain-/Submission-Flows eine offensichtliche Variante einreicht, kann der Admin statt nur der Domain das **Brand-Token** promoten. - **Allowlist-Notausgang** (`brand_token_allowlist`): falls ein Token doch kollidiert, einzelne legitime Domains explizit ausnehmen. v1 optional, im Design vorgesehen. --- ## 6. Verteilung aufs Gerät (Layer 1) - Neuer Endpoint `GET /api/url-filter/brand-tokens` — JSON/Zeilenliste, ETag- gecacht. Spiegelt exakt den `blocklist.bin`-Sync-Mechanismus: App lädt → schreibt in App-Group → Darwin-Notification → Extension reloadet. - Datei klein (~wenige KB bei einigen hundert Tokens). - **File-Protection `.completeUntilFirstUserAuthentication`** — wie nach dem Layer-1-Bugfix (2026-05-22), damit die Extension sie auch bei gesperrtem Gerät lesen kann. - **Privacy-Abwägung (ehrlich):** die Hash-Liste ist gehasht, damit keine Klartext-Casino-Domains auf dem Gerät eines Spielers liegen. Brand-Tokens sind Klartext-Markenfragmente — Substring-Matching gegen Hashes ist unmöglich, Klartext ist also nötig. Es sind aber nur einige hundert Markenfragmente, kein browsing-history-verräterischer Datensatz. Datei file-protected. **Entscheidung (2026-05-22): Klartext akzeptiert.** `hans-mueller`-DSGVO-Gegencheck bei der Umsetzung (kein Blocker fürs Konzept). --- ## 7. Integration ### Layer 1 — `DnsFilter.classify` Nach dem `hashList.matchesAnySuffix(domain)`-**Miss**, vor `.forward`: ``` if hashList-Miss: if brandTokens.matches(domain): // der Algo aus §4 return .block return .forward ``` Neue Klasse `BrandTokenList` (Pendant zu `HashList`) — lädt die Token-Datei, reloadet via Darwin-Notification. Performance: pro verfehlter Query ein paar Set-Lookups → Mikrosekunden, NE-Memory-unkritisch. ### Mail (Mo) Derselbe `brandTokens.matches(...)` auf die Absender-Domain (und optional den Display-Namen) anwenden. Owner der Integration bleibt Mo; das geteilte Stück ist die Token-Liste + der §4-Algorithmus. --- ## 8. Was es NICHT tut (ehrliche Grenzen) - Keine **neuen** Marken (ohne Token kein Match). - Glued-Varianten ohne Ziffern/Trenner/Generik-Suffix (`slotoroxyz.com`) → Miss; bleibt Sache des Feeds. - Kein Layer-2-Support. - False Positives werden stark reduziert, nicht zu 100% eliminiert → Allowlist. --- ## 9. Entscheidungen **Entschieden (2026-05-22):** 1. **Privacy:** Klartext-Brand-Tokens auf dem Gerät akzeptiert, file-protected. `hans-mueller`-DSGVO-Gegencheck bei der Umsetzung (kein Blocker fürs Konzept). 2. **Generic-Suffix-Regel (§4c):** drin — v1 enthält Token+Suffix-Matching (`slotorobet` = `slotoro`+`bet`). **Noch offen (bei Umsetzung klären):** 3. **Token-Kuration:** rein Admin, oder community-/submission-gespeist? 4. **Sync-Kanal:** eigener Endpoint, oder in den `blocklist.bin`-Sync bündeln? 5. **Bootstrap-Aufwand:** Mining der 329k-Liste nach Präfix-Clustern — wie viel manuelle Freigabe ist realistisch? --- ## 10. Rollout-Phasen - **Phase 1:** DB-Tabelle + §4-Algo-Spec + Testvektoren + Bootstrap-Mining + Admin-Kuration-UI. - **Phase 2:** Endpoint + Sync + `BrandTokenList` + `DnsFilter`-Einbau (Layer 1). - **Phase 3:** Mo-Mail-Integration. - Hinter Feature-Flag, mit FP-Report-Pfad zum Monitoring.