rebreak-monorepo/docs/concepts/brand-token-matching.md
chahinebrini 7cc30db020 feat(blocker): VIP-Liste als Kachel-Grid + Brand-Token-Konzept
VIP-Liste-Sektion: zwei Kachel-Sektionen statt flacher Chips —
"Meine VIP-Domains" (eigene Custom-Domains, Stern + Status-Badge) und
"Vordefinierte Top-Seiten" (kuratiert, schlicht). Read-only, kein
Freigabe-Button. Kein Pulse-Ring (auf User-Wunsch entfernt).

docs/concepts/brand-token-matching.md: abgenommenes Konzept für geteiltes
Brand-Token-Matching (Layer 1 DNS + Mail/Mo) gegen den Nummern-Trick der
Gambling-Industrie (slotoro.bet → slotoro88.bet). Im Backlog.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 18:15:59 +02:00

7.9 KiB

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 winterwinterwin → 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 slotoro88slotoro MATCH (entnummeriert)
slotoro2.com slotoro2slotoro 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 slotoro88slotoro 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.tsDomainHasher.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.