57 Commits

Author SHA1 Message Date
chahinebrini
63fae25531 fix(android-protection): explicit specialUse FGS type — Samsung/Android 16 crash loop
RebreakVpnService.onStartCommand crashed with SecurityException because Android 16's validateForegroundServiceType rejects the implicit 2-arg startForeground(). Now passes FOREGROUND_SERVICE_TYPE_SPECIAL_USE explicitly (Google's documented best practice) and guards the call so a failed foreground promotion stops the service cleanly instead of crashing the app. Verified vs reported Galaxy A54 / Android 16 signature (97% of crash events, 1-user crash loop).

Bundles pending working-tree work across native/marketing/locales/mac + graphify-out rebuild. gitignore: google-services.json + /screenshots/.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 22:33:28 +02:00
chahinebrini
fe6a63bd8d chore(release): v0.4.4 (versionCode 70) + deploy.sh self-healing
- Release 0.4.4 zu Play Internal Testing (Android Tamper-Lock-Präzision +
  a11y-Onboarding-Guide), Notes ins CHANGELOG archiviert.
- deploy.sh ensure_native_dir jetzt self-healing: prüft Marker-Datei
  (android/app/build.gradle bzw. ios/Podfile) statt nur ob der Ordner
  existiert. Ein halb-gewischtes native-Verzeichnis (nur build/-Output) wird
  erkannt und via 'expo prebuild --clean' sauber regeneriert, statt mit
  "autolinking.json doesn't exist" zu failen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 07:52:10 +02:00
chahinebrini
4a013bc43b feat(android-protection): präzise Tamper-Lock + a11y-Onboarding-Guide
Tamper-Lock von Keyword-Scanning auf präzise Einzel-Surfaces umgebaut:
blockt nur ReBreaks eigene Screens (Admin-Deaktivierung via DeviceAdminAdd,
a11y-Ausschalten, VPN-Trennen/Surface), nie Listen oder fremde Apps.

- Deny-Removal = Admin-only: OS graut Uninstall+Force-Stop für aktiven
  Device-Admin aus; einziger Bypass (Admin deaktivieren) bleibt a11y-gesperrt.
  Andere Apps verwalten/force-stoppen/deinstallieren bleibt komplett frei.
- a11y-Onboarding: passiver Bottom-Overlay-Hinweis + Settings-Reset auf
  Startseite nach Aktivierung + 1s-Delay vor App-Rückkehr.
- VPN-Trennen-Dialog + a11y-Ausschalten neu abgedeckt.
- a11y-Service-Icon im Plugin (klar als ReBreak erkennbar).

Verifiziert auf A50 per logcat: alle 4 Surfaces blocken, Listen + fremde
Apps frei, keine False-Positives.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 04:05:41 +02:00
chahinebrini
c7fc237dfd feat(android-protection): device-admin uninstall-block + boot-receiver + config plugin
Android self-bind protection auf nahezu MDM-Niveau ohne Device-Owner:
- Device-Admin (RebreakDeviceAdminReceiver) blockt Uninstall OS-seitig, aktiv ab
  Boot ohne Prozess/a11y. Deaktivierung nur via 24h-Cooldown (removeDeviceAdmin in
  forceDisable). a11y blockt die DeviceAdminAdd-Settings-Seite (Class-Match, auf
  Samsung One UI per Logcat verifiziert).
- Boot-Receiver (RebreakVpnBootReceiver) startet VPN+a11y nach Reboot, damit der
  Tamper-Lock ohne manuellen App-Start hochkommt.
- Manifest-Wiring (Device-Admin-Receiver, Boot-Receiver, RECEIVE_BOOT_COMPLETED,
  device_admin.xml) ins with-rebreak-protection-android Config-Plugin verlagert →
  ueberlebt 'expo prebuild' (android/ ist gitignored).
- a11y-Detection zurueck auf die funktionierende Version: zu breites 'loeschen'-
  Uninstall-Keyword raus (blockte halbe Settings); a11y-Label jetzt 'ReBreak Schutz'.
- a11y-Deeplink behaelt den Samsung-Step-Guide (openAccessibilitySettings).

