User generiert 4-stelligen Code in der App, setzt ihn manuell als
Screen Time Passcode â ReBreak speichert ihn auf dem Backend.
Damit kann niemand Screen Time deaktivieren â deny-removal bleibt
aktiv â App nicht deinstallierbar ohne den Passcode.
Backend:
- Profile.screentimePasscode Feld (Migration add_screentime_passcode)
- POST /api/protection/screentime-passcode â Code speichern
- GET /api/protection/screentime-passcode â Code abrufen (nach Cooldown)
iOS UI (blocker.tsx):
- ScreentimePasscodeCard erscheint wenn Layer 1 + 2 aktiv (iOS only)
- Code-Generierung â Einmal-Anzeige â Deep-Link zu Settings â Screen Time
- BestÀtigung speichert Code auf Backend, Card zeigt Confirmed-State
Locales: DE/EN/FR/AR screentime_* Keys
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
iCloud-Sign-In Pattern: wenn ein neues GerÀt versucht sich anzumelden
und das Plan-Limit erreicht ist, kann der User auf einem bereits
angemeldeten GerĂ€t bestĂ€tigen â Code wird auf BEIDEN GerĂ€ten gezeigt
fĂŒr visuellen Vergleich (verhindert Code-Forwarding-Attacken).
Backend:
- New table device_approval_requests + supabase_realtime + RLS
- POST /api/devices/approvals â create (new device)
- GET /api/devices/approvals â list pending (existing devices)
- GET /api/devices/approvals/:id â status poll (new device)
- POST /api/devices/approvals/:id/approve â approve + atomic evict
- POST /api/devices/approvals/:id/reject â reject
- POST /api/devices/approvals/:id/email â trigger email fallback
- POST /api/devices/approvals/email/:token â magic-link approve (no auth)
- Email-Template via Resend (lyra-neutral, security-formal)
- 10min TTL, 6-digit numeric codes (crypto-random)
Frontend (rebreak-native):
- DeviceApprovalIncomingSheet â existing devices: code + device-picker + Allow/Reject
- DeviceApprovalPendingSheet â new device: code + spinner + 'Send via email'
- useDeviceApprovalRealtime â postgres_changes subscription
- DeviceLimitReachedSheet â neues CTA 'Auf anderem GerĂ€t bestĂ€tigen'
- i18n DE/EN/FR/AR
Migration lÀuft automatisch via prisma migrate deploy bei push.
Modal zeigte auf iOS "Du kannst den ReBreak-Bedienungshilfe-Dienst jetzt
in den Einstellungen ausschalten" â Bedienungshilfe/Accessibility-Service
ist ein Android-Konzept, existiert auf iOS nicht.
iOS: NEFilter + Family Controls werden von forceDisable() vollstÀndig
abgeschaltet, User muss nichts in Settings tun. Neue iOS-Variante zeigt
nur "Cooldown abgelaufen â Schutz deaktiviert." + OK, kein Settings-Button.
Android: unverÀndert (a11y-Service braucht Settings-Deeplink).
i18n DE/EN/FR/AR: cooldown_elapsed_message_ios neu.
Felder wurden nirgendwo gelesen/angezeigt (nur in raw_user_meta_data
gespeichert ohne Verwendung). Inkonsistent mit OAuth-Flow der sie
gar nicht erfasst. Entfernt:
- 2 Inputs aus signup.tsx
- firstName/lastName aus signUp metadata-Typ + data
- 8 i18n-keys (de/en/fr/ar)
- DB-Cleanup via SQL fĂŒr 5 existing User (raw_user_meta_data - 'first_name' - 'last_name')
Art. 5(1)c DSGVO: nur Daten verarbeiten die fĂŒr Zweck notwendig sind.
Nickname allein reicht â AnonymitĂ€t-Pattern (memory/feedback_anonymity_nickname.md).
## TTS Auto-Play Preference
User-Request: wenn Voice einmal aktiviert, soll Lyra auf jeder Slide
automatisch sprechen â nicht jede Slide extra antippen.
- stores/lyraVoice.ts: zustand-store mit AsyncStorage-Persistence
(@rebreak/lyraVoiceEnabled). Default OFF.
- LyraBubble auto-plays on text-change wenn enabled
- Audio-Button toggled die Preference + stoppt current playback
- Visuell: Button ist orange-filled wenn voice ON, ghost-bordered wenn OFF
- Icon: volume-mute-outline (OFF) / volume-medium / hourglass / stop
- Cleanup beim Unmount (stopLyraSpeech) + bei text-change
Initialisiert via init() in app/_layout.tsx (analog language/theme/appLock).
Locale-keys: audio_play â "Stimme einschalten", neu audio_disable â "Stimme
ausschalten" in 4 Sprachen.
## DiGA Test Codes 011-100
Aktuell 10 Codes (REBREAK-TEST-001..010), aber 100 Android-Tester kommen
morgen onboarding. Migration 20260518_extend_diga_test_codes seeded 90
zusÀtzliche Codes via generate_series(11, 100) + LPAD-Padding.
- Label: 'test_batch_2026-05-android' fĂŒr Auditbarkeit (vs '...2026-05'
fĂŒr die ersten 10)
- grants_plan: 'legend' wie die ersten 10
- ON CONFLICT DO NOTHING â idempotent
Distribution-Pattern: Tester N kriegt Code REBREAK-TEST-<NNN-padded>.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
## Protection Pre-Explainer: External Pointer
Vorher: Pulse-Ring absolute-positioniert IM Screenshot â Position musste
per-locale fine-tuned werden weil Apple-Dialog-Höhe variiert (DE/EN/FR/AR
haben unterschiedliche Text-LĂ€ngen â Dialog hat verschiedene Höhen â
Erlauben-Button rutscht).
Jetzt: animierter Pfeil + Label-Pill UNTER dem Screenshot. Dimensions-
agnostic, funktioniert in allen 4 Sprachen ohne Locale-spezifische Magie.
- ScreenshotPointer komplett refactored: caret-up + bouncing pill mit
Button-Label-Text (z.B. 'Tippe "Erlauben"' / 'Tap "Allow"' / etc.)
- onboardingAssets.ts: getPointerPosition deprecated/entfernt
- ProtectionSlide nutzt neue API mit buttonLabelKey
- 4 Locales: dialog_button_allow + dialog_button_continue
- tap_marker_hint refined (kein "roter Marker"-Ref mehr)
## i18n-aware Screenshots
en/fr/ar Permission-Dialog-Screenshots zur Map ergÀnzt. Resolver fÀllt
auf de zurĂŒck wenn andere Sprache fehlt.
## Dynamic Sizing
ProtectionSlide nutzt useWindowDimensions:
height: min(320, max(200, screenH * 0.32))
â passt auf iPhone SE (213px) bis Pro Max (320px capped) ohne Scroll.
OnboardingShell ScrollView-Padding reduziert (16â12 top, 24â16 bottom).
ProtectionSlide-Spacing tightened.
## Blocker: lockedIn Fix
Bug: `lockedIn = appDeletionLockActive` ignorierte URL-Filter-State â
wenn User nur FC aktivierte (ohne URL-Filter), zeigte App grĂŒnen "Schutz
aktiv"-Banner obwohl URL-Filter aus war. Fix:
lockedIn = urlFilter && appDeletionLock
â Beide mĂŒssen wirklich aktiv sein fĂŒr den grĂŒnen Banner.
## LayerSwitchCard: lockedHint Prop
Optional Hint-Text der unter dem active Layer angezeigt wird, z.B.
"System-gesperrt. Nur in iOS-Einstellungen â Bildschirmzeit â Verwaltung
durch ReBreak deaktivierbar.". Wird fĂŒr iOS App-Lock-Card genutzt.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
## Backend: Anti-Auto-Reactivation nach Cooldown
Bug: nach Cooldown-Ablauf wurde der URL-Filter automatisch wieder
reaktiviert (enforceProtection-Loop fÀngt 'recoveringFromBypass'-Phase ab).
Damit war der Cooldown-Schritt entwertet â User konnte nicht wirklich
abschalten, weil die App den Schutz sofort wieder hochfuhr.
Fix: Profile.protectionDisabledAt (DateTime nullable). Wird in
/api/cooldown/status auf cooldown-auto-resolve gesetzt. /api/protection/state
gibt dann protectionShouldBeActive=false zurĂŒck â Frontend macht KEINE
Auto-Reactivation. User muss explizit re-aktivieren (CTA in der App).
- Migration 20260517_protection_disabled_at
- Schema: Profile.protectionDisabledAt
- /api/cooldown/status: setzt das Feld auf expired+resolve
- /api/protection/state: includes profile.protectionDisabledAt in shouldBeActive-Berechnung
- /api/protection/mark-active (POST, NEU): clears das Feld, vom Frontend
auto-aufgerufen nach erfolgreichem activateUrlFilter
Bypass-Recovery durch externe iOS-Settings-Disable (nicht cooldown-bezogen)
funktioniert weiter â protectionDisabledAt ist dann null, alte Logik greift.
## Frontend: ProtectionOffSheet (Custom-Sheet statt Alert.alert)
Bisheriges native Alert mit OK+Reactivate-Buttons hat keine visuelle
Hierarchy (iOS macht beide gleich). Ersetzt mit FormSheet:
- GroĂer blauer Primary "Schutz wieder einschalten"
- Ghost-Link "SpÀter"
- Swipe-down / Backdrop-Tap zum SchlieĂen
## Frontend: ProtectionSlide mit Pre-Explainer (Screenshot + Pulse-Marker)
User-Request: vor dem iOS-Permission-Dialog ein ErklÀrungs-Screen zeigen
damit der User weiĂ wo er tappen muss (Apple's "Don't Allow" ist groĂ+
blau = Trap, "Allow" ist der unscheinbare Button unten).
- components/onboarding/ScreenshotPointer.tsx â Reanimated pulsing red
ring, positionierbar via {xPercent, yPercent}
- lib/onboardingAssets.ts â locale-aware require()-Map fĂŒr Screenshot-
Assets mit de-Fallback
- assets/onboarding/de/ â 4 iOS-Screenshots vom User (url_filter +
screen_time permission dialogs + 2 confirm screens)
- ProtectionSlide refactored: internal phase state preexplain_url â
preexplain_lock â done. Jede Phase zeigt Screenshot + Pulse-Marker auf
korrekten Button + Lyra-Bubble + activate-CTA.
## Locale-Keys
- onboarding.lyra.protection_url.body, onboarding.lyra.protection_lock.body
- onboarding.protection.url_title, .lock_title, .tap_marker_hint
- onboarding.protection.applock_failed_*, applock_skip
- blocker.protection_off_later, reactivate_btn (refined)
## Bugfix: de.json JSON-syntax
Smart-quote-typo: schlieĂendes "" nach âErlauben" und âFortfahren" war
ein plain ASCII " (U+0022) statt U+201D, was den JSON-String frĂŒh
terminiert hat. Metro+Hermes warfen "unrecognized Unicode â".
Fix: escapte \" verwendet â JSON-safe.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>