CI (frozen-lockfile) brach seit 2 Commits: committed package.json hatte
expo-blur entfernt, pnpm-lock.yaml aber behalten → ERR_PNPM_OUTDATED_LOCKFILE,
Deploy blieb auf fd44687 (STT/TTS/Coach-Fixes nie live). expo-blur wird für
iOS-Vibrancy (SearchBarFloating) wieder gebraucht → package.json + Lockfile
konsistent mit expo-blur. Android nutzt platform-conditional Fallback.
Trägt zusätzlich den Version-Sync 0.3.6 → 0.3.13 (zied Versions-Schema).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
TTS-Sprache war provider-übergreifend hart auf "de" verdrahtet, locale aus dem
Request wurde ignoriert → arabischer Text wurde deutsch-phonetisch gesprochen.
- locale aus Body auslesen → Basis-Sprachcode an alle Provider
- Pro: Cartesia sonic-2 → sonic-3 (sonic-2 kann kein Arabisch; sonic-3 = 42 Sprachen)
- Legend: ElevenLabs language_code gesetzt (turbo_v2_5 multilingual, ar dabei)
- Google-Fallback: BCP-47-Map (ar→ar-XA etc.), de-Voice nur noch für de
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
nova-2 unterstützt kein ar/tr → Deepgram 400 "No such model/language/tier
combination" → leeres Transcript ("kein Text nach Speech"). nova-3 deckt alle
gelisteten Sprachen als diskrete Codes ab (de/en/tr/ar/fr/es/pt/it), ohne
Regression. Verifiziert gg. Deepgram models-languages-overview.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Neue Tabelle `global_mail_display_names` für admin-kuratierte Glücksspiel-Marken.
Mo's mail-classifier try/catch-Fallback wird damit produktiv (Display-Name-Layer 2.5).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Task 1 — Junk-Folder Fix:
- noopTimer (alle 2min) ruft jetzt triggerScan(conn) fire-and-forget auf
- Outlook/Hotmail-Mails die direkt in "Junk Email" landen werden damit
innerhalb von max. 2min erfasst (IDLE hört nur INBOX, kein exists-Event)
- Consent-Guard analog exists-Event: nur wenn conn.consentAt gesetzt
Task 2 — Layer 2.6 global Display-Name-Patterns:
- getMailDisplayNamePatterns(userId) neu in db/domains.ts:
lädt aus global_mail_display_names (admin-curated, pending Migration)
+ user_custom_domains type=mail_display_name (backward-compat)
mit try/catch-Fallback bis Schema-Migration deployed ist
- getCustomMailDisplayNames() als @deprecated markiert (bleibt für Übergangszeitraum)
- scan-internal.post.ts: Import auf getMailDisplayNamePatterns umgestellt
- mail-classifier.ts: Layer 2.6 Kommentar von "dead code" auf "live v1.1" aktualisiert
Schema-Migration (global_mail_display_names) ist Aufgabe von rebreak-backend.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Bestehender OAuth Client `Rebreak Android Debug` mit Android-Type
verlangte zusätzlichen "Enable Custom URI scheme"-Toggle und passte
strukturell nicht zum iOS-only-Targeting. Neuer iOS-Client angelegt
mit Bundle-ID org.rebreak.app — Reverse-Client-ID-Redirect-URI
funktioniert out-of-the-box ohne Console-Toggle.
Infisical-Secret GOOGLE_OAUTH_CLIENT_ID wurde parallel auf neue
iOS-Client-ID aktualisiert (staging + prod).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Google iOS-OAuth-Client lehnt `rebreak://...`-Schemes mit
`invalid_request` ab. Reverse-Client-ID-Format ist required.
Empirisch verifiziert 2026-05-28 (siehe memory).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
MDM-VPN-Pivot (Phase F.2 done):
- ops/mdm/profiles/rebreak-iphone-protection.mobileconfig auf v5 mit
com.apple.vpn.managed Payload + OnDemandUserOverrideDisabled. iPhone-User
kann ReBreak-VPN-Profile nicht entfernen und "Bedarf verbinden"-Toggle
ist disabled. allowEnablingRestrictions empirisch widerlegt für FC-Toggle-
Lock — out.
- DEV-removable Variante als Test-Profile dazu.
- Bootstrap-Tool (rebreak-supervise.sh) + Supervision-Identity-Setup-Doc.
- PHASES.md updated mit empirischen Befunden.
App-side MDM-Detect (Pfad-a Banner-Logic):
- modules/rebreak-protection: getDeviceState() returnt mdmManaged via
Heuristik NETunnelProviderManager.count > 1 (App selbst kann nur einen
eigenen erstellen, MDM-Push fügt einen zweiten hinzu).
- DeviceLayers.mdmManaged?: boolean Type.
- blocker.tsx: lockedIn-Bedingung erweitert um mdmManaged. Bei MDM-managed
iPhones wird der App-Lock-Card (FC-Authorization-Toggle UI) ausgeblendet
weil der per-App FC-Toggle nicht lockbar ist und durch den MDM-VPN-Layer
redundant.
Layer-2-Country-Curated-Pivot:
- backend: vip-swap.post.ts raus, suggest.post.ts rein. Curated-domains
durch admin (separate Tabelle/Pfad), getrennt von User-Custom-Domains.
- Admin-APIs für curated-domain Pflege (index.get + [id].patch).
- seed-country-blocklists Script für initiale Curated-Domain-Liste.
- protection/webcontent-domains.get refactored für Country-Curated-Pfad.
- Migration drop_vip_swap_fields.sql + schema.prisma adjusted.
- docs/concepts/layer2-country-pivot.md mit Architektur + Decision-Trail.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Der VpnService lädt die Blockliste bei onStartCommand(START). Ist
blocklist.bin beim ersten Aktivieren noch nicht gesynct → 0 Hashes.
syncBlocklist schickt zwar ACTION_RELOAD, aber via ctx.startService(),
das Android 8+ als Background-Start still verwerfen kann → Filter bleibt
auf 0 Hashes bis Geräte-Neustart: VPN aktiv, aber nichts geblockt.
scheduleBlocklistSelfHeal: lädt hashList nach 2/5/15/30/60s erneut bis
Hashes da sind (max 30 Versuche). Greift unabhängig vom ACTION_RELOAD-
Intent. Analog zum iOS-PacketTunnel-Self-Heal.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
In der "Vordefinierte Top-Seiten"-Sektion der VIP-Liste ein
"Seite vorschlagen"-Link → SuggestCuratedSheet: Domain-Eingabe →
POST /api/curated-domains/suggest (Land via Geräte-Region). Response-
Handling: Erfolg / schon vorgeschlagen / approved / rejected / ungültig.
- useCuratedSuggest.ts (neu), SuggestCuratedSheet.tsx (neu)
- VipDomainList.tsx: Suggest-Link in der curated Sub-Sektion + Sheet-State
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
User können länderspezifische Glücksspielseiten für die kuratierte
VIP-Layer-2-Liste vorschlagen — wichtig für Länder mit kurzer
Starter-Liste (z.B. TN).
- Schema: CuratedDomain (domain, country, status, suggestedByUserId);
Migration 20260522_curated_domains
- webcontent-domains.get.ts komponiert jetzt JSON-Basis + DB-approved
Curated-Domains pro Land
- POST /api/curated-domains/suggest legt einen suggested-Eintrag an
Admin-Approve (Endpoint + Admin-App-View) folgt als nächster Block.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
TN-User fielen bisher mangels TN-Liste auf die DE-Liste zurück. Jetzt
eigene (kurze) TN-Starter-Liste: mbet216.com, 2xbet365.com, cesar365.com,
icombet.com, unibet365.net (von einem TN-Test-User gemeldet).
TN in COUNTRY_KEYS (webcontent-Endpoint) + VIP_COUNTRIES (Geräte-Region-
Auflösung + Add-Check). Native Region-Logik ist generisch (Locale.region
→ JSON-Key) — kein Native-Code nötig. gambling-domains.json _meta v3.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Der setTimeout(80)+onImageLoad-Ansatz war ein Timing-Hack gegen ein
strukturelles Problem (lazy Item-Measurement unter Fabric -> scrollToEnd
landet zu kurz). Stattdessen jetzt inverted FlatList: Index 0 sitzt
permanent am Bildschirmrand, neueste Nachricht immer sichtbar.
- dm.tsx: inverted + reversedMessages, Gruppen-Logik gespiegelt,
manuellen Auto-Scroll + keyboardHeight-State entfernt
- ChatBubble.tsx: onImageLoad-Prop entfernt (obsolet)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Der VipSwapSheet wurde im selben Tick geöffnet wie der AddDomainSheet
dismisst — iOS verschluckt dann das zweite Modal, der Swap-Dialog kam
nie sichtbar. 320ms-Delay (Muster wie fromDetailsToExplainer).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
⚠️ TEMP — damit der VIP-Swap-Dialog schon ab der 4. Web-Domain triggert
statt erst ab 31. NACH DEM TEST auf 30 zurücksetzen.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- useCustomDomains: CustomDomain um vipDeferUntil/vipEvictAt, AddDomainResult
um vipFull/newDomainId; addDomain liefert vipFull durch; submitVipSwap()
- VipSwapSheet (neu): Dialog wenn VIP voll — User wählt eine eigene Domain,
die in 24h ersetzt wird
- VipDomainList: Badge „wird in Xh ersetzt" auf der ersetzten Kachel
- blocker.tsx: vipFull → AddDomainSheet zu, VipSwapSheet auf
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Wenn die VIP-Liste (Layer 2) voll ist (>30 eigene Web-Domains) und der
User eine neue Custom-Domain hinzufügt, ersetzt er bewusst eine
bestehende — der Tausch greift in der VIP erst nach 24h Cooldown.
- Schema: UserCustomDomain.vipDeferUntil + vipEvictAt
(Migration 20260522_add_vip_swap_fields, additiv + nullable)
- getWebCustomDomains: filtert deferred (noch nicht in VIP) + evicted
(Cooldown durch → raus) — lazy ausgewertet, kein Cron
- POST /api/custom-domains: neue Web-Domain über dem 30er-Cap → wird
zurückgestellt (vipDeferUntil gesetzt), Response-Flag vipFull
- POST /api/custom-domains/vip-swap: setzt effectiveAt = jetzt+24h auf
neue + ersetzte Domain
- Layer 1 bleibt unberührt — die neue Domain ist dort sofort aktiv
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Bug 1 — Chat-Liste: RefreshControl nutzte React-Querys `isRefetching`,
das bei JEDEM Background-Refetch (focus-/stale-getriggert) true wird →
nach Zurück-Navigation hing der Pull-to-Refresh-Spinner endlos. Fix:
eigener `userRefreshing`-State, nur bei explizitem Pull-to-Refresh true,
im finally zurückgesetzt.
Bug 2 — Conversation scrollte nicht bis zur letzten Nachricht, wenn die
ein Bild war: onContentSizeChange-scrollToEnd feuerte vor dem Bild-Load.
Fix: ChatBubble bekommt onImageLoad-Callback, die letzte Bild-Nachricht
triggert nach dem Laden erneut scrollToBottom.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Neue RedirectTestCard im Debug-Screen mit zwei Buttons:
- Kontrolle: tipico.de direkt öffnen
- Test: httpbin-302-Redirect → tipico.de
Spielt den Casino-Mail-Fall nach (erlaubter Zwischen-Host → 302 →
blockierte Domain), um zu prüfen ob der DNS-Filter die Zieldomain auch
nach einem Redirect noch sinkholet. Frontend-only.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
startTunnel lädt blocklist.bin manchmal mit 0 Hashes (Datei wegen
Data-Protection bei gesperrtem Gerät noch nicht lesbar). Bisher blieb
Layer 1 dann tot bis zum nächsten App-Sync — in den Geräte-Logs als
~30-Min-Fenster mit 0 Hashes sichtbar (z.B. 16:22 startTunnel→0,
erst 16:55 per Darwin-Reload geheilt).
scheduleBlocklistRetryIfEmpty: lädt nach 3/10/30/60/120/300s erneut,
bis Hashes da sind (max 20 Versuche). Sobald das Gerät entsperrt ist,
wird die Datei lesbar → Self-Heal greift, ohne auf einen App-Sync zu
warten.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Der Test prüfte die alte Per-Bucket-Logik (web/mail getrennt) + Free-Tier.
Angepasst an den 10/20-Pool-Refactor: customDomains als Zahl, Pro 10 /
Legend 20, gemeinsamer web+mail-Pool, LIMIT_REACHED, GET liefert
{ items, count, limit }. Free-Tier-Fälle entfernt. 45/45 grün.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- PostCard: Bilder mit borderRadius 10 + overflow:hidden — Ecken wieder rund
- dm.tsx: myUserId synchron aus useAuthStore statt async getSession —
behebt den hängenden Lade-Spinner beim Zurück aus einer Conversation
(async getSession-Fenster auf jedem Mount → enabled-Flackern der Query)
- ChatBubble: expo-image memory-disk-Cache + 200ms-Transition für
smootheres Bild-Laden
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Custom-Domain-Slots sind jetzt EIN gemeinsamer Pool für web + mail
(Pro 10 / Legend 20) statt getrennter web/mail-Buckets. Free-Tier ist
entfallen — PLAN_LIMITS hat nur noch pro + legend, getPlanLimits
defaultet auf pro.
Backend:
- plan-features: customDomains ist eine Zahl (CustomDomainLimits weg)
- index.post: Slot-Check gegen Gesamt-Count, Fehler einheitlich LIMIT_REACHED
- index.get: liefert { items, count, limit }
- change-preview + coach/message an die neue Form angepasst
Frontend:
- useCustomDomains: count/limit (Zahlen) statt countsByType/limits
- AddDomainSheet: ein generischer Limit-Hinweis (error_limit_reached)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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>
Layer 1 (PacketTunnel-DNS-Sinkhole) blockte nichts: `startTunnel` lud
0 Hashes, obwohl `blocklist.bin` 330k Hashes hatte. Ursache: `syncBlocklist`
setzte `URLFileProtection.complete` — die Datei ist damit bei GESPERRTEM
Gerät unlesbar. Der PacketTunnel wird OS-getrieben (on-demand) auch während
Lock-Phasen neu gestartet → `open()` schlägt fehl → 0 Hashes → Layer 1 tot
bis zum nächsten App-Sync.
Beweis aus den Extension-Logs: `blocklist reloaded` (Darwin-Notif nach
App-Sync, Gerät entsperrt) = 329k-330k Hashes; `startTunnel → blocklist
geladen` = 0 — dieselbe Datei, dieselbe Path.
Fix: `.complete` → `.completeUntilFirstUserAuthentication` (blocklist.bin +
webContent-Cache). Bleibt at-rest verschlüsselt (DiGA-konform), ist aber nach
dem ersten Entsperren seit Boot lesbar — die korrekte Klasse für eine Datei,
die eine Network-Extension 24/7 lesen muss.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Blocker-UI:
- FilterTile: Trash-Button → status-aware Freigabe-Button (Freigeben/Erneut/
in-Prüfung); RemoveDomainSheet entfernt — kein Domain-Entfernen mehr in der UI
- VIP-Liste landabhängig: zeigt die komponierte Endpoint-Liste statt nur
eigener Customs; Land über Geräte-Region (expo-localization)
- VIP-Realtime: refetch bei Domain-Add/Approve/Reject, pulsierender Ring
für neue/active/submitted Chips
VIP-Komposition (webcontent-domains):
- Hybrid: Customs auf 30 gekappt, 20 Slots fest für die kuratierte Top-Liste
reserviert — Customs können die Top-Gambling-Domains nicht verdrängen
Add-Check (custom-domains POST), für web reaktiviert — 3 Fälle gegen
Layer 1 (global) + Layer 2 (kuratierte VIP):
- weder global noch kuratiert → normaler active-Eintrag
- global + kuratiert → alreadyProtected, kein Slot
- global, nicht kuratiert → inGlobalNotVip; per addToVip als status=approved
speicherbar (kein Slot, nur VIP-Liste)
DE-Gambling-Liste 30→36, nach Relevanz sortiert (erste 20 = reservierte Plätze)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Die Warte-Schleife lief nur solange status==.connecting. Direkt nach
startVPNTunnel() steht der Status aber oft noch kurz auf .disconnected →
Schleife uebersprungen → sofort false-negative 'tunnel_not_connected',
obwohl der Tunnel Sekundenbruchteile spaeter sauber connected.
Jetzt: pollen bis .connected oder .invalid oder ~6s Timeout.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
K2: forwardPacket schreibt jetzt bei jedem Upstream-Fehler eine synthetische
SERVFAIL ins TUN (zentrale finish(gotAnswer:)) — DNS-Client haengt nicht mehr.
H1: harter In-Flight-Cap (32) statt Connection-pro-Query — ueber dem Cap sofort
SERVFAIL; deckelt den NE-Memory-Footprint.
K1: buildErrorResponse + dnsQuestionEnd schneiden das Paket hinter der Question-
Section ab (EDNS-OPT-Muell weg), IP/UDP-Length neu berechnet.
H3: Compression-Pointer-QNAME → fail-open (.forward) statt stillem Drop.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Meine Filter (unified web + mail_domain, Typ-Badge pro Kachel, Slot-Pool-
Zaehler) + collapsible VIP-Liste (Zweitschutz-Beschreibung, read-only). Die
3 redundanten Web-Bloecke (CustomFilterOverview + DomainSection Eigene Domains)
entfernt. Slot-Pool rein frontend (limits.web+limits.mail), kein Backend noetig.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Bug: die RebreakPacketTunnelExtension.appex wurde nach
ReBreak.app/Extensions/ statt PlugIns/ embedded → Install-Crash
AppexBundleMissingEXAppExtensionAttributesDict (klassische NSExtension
im ExtensionKit-Ordner).
Ursache (verifiziert gegen node_modules/xcode + E2E-Lauf der Plugin-Kette):
withXcodeProject-Mods laufen LIFO — withPacketTunnelTarget zuerst,
withExtensionTarget (NEURLFilter) danach. proj.addTarget() bettet die
.appex über buildPhaseObject() ein, das projektweit die erste
PBXCopyFilesBuildPhase mit Section-Comment "Copy Files" matcht. Der
NEURLFilter-Umbau ändert nur phase.name/dstSubfolderSpec, nicht den
Section-Comment → die PacketTunnel-.appex landet in der dst-16-Phase
des NEURLFilter-Targets.
Fix in withPacketTunnelTarget: die .appex aus allen Copy-Files-Phasen
herausziehen, exklusiv in die eigene dst-13-Phase legen UND deren
Section-Comment auf "Embed App Extensions" umbenennen — damit der
nachfolgende NEURLFilter-addTarget-Lookup die Phase nicht mehr matcht.
NEURLFilter-Code unangetastet (dessen Umbau matcht per .appex-Dateiname).
Verifiziert: frisches Projekt, vorhandenes URLFilter-Target, Idempotenz.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Neuer iOS-Layer-1-Filter: ein NEPacketTunnelProvider-DNS-Sinkhole — MDM-frei,
ab iOS 16, Parität zum Android-VPN-DNS-Filter. Ersetzt den Apple-seitig
blockierten NEURLFilter als Default. NEURLFilter-/PIR-Code bleibt inaktiv als
iOS-26-Upgrade-Pfad erhalten (User-Entscheidung).
Neues Extension-Target RebreakPacketTunnelExtension/:
- PacketTunnelProvider.swift — TUN-Setup (virtuelle DNS-IP 10.0.0.1, nur diese
Route ins TUN), Read-Loop, NXDOMAIN-Sinkhole, Upstream-Forward via
NWConnection zu 1.1.1.1, Blocklist-Reload via Darwin-Notification.
- DnsFilter.swift / HashList.swift / DomainHasher.swift — Swift-Ports der
Android-DNS-Filter-Logik. blocklist.bin-Format (sortierte big-endian UInt64,
SHA-256-Prefix) 1:1 beibehalten, mmap statt Heap-Load.
RebreakProtectionModule.swift:
- activateUrlFilter startet jetzt den Packet-Tunnel via NETunnelProviderManager
(Default-Layer-1, On-Demand-Auto-Reconnect aktiv).
- NEURLFilter-Code in activateNeUrlFilter ausgelagert (inaktiv, behalten).
- getDeviceState/disable lesen bzw. stoppen den Tunnel-Status.
with-rebreak-protection-ios.js: zweites app_extension-Target, klassischer
Embed-Pfad (dstSubfolderSpec 13), packet-tunnel-provider-Entitlement + App-Group.
app.config.ts: zweites appExtensions-Target.
NICHT auf echtem Gerät verifiziert — NE-Packet-Tunnel laufen nicht im
Simulator. Ungetestete Annahmen im Code mit "UNGETESTETE ANNAHME" markiert.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
getWebCustomDomains schliesst nur noch 'rejected' aus — 'approved' Domains
bleiben in der Layer-2-VIP (Zweitschutz, falls Layer 1 aus ist). Reihenfolge:
pending zuerst (keine Layer-1-Deckung → duerfen nie aus dem 50er-Cap fallen),
dann approved neueste-zuerst (Ueberlauf = aelteste approved, via Layer 1 gedeckt).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Der alreadyGlobal-Pre-Check (Domain schon in der 208k-Layer-1-Blocklist →
kein Custom-Slot) gilt jetzt nur noch fuer Mail-Typen. Fuer type='web'
uebersprungen: Web-Custom-Domains speisen die Layer-2-VIP-Liste, eine separate
Schicht — eine global (Layer 1) gelistete Domain muss in die VIP koennen,
gerade weil Layer 2 das Netz ist, wenn Layer 1 aus ist.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
syncWebContentDomains war als Side-Effect an syncBlocklist gehaengt, das nur
bei aktivem URL-Filter laeuft. Layer 2 haengt aber an Family Controls — der
Sync lief nie wenn nur App-Lock/FC aktiv war. Jetzt eigene syncWebContent-
Funktion, ungated: Mount + App-Foreground + nach Domain-Add/-Remove.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>