Session-Frontend in diesem Batch:
- Avatar-Placeholder: neutrales clarity-avatar-line SVG statt dominantem Blau.
- DiGA-Milestone folgt kumulativen protectedDays (erreicht rueckfall-anfaellige User).
- Dev-Build crasht nicht mehr ohne CallKit-Native-Modul.
- VPN-Permission-Dialog nur noch im Bypass-Fall.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 04:52:49 +02:00
chahinebrini
fb2d90b947 fix(calls): no duplicate incoming-call notifications
- backend: skip Expo alert push to iOS devices that already received VoIP push
  (CallKit + banner = double ring)
- native: receiveIncoming no longer triggers InCallManager.startRingtone —
  CallKit/ConnectionService play their own ring. Dedup if same callId
  arrives twice (Realtime + VoIP-Push race).
2026-06-04 18:28:00 +02:00
chahinebrini
4a520ba7c9 feat(calls): B4 — VoIP-PushKit config-plugin + client voipToken + JS payload bridge 2026-06-04 10:02:48 +02:00
chahinebrini
822053e11e feat(calls): CallKit/ConnectionService + VoIP-PushKit + EU-Ringback
Caller/Callee UX:
- lib/ringback.ts + assets/sounds/ringback_eu.mp3 (EU 425Hz Festnetz-Tone)
- stores/call.ts: stopRingback bei connected, hangup-reasons, logCallToChat fix
- locales: 'Wird angerufen…' statt 'Ruft an…'

CallKit (iOS) + ConnectionService (Android):
- lib/callkit.ts: setupCallKeep, displayIncomingCall, startOutgoingCall, reportConnected/Ended (appName 'ReBreak-Audio', includesCallsInRecents=false für DSGVO/DiGA)
- hooks/useCallKeepEvents.ts: native answer/end/mute → useCallStore-Actions
- stores/call.ts: CallKit-Aufrufe an allen lifecycle-Punkten
- app.config.ts: @config-plugins/react-native-callkeep + UIBackgroundModes voip/audio + Android-Telecom-Perms

VoIP-PushKit Backend:
- services/voip-push.ts: @parse/node-apn Provider mit .p12 (Topic org.rebreak.app.voip)
- services/push.ts sendCallRingPush: feuert beide Pfade (VoIP iOS + Expo Android/Fallback)
- prisma: push_tokens.voip_token Column + Migration 20260604
- api/users/me/push-token: optional voipToken im Body
- Env (Infisical): APNS_VOIP_P12_PATH/PASSWORD/TOPIC/PRODUCTION

Push-tap routing + cold-start handling:
- app/_layout.tsx: type:'call' Push → useCallStore.receiveIncoming + /call

Docs: ops/CALLKIT_SETUP.md (Apple-Portal-Steps für VoIP-Cert)
2026-06-04 09:27:13 +02:00
chahinebrini
89e4e3481b feat(calls): Phase 0 — calls_enabled opt-out + canCall guard (mutual-follow); DM UI batch
Backend (voice-call groundwork, no call engine yet):
- Profile.callsEnabled (Boolean default true) + migration
- canCall(caller,callee): mutual-follow AND callee.callsEnabled — server-side hard guard
- POST /api/me/calls-enabled (opt-out toggle), GET /api/chat/can-call/:userId
- expose callsEnabled in /api/auth/me

Frontend:
- "Allow calls" toggle in Profile privacy section (default on, optimistic+rollback)
- Me.callsEnabled + i18n DE/EN/FR/AR

Bundled DM UI work from this session:
- image lightbox is now a swipeable carousel over all shared images (+ counter)
- keyboard stays open after sending (input ref refocus)
- voice notes: Instagram-style waveforms (own=white/mint, other=black/grey),
  removed the blue progress dot; lazy-load expo-media-library with clean fallback
- expo-linear-gradient + expo-media-library deps

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 21:14:31 +02:00
chahinebrini
50425a62ee fix(devices): Magic-Hub zeigt jetzt alle Native-Geraete, Native dedupliziert Mac
Magic-Mac-Hub (/api/magic/devices):
- Filter boundToPlan war zu eng \u2014 iPhone/iPad ohne aktiven Plan-Lock
  fielen raus. Jetzt: alle UserDevice-Rows des Users ausser den
  magic-enrolled, plus ProtectedDevice mit Dedupe.

Native /devices Page:
- MacBook erschien doppelt: einmal als UserDevice (registriert via
  Magic-Mac, model=Mac14,9) und einmal als ProtectedDevice (alter
  DNS-Flow). Dedupe per platform-key (mac/ios/android/win):
  wenn UserDevice mit gleicher Plattform existiert, blende
  ProtectedDevice aus.
- Slot-Counter zaehlt jetzt nach dedupe (totalRegistered).
2026-06-03 19:43:33 +02:00
chahinebrini
ac72fabc34 feat(magic): Hub vereinigt Magic-Bindings + alte ProtectedDevices
- GET /api/magic/devices fetcht jetzt parallel listMagicDevices()
  + listProtectedDevices() und merged beide Quellen in eine
  Response. Items haben neues 'source' Feld (magic|protected).
- ProtectedDevice (alter Native-DNS-Flow) wird auf gleiche
  Shape gemappt: label->hostname, platform->model.
- Mac-App MagicDevice: source-Feld optional + resolvedSource
  Fallback fuer Backwards-Compat. id mit source-Prefix gegen
  Collisions zwischen Tabellen.
- DeviceHubView Row: protected-Geraete bekommen graues
  'Native-App' Badge und Hinweis 'Verwaltung in der
  ReBreak-App' statt Trash-Button (Release laeuft dort).
2026-06-03 11:05:15 +02:00
chahinebrini
8670b45351 fix(magic): inline mobileconfig template as TS constant
serverAssets approach didn't bundle the template into the Nitro
output (no .output-staging/server/chunks/raw/ dir, no asset-storage
mount in nitro.mjs). Logs confirm: '[Magic] Profile template missing
in serverAssets'.

Drop serverAssets entirely. Inline the template (~2KB) as a TS
constant in backend/server/utils/magic-profile-template.ts. Build-
robust, no FS/storage dependency at runtime. Canonical source of
truth remains ops/mdm/rebreak-mac-dns-filter.mobileconfig — keep in
sync manually until/unless we add a codegen step.
2026-06-03 09:57:27 +02:00
chahinebrini
ea759cc79c fix(magic): explicit imports for new db/utils functions
Nitro auto-import did not pick up findMagicDeviceByToken / listMagicDevices /
countActiveMagicBindings / createAdGuardClient on first build. Added explicit
imports as safety net.
2026-06-02 09:54:40 +02:00
chahinebrini
c1edef8abd feat(magic): RebreakMagic device-binding + DNS profile
- backend: /api/magic/{register,devices,profile,release} + AdGuard provisioning + 24h cooldown
- prisma: magic_binding_fields migration (additive on UserDevice)
- mac-app: Phase 2 - Login + MacRegistration + Profile install
- marketing: landing section + /download/rebreakmagic + DMG
- lyra: forbidden phrases + RebreakMagic coach guidance
2026-06-02 09:15:19 +02:00
chahinebrini
53d8ace974 chore(native): strings fix + app.config + plist updates
- strings.xml: accessibility_service_summary = "ReBreak — Schutz"
  (war durch Linter mehrfach zurückgesetzt worden)
- app.config.ts + Info.plist: version bumps
- CHANGELOG.md + deploy-runtimes: release-tracking aktualisiert

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 09:32:41 +02:00
chahinebrini
b1d382bada testflight 2026-06-01 04:44:19 +02:00
chahinebrini
db0aa6d24e feat(native): Protection Onboarding v2 + Devices + ProtectionSlide
- ProtectionOnboardingSheet: Android a11y 2-step flow mit tamper-arm nach Return
- ProtectionDetailsSheet: cleanup, iOS/Android split, locked-state logic
- ProtectionSlide: neuer Onboarding-Slide für Protection-Intro
- _layout.tsx: reconcileVpn on app-foreground (Android VPN self-heal)
- devices.tsx: Two-device approval flow
- useProtectionState: applyCooldownDisableIfElapsed, forceDisable on cooldown-end
- iOS module Info.plist: bundle version bumps
- app.config.ts: minor config updates
- tmp/.deploy-runtimes: build-time metrics aktualisiert

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 04:30:20 +02:00
chahinebrini
578abfe3bb chore(release): v0.3.13 build 46 / vc36 — DM scroll fix + chat timestamps weekday/days/weeks/months 2026-05-31 07:33:06 +02:00
chahinebrini
2715d2620b chore(release): ios buildNumber 44 → 45 (TestFlight + MDM deployed) 2026-05-31 03:42:23 +02:00
chahinebrini
acf14aaf11 chore(release): rebreak-native v0.3.13 build 44 (versionCode 35)
- Bump android versionCode 33 → 35 (build.gradle was out-of-sync with app.config.ts)
- Archive NEXT_RELEASE.md → CHANGELOG.md
- Released to Google Play Internal Track

Release notes (Build 44):
- DM-Chat: scrollt jetzt zuverlässig zur neuesten Nachricht
- Lyra-Sprachnachrichten: arabisch/türkisch antworten in richtiger Sprache (STT fix)
- DM Push-Notifications: Fehler-Logging hinzugefügt
2026-05-31 02:03:00 +02:00
chahinebrini
b956b3b1fc chore(ui): branded splash.png statt weißer icon.png-Box + Release-Notes
- app.config.ts splash: icon.png → splash.png (full-bleed Navy-Gradient, cover)
- NEXT_RELEASE.md: Blur-Menüs + neuer Splash ergänzt

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 11:52:58 +02:00
chahinebrini
f83a13ba60 chore(release): deploy.sh all-platform (MDM/TF/Android) + NEXT_RELEASE-Workflow
- deploy.sh: Android-Signing-Auto-Restore aus build-config/android-signing/
  (gitignored), getestet für MDM + TF + Android
- .gitignore: *.keystore + build-config/android-signing/
- NEXT_RELEASE.md wiederhergestellt mit Notes fürs WA-Chat-Popup + Comment-Likes
  (deploy.sh archiviert es in CHANGELOG + leert es danach)
- buildNumber 27→41, versionCode 18→31 (Release-State)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 11:34:57 +02:00
chahinebrini
b31066a04c feat(chat): native action sheet + Insta-style heart for DM messages
- ChatBubble: useActionSheet replaces custom Modal (native iOS popup, Android bottom sheet)
- DM mode (isDM prop): hides like-count, shows Insta-style heart badge under bubble when liked
- Group chat unchanged
- Cleanup: remove unused Modal/Platform imports, sheet styles, actionsOpen state
- deploy.sh: auto-detect ANDROID_HOME + auto-create local.properties for local Gradle
- NEXT_RELEASE.md: DM reactions release note
- Includes other staged work across binder-mac, marketing, ops/mdm, ios/
2026-05-30 09:14:32 +02:00
chahinebrini
d9f5b631b1 chore(release): bump iOS auf v0.3.6 Build 15
MDM-Pilot: bei MDM-managed iPhones FC-Authorization-Toggle-UI ausgeblendet,
Schutz läuft via MDM-managed VPN-Layer. Backend: Layer-2 Country-Curated-Pivot deployed.
2026-05-25 07:16:08 +02:00
chahinebrini
b6b1f68940 chore(release): URLFilter-Extension raus + Android versionCode 11 2026-05-22 22:19:52 +02:00
chahinebrini
435aaeefb1 chore(release): bump iOS auf v0.3.5 Build 14
TestFlight-Release. Highlights: Layer-1-VPN-Fix (File-Protection) +
Self-Heal, VIP-Liste landabhängig + Kachel-UI, VIP-Slot-Replace mit
Cooldown, Curated-Domain-Vorschläge, Slot-Pool 10/20, Chat-Fixes
(inverted FlatList, Listen-Hänger).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 21:31:29 +02:00
chahinebrini
5a16cf771b feat(ios-protection): v1 NEPacketTunnelProvider DNS-Sinkhole als Layer-1
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>
2026-05-21 23:13:54 +02:00
chahinebrini
50d3c6a8e1 fix(protection): Layer 2 — Family Controls default-on + Resource-Bundle-Pfad
- familyControlsEnabled jetzt default-on (Apple-Distribution-Entitlement
  freigegeben); nur REBREAK_ENABLE_FAMILY_CONTROLS=0 schaltet ab.
- gambling-domains.json ins Pod-Verzeichnis verschoben + Podspec resource_bundles
  auf within-pod-Pfad korrigiert — CocoaPods buendelt keine Dateien ausserhalb
  des Pod-Roots (Bundle blieb sonst leer → Layer 2 griff nicht).
- Layer 2 (webContent-Filter) damit verifiziert funktionierend.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 20:13:57 +02:00
chahinebrini
29bbf23405 feat(protection): iOS NEURLFilter-Spike + PIR-Server-Ops
NEURLFilter-Stack (iOS 26): Extension RebreakURLFilter -> URLFilterExtension
umbenannt, url-filter-provider-Entitlement, Bloom-Prefilter-Extension,
PIR-Client-Config (pirServerURL/pirAuthToken via Build-Env).
PIR-Server-Ops unter ops/pir-server/ (Dockerfile, build-and-deploy, Patches,
DTS-Report). backend/scripts/generate-pir-input.ts erzeugt die PIR-Datenbank.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 18:09:42 +02:00
chahinebrini
c6604f02df chore(release): v0.3.4 — enable Family Controls (Apple distribution entitlement approved) 2026-05-20 07:13:13 +02:00
chahinebrini
5a44b8162e chore(release): v0.3.3 — FC-gating + native log-viewer + expo-image 2026-05-20 05:34:57 +02:00
chahinebrini
f318364c7e fix(protection): gate Family Controls on entitlement + native log viewer
Verifiziert via Build-11-.ipa-Inspektion + Device-Logs:
- com.apple.developer.family-controls FEHLT im signierten Distribution-Build
  (Plugin gated korrekt auf REBREAK_ENABLE_FAMILY_CONTROLS — Apple-
  Distribution-Approval pending).
- Device-Log beweist: ManagedSettingsAgent-XPC → "error 159 Sandbox
  restriction" → NSCocoaErrorDomain:4099. Kein Daemon-/Race-Problem,
  Retry zwecklos.

Root-Cause des FC-Bugs: app.config.ts hatte familyControlsEnabled hart
auf true (falscher "approved"-Kommentar) → App bot App-Lock an obwohl
sandbox-blockiert. FAMILY_CONTROLS_AVAILABLE wurde nirgends konsumiert.

Fixes:
- app.config.ts: familyControlsEnabled an REBREAK_ENABLE_FAMILY_CONTROLS
  gekoppelt (== Plugin-Gating) → in TestFlight/production false.
- blocker.tsx: iOS App-Lock-Card nur wenn FAMILY_CONTROLS_AVAILABLE.
  lockedIn akzeptiert URL-Filter allein wenn FC build-seitig fehlt.
- ProtectionSlide.tsx: Onboarding überspringt den App-Lock-Step (FC)
  wenn nicht verfügbar — URL-Filter allein = vollwertiger Schutz.

Native Log-Viewer (für NEFilter-Debug ohne Mac/Console.app):
- Swift: getProtectionLogs/clearProtectionLogs lesen SharedLogStore aus
  App-Group-UserDefaults.
- lib/protection.ts wrapper + TS-module-types.
- debug.tsx: ProtectionLogCard (iOS) — native NEFilter/FC-Logs in der
  App sichtbar, copy/clear/refresh.
2026-05-20 05:32:06 +02:00
chahinebrini
a9015d1951 chore(release): v0.3.2 — bug-fix bundle (permission/cooldown/onboarding) 2026-05-20 04:30:24 +02:00
chahinebrini
b9b397b346 chore(release): v0.3.1 + eas-release.sh script
- package.json + app.config.ts (Expo source-of-truth) auf 0.3.1
- versionCode/buildNumber bleibt 10 (native files schon synced)
- eas-release.sh: build+submit für TestFlight/Play-Internal, default
  iOS, --android/--both flags, version-check-prompt vor build

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 08:11:18 +02:00
chahinebrini
23cc147231 feat(auth/ios): native Apple Sign-In via expo-apple-authentication
Vorher: stores/auth.ts hatte TODO + fiel auf Supabase-Web-OAuth-Flow zurück,
was fehlschlug mit 400 'Unsupported provider: missing OAuth client ID' weil
der Supabase-Apple-OAuth-Provider nicht konfiguriert ist.

Jetzt: native Flow ohne Supabase-Provider-Config —
- expo-apple-authentication.signInAsync() → identityToken
- supabase.auth.signInWithIdToken({provider:'apple', token}) verifiziert direkt
  gegen Apple's Public-Keys (kein Client-Secret-JWT-Setup nötig)
- User-Cancel (ERR_REQUEST_CANCELED) → leeres Resultat statt Error
- Platform-Guard: Apple-Path nur auf iOS

app.config.ts: ios.usesAppleSignIn=true → Expo prebuild generiert das
com.apple.developer.applesignin-Entitlement in die .entitlements. Beim
ersten EAS-Build wird die Capability auto-registriert im Apple-Developer-
Portal für org.rebreak.app.

signin.tsx + signup.tsx: Apple-Button conditional auf Platform.OS==='ios'
gerendert. Android-User sehen nur Google-Button (auf Android gibt es kein
natives Apple Sign-In).

App-Store-Submission-Pflicht (Apple Guideline 4.8 — wer OAuth-Login mit
3rd-Party-Provider anbietet, muss auch Apple Sign-In bieten).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 00:13:45 +02:00
chahinebrini
c9273a2770 chore(release): v0.3.0 — version bump for TestFlight + Play Console
- package.json: 0.2.0 → 0.3.0
- app.config.ts: version 0.2.1 → 0.3.0
- iOS buildNumber: 9 → 10
- Android versionCode: 9 → 10
- CHANGELOG.md: v0.3.0 entry with Duo-Onboarding, DiGA, Stripe-pivot, Arabic+RTL,
  NEFilter-robust-disable, anti-auto-reactivation, FC always-on, etc.

Note: Android-Build wird vorerst NICHT submittet — Onboarding-Slides müssen für
Android-Protection-Mechanismus (VPN + a11y statt iOS NEFilter + Family Controls)
mit eigenen Pre-Explainer-Screenshots + Texten angepasst werden. Erst dann
v0.3.1 oder gesammelt mit Android.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 21:47:58 +02:00
chahinebrini
b23bd6d29f feat(onboarding,protection): Duo-style flow + cooldown auto-disable fix + Family Controls live
## Duo-Style Onboarding (Foundation + alle Slides)

Self-contained Onboarding-Flow mit Lyra-Mascot ersetzt das Spotlight-POC vom
vorherigen Iteration. Slides leben unter `components/onboarding/slides/`.

- Foundation: OnboardingShell (Progress + ScrollView + sticky CTABar),
  LyraBubble (Rive-Avatar + animierte Speech-Bubble), SlideProgress, CTABar
- Slides: Welcome, Privacy (4 Versprechen), Nickname (inline + PATCH /me),
  DigaChoice (Ja/Nein-Branch), DigaCode (redeem-Endpoint + inline-Errors),
  Plan (Pro/Legend cards, monthly/yearly toggle, 2 Monate gratis, Härtefall-
  Mailto), Payment (RevenueCat-Dev-Stub bis Phase-0), Protection (activate +
  PermissionDeniedSheet-Wiring), Done (animierter Checkmark + Streak-Day-1)
- State-Machine in app/onboarding/index.tsx: 9 Slides, DiGA-Branch, Resume-
  on-launch via slideFromStep(me.onboardingStep)
- Routing-gate in (app)/_layout.tsx: step != 'done' → /onboarding
- Backend Profile.onboardingStep enum extended:
  welcome | account | plan | pre_protection | done (+ legacy nickname/block)
- Backend diga redeem: step='pre_protection' (NICHT 'done') — User muss noch
  durch Protection-Slide für NEFilter/VPN-Aktivierung
- Locale-Keys (de/en/fr/ar): onboarding.lyra.<slide>.body, .cta_primary,
  Plan-Tier-Details (3,99/7,99 €/Mo, 39,90/79,90 €/Jahr mit 2 Monaten gratis),
  Härtefall-Link, DiGA-Code-Errors, Protection-Feat-Descriptions

## Cooldown Auto-Disable Race-Fix

Bug: nach Cooldown-Ablauf bleib URL-Filter installiert (NEFilter in iOS-
Settings sichtbar als "Läuft..."). Root-cause: `/api/cooldown/status` GET
auto-resolved beim ersten expired-Hit; zweiter Call in
applyCooldownDisableIfElapsed sah cooldownEndsAt=null → bail → forceDisable
nie aufgerufen.

- useProtectionState.fetchState: lokalen next.cooldown.endsAt state nutzen
  statt redundantem API-Call. Atomarer, race-frei.
- AppState-Listener-Path unverändert (dort ist es der erste API-Call, kein
  Race).
- lib/protection.forceDisable: console.log für Debug-Visibility.

## iOS NEFilter Robust-Disable (Native)

`removeFromPreferences()` alleine ist auf iOS 18+ unzuverlässig — Settings-
UI zeigt "Läuft..." obwohl Provider beendet sein sollte. 2-Step-Pattern:

  1. loadFromPreferences
  2. isEnabled = false + saveToPreferences (stoppt Filter-Daemon)
  3. removeFromPreferences (Config-Eintrag aus Settings)

Quelle: Apple-Developer-Forums + eigene Empirie. Pattern wird auch in
PermissionDeniedSheet's resetUrlFilter genutzt (analog).

## Family Controls jetzt immer aktiv

Apple-Entitlement seit 2026-05 für ReBreak approved (TestFlight-akzeptiert).
`familyControlsEnabled: true` hart in app.config.ts (kein Env-Var-Gating mehr).
"Bald verfügbar"-Placeholder in blocker.tsx entfernt — App-Lock-Toggle ist
jetzt voll funktional auf iOS.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 17:48:05 +02:00
chahinebrini
4124463097 chore(release): bump to v0.2.1 / versionCode 9 — theme-crash, double-splash, dm-reopen fixes
- Android Theme parent → Theme.MaterialComponents.DayNight.NoActionBar.Bridge
  (fix BadgeDrawable crash in react-native-bottom-tabs after AccessibilityService toggle)
- Plugin with-material-theme-android keeps theme idempotent across prebuilds
- Plugin with-release-signing-android wires release signingConfig from key.properties
- Splash: align native splash image with JS BrandSplash (icon.png) to eliminate
  double-splash flicker on app start
- DM: reset partner/messages/replyTo state on userId change, disable cache for
  history query, switch spinner condition to isLoading||isFetching so reopens
  always load fresh and never show empty-state with stale partner

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 09:32:28 +02:00
chahinebrini
d97e3aa496 chore(release): bump to v0.2.0 / versionCode 8 — device-binding, custom mail-filter, chat v1.0, Mac DNS auto-detect
CHANGELOG entry covers:
- Mac DNS auto-detect (DoH handshake + realtime)
- Device-account-binding (Pro/Legend anti-bypass)
- Custom mail-patterns alongside web-domains (10 + 10 for Legend)
- Unified filter section with single + button + auto-detect sheet
- Chat v1.0 DM-only with unread badge
- Avatar cropper switched to iOS-native UIImagePickerController
- Help/Support pages (FAQ, Contact, About, Crisis hotlines)
- Settings: notification + streak-reminder section
- Game-over modal layout + regenerate suggestion
- Devices page redesign with central <Button> + progress bar
- Pre-check global blocklist before consuming a slot
- Local dev FAMILY_CONTROLS flag defaults on

Marketing download page version bumped to 0.2.0 build 8; sha256 +
apk size will be filled after the EAS build lands and the APK is
uploaded to the download host.
2026-05-16 03:22:53 +02:00
chahinebrini
e1ba0ebeaf chore(native): bump versionCode/buildNumber to 7 for device-info + i18n release
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 21:17:43 +02:00
chahinebrini
0452007daf chore(native): bump iOS buildNumber 4 → 6 for fr-locale + lyra-voice release
Sync mit Android versionCode 6 (buildNumber 5 wurde nie als iOS-EAS-Build
genutzt, daher direkt auf 6 für konsistente Plattform-Nummerierung).

Changelog für buildNumber 6:
- French app-language for test customers
- Lyra voice picker (legend tier)
- Realtime debug page (DEV-only)
- Mail-filter refactor: Groq removed, deterministic pipeline only
- Bugfix: protectedDevices array guard

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-15 16:41:13 +02:00
chahinebrini
fb29c061c3 chore(native): bump android versionCode to 6 for fr-locale + lyra-voice release
Changelog für versionCode 6:
- French app-language for test customers
- Lyra voice picker (legend tier)
- Realtime debug page (DEV-only)
- Mail-filter refactor: Groq removed, deterministic pipeline only
- Bugfix: protectedDevices array guard

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-15 11:03:43 +02:00
chahinebrini
740589db5b chore(native): bump android versionCode to 5 for mail-page-ui release
Covers 10 commits: reactive mail page, donut/legend layout overhaul,
multi-layer classifier, DSGVO Art.17 sample cleanup. CHANGELOG.md added.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 22:16:02 +02:00
chahinebrini
30ed4b90b4 chore(native): bump build number to 4 for staging release
iOS buildNumber: 3 → 4
Android versionCode: 3 → 4
version stays 0.1.0 (no breaking changes, MVP phase)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 00:02:49 +02:00
chahinebrini
e48a3187a6 chore(rebreak-native): expose familyControlsEnabled flag to the JS bundle
extra.familyControlsEnabled mirrors the eas.json REBREAK_ENABLE_FAMILY_CONTROLS
build flag — true for development builds (Apple granted the Development Family
Controls entitlement), false for TestFlight/production (Distribution entitlement
still pending). The Blocker page uses it to show "App-Lock — coming soon" instead
of a toggle that throws NSCocoaError 4099, while keeping the protection banner
positive (the URL filter carries it).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 21:39:37 +02:00
chahinebrini
aa9466aa92 feat(rebreak-native): Face ID app lock (opt-in)
Privacy/stigma layer on top of the authenticated Supabase session — re-auth on
open so nobody but the user can open Rebreak. Not a login replacement.

- expo-local-authentication; NSFaceIDUsageDescription in app.config
- stores/appLock.ts: persisted `enabled` pref, in-memory `locked`, device-
  capability check (`available`), device-passcode fallback on biometric failure
- AppLockGate wraps the root layout: locks immediately on `background` (not
  `inactive` → app-switcher peek doesn't lock), renders LockScreen while
  `enabled && locked && session`
- LockScreen: dark brand screen, auto-prompts on mount + on return from
  background, "Abmelden" escape hatch (clears session → fresh login next launch)
- Settings: new "Sicherheit" section, native UISwitch; enabling requires a
  successful biometric prompt first; row disabled + explained when device has no
  biometrics/passcode
- de/en strings

Per product call: the lock gates the whole app incl. SOS (SOS already requires
an authenticated user, so there's no unauthenticated path to carve out).

Cold-start: appLock init blocks the splash → `locked` is set before first paint,
no flash of unlocked content. ios/ is gitignored so EAS prebuilds the new module.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 19:41:56 +02:00
chahinebrini
5d2db6d642 fix(ios): register RebreakURLFilter extension with EAS for credential generation
The TestFlight build kept failing with:
  error: No profiles for 'org.rebreak.app.RebreakURLFilter' were found ...
  Automatic signing is disabled and unable to generate a profile.
  (in target 'RebreakURLFilter' from project 'ReBreak')

EAS managed credentials only provision the main app bundle ID. App extensions
must be declared up-front via extra.eas.build.experimental.ios.appExtensions so
the CLI knows to register the extension's App ID (+ app-group + network-extension
capabilities) and generate a distribution provisioning profile for it.

Next step (interactive, needs Apple login): `eas credentials` → iOS → preview
to actually create the extension credentials, then re-run the build.

(FamilyControls entitlement stays commented out in with-rebreak-protection-ios.js
until Apple grants the Distribution entitlement — this build ships without App-Lock.)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 17:50:48 +02:00
chahinebrini
ac956b8311 fix(ios): config plugin to disable code-signing for pod resource bundles
EAS iOS build failed: "Starting from Xcode 14, resource bundles are signed by
default, which requires setting the development team for each resource bundle
target." Cause: we build with useFrameworks: "static" (expo-build-properties),
so CocoaPods generates resource-bundle targets for pods with resources, and
Xcode 14+ wants them signed. EAS has no dev team for those.

New plugin with-resource-bundle-signing-fix injects into the Podfile's existing
post_install hook: sets CODE_SIGNING_ALLOWED = 'NO' for every pod resource-bundle
target (they don't need signing). Idempotent; runs as withDangerousMod('ios')
during prebuild so it survives EAS's clean prebuild.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 23:35:32 +02:00
chahinebrini
0bad2185ec chore(ios): set ios.buildNumber to 3 (matches Android versionCode) for TestFlight
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 22:42:50 +02:00
chahinebrini
16c2e40242 chore(android): versionCode 3 (build v0.1.0 vc3 — icon fix + updates)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 16:18:03 +02:00
chahinebrini
297ad7437b fix(android): adaptive icon — white bg + padded dark logo (was zoomed/clipped)
Old adaptiveIcon was a full-bleed dark logo on a #0a0a0a background → the
launcher mask cropped it ("zoomed in" look). Now: white background (matches
the Play Store listing icon look) + the same logo at ~62% on a transparent
canvas → mask has nothing to clip.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 15:06:12 +02:00