276 Commits

Author SHA1 Message Date
chahinebrini
89391a807b fix: Arabic STT + DM scroll + info sheet FormSheet
STT (Arabic):
- Deepgram hat nova-2-general ar/tr-Support eingestellt (400 Bad Request)
- Fix: einheitlich nova-3 für alle Sprachen inkl. ar/tr
- Stale Kommentar aus 2026-05-30 entfernt

DM scroll-to-bottom:
- onLayout auf FlatList hinzugefügt → zusätzlicher scrollToEnd nach
  initialem Layout-Pass (Android-specific race condition)
- onOpenImage im FlatList-Renderer auf Lightbox verdrahtet (war () => {})

Info-Sheet:
- Modal(pageSheet) → FormSheet mit initialHeightPct={0.85}
- Nutzt jetzt unsere eigene Sheet-Komponente konsistent

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 10:25:35 +02:00
chahinebrini
f59d1800c7 feat(dm): info sheet + avatar→profile + scroll fix + image cache
- Header: Avatar-Tap navigiert direkt zum Profil des Chatpartners
- Header: Info-Icon (ℹ) rechts öffnet neues Info-Sheet
- Header: kein separater BG mehr — blendet in native Background ein
- Info-Sheet (pageSheet Modal):
    - Partner-Karte mit Avatar + "Profil anzeigen"-Link
    - Geteilte Medien als 3-Spalten-Grid (neueste zuerst)
    - Tap auf Bild → Lightbox-Modal (Vollbild mit Close-Button)
- Scroll-to-bottom: 3-stufiges Timing (rAF + 100ms + 300ms) für
  zuverlässiges Bottom-Scroll auch wenn Bilder nachgeladen werden
- expo-image cachePolicy="memory-disk" überall: ChatBubble-Image +
  alle Images im Info-Sheet + Lightbox
- i18n: dm.view_profile / dm.shared_media / dm.no_shared_media (DE/EN/FR/AR)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 10:00:03 +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
617312f367 fix(vpn): bypass own domains in DNS filter (rebreak.org, rebreak.app)
OAuth-Callbacks gehen an db-staging.rebreak.org — wenn der In-Flight-Cap
kurz erreicht wird, kriegt das SERVFAIL statt einer echten Antwort.
Eigene Infrastruktur-Domains explizit als Bypass deklariert: werden nie
aus der Blocklist geblockt und umgehen den In-Flight-Zähler nicht
(Forward läuft weiterhin normal, aber Block-Entscheidung wird übersprungen).

Gilt für iOS (PacketTunnelProvider) und Android (DnsFilter) gleichzeitig.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 05:12:50 +02:00
chahinebrini
b1d382bada testflight 2026-06-01 04:44:19 +02:00
chahinebrini
d65ba84eb1 feat(binder): MDMClient, EnrollView improvements + supervise flow_backup
- MDMClient: error handling verbessert
- SuperviseRunner: robustere EOF-nach-Success Erkennung
- EnrollView: Enrollment-Status-Polling, Retry-Logik
- SuperviseView: UX-Verbesserungen
- ConfigureView: minor cleanup
- flow_backup.go: backup flow für supervise-magic

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 04:30:28 +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
adf0d33f1b feat(android): Protection Module v2 — VPN self-heal, boot-receiver, multilang
- RebreakProtectionModule: reconcileVpn, Samsung overlay guide, openPowerDialog,
  armTamperLock preconditions (VPN+a11y required), openAccessibilitySettings fallback chain
- RebreakVpnService: onRevoke auto-recover, blocklist self-heal on empty start,
  ACTION_RESTART für hard-reload nach blocklist-Änderung
- VpnBootReceiver (neu): startet VPN nach Geräte-Neustart wenn filter_enabled=true
- Strings: DE/EN/FR/AR a11y-Guide, Overlay-Permission, Hint-Steps
- RebreakProtectionModule.ts: reconcileVpn, armTamperLock, disarmTamperLock,
  openPowerDialog, openAccessibilitySettings, dismissAccessibilityHint exports

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 04:30:07 +02:00
chahinebrini
ab4b9c48e5 feat(ios): Screen Time Passcode als Layer 3 (setup flow)
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>
2026-06-01 04:19:43 +02:00
chahinebrini
59766f8530 fix(android): block Force Stop + App-Info auf Samsung OneUI
Samsung nutzt generische Klassen (SubSettings, FrameLayout) statt
InstalledAppDetails für die App-Info-Seite — classGuard schlug fehl,
Tamper-Lock griff nicht.

Fixes:
- classGuard für Uninstall-Pfad entfernt: Text-Kombination "rebreak"
  + Deinstall-Keyword reicht (App-Liste zeigt Buttons nie inline)
- Force-Stop-Bestätigungsdialog explizit erkannt via "stopp erzwingen"
  + "abbrechen" + "ok" (Dialog nennt App-Namen nie)
- Throttle-Reset bei TYPE_WINDOW_STATE_CHANGED: eliminiert 400ms-Fenster
  zwischen Activity-Wechsel und erstem CONTENT_CHANGED-Check
- ApplicationDetail (ohne 's') zu Pattern-Listen: Samsung OEM-Variante
- accessibility_service_summary korrigiert: "ReBreak — Schutz" statt
  "Sichert den Schutz gegen Abschalten ab" (HIGH_CONFIDENCE_KEYWORD muss matchen)

Getestet auf Samsung Galaxy A50 (One UI): App-Info-Seite wird sofort
per BACK-Action geblockt, Lyra-Toast erscheint.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 04:11:01 +02:00
chahinebrini
2e056c7257 feat(devices): Apple-style two-device approval flow + email fallback
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.
2026-06-01 02:36:28 +02:00
chahinebrini
efca157969 fix(backend): device-mgmt cleanup + stats rejected fallback + realtime refresh
- devices: cleanupStaleDevices() purges phantoms >14d not bound; called from
  GET /api/devices + register limit-check. Deterministic sort
  (lastSeenAt, createdAt, id) so iPad+iPhone see identical order.
  Merge-cutoff tightened 30d -> 7d.
- stats: rejected aggregates from notifications(type='domain_rejected')
  via Math.max — admin reject cascade-deletes submission row.
- blocker.tsx: useDomainSubmissionRealtime triggers blockerStats.refresh()
  directly (not stale-check only) so info-sheet shows fresh rejected count.
2026-06-01 02:23:27 +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
49e558902b fix(dm): always scroll to bottom on new messages and content-size changes
The smart isNearBottomRef gating was too restrictive — own sent messages,
image-loads, and incoming partner messages were sometimes not scrolled to.
Adopt the room-chat pattern: always scroll on messages.length change and
onContentSizeChange. Drop isNearBottomRef + firstContentSizeChangeRef +
onScroll handler.
2026-05-31 01:37:44 +02:00
chahinebrini
685782b538 fix(coach): dynamische Sprache (Text-Detection + App-Locale-Fallback)
LLM-Prompt (message.post + sos-stream):
- LANG_INSTRUCTIONS Map raus, ersetzt durch dynamische Instruktion
  'Reply in {detectedFromUser} ... fallback: {appLang}'
- Lyra matcht jetzt die Sprache der letzten User-Message (per
  detectLang Unicode-Detection); App-Locale ist nur noch Fallback
- Instruktion doppelt eingehängt (Anfang + Ende des System-Prompts)
  gegen recency bias bei langen deutschen Prompts

TTS (speak dispatcher + speak-cartesia + speak-elevenlabs):
- Kein 'de'-Default mehr für language. detectLang(text, locale) leitet
  Sprache primär aus dem Antwort-Text ab (Arabic/Cyrillic/CJK/Turkish-
  Letters), Locale als Fallback
- Cartesia + ElevenLabs: language/language_code nur senden wenn
  ableitbar, sonst Provider auto-detect statt erzwungenem 'de'
- speak-cartesia: sonic-2 → sonic-3 (Multi-Lang, war beim Dispatcher-
  Fix gestern vergessen worden)
- Google: en-US neutraler Fallback statt de-DE-Bias

Neu: server/utils/detect-lang.ts
2026-05-31 00:12:40 +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
89775838bc fix(chat): gedrückte Bubble scharf über Blur + Emoji-Ring entfernt
- MessageActionMenu: scharfe Preview-Kopie der gedrückten Bubble am Anker
  (bleibt über dem Blur sichtbar, WhatsApp-Stil) statt mitgeblurrt
- Reaktions-Leiste: kein Ring/Hintergrund mehr, aktives Emoji nur leicht größer
- Reaction-Pills: plain Emoji + Count ohne Hintergrund/Border

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 11:44:41 +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
2591b2a89c feat(chat): WhatsApp-Style Reaktions-/Aktions-Popup (DM) + Reaction-Pills
- MessageActionMenu: an der Bubble verankert (measureInWindow) statt zentriert,
  Blur-Backdrop, Emoji-Leiste oben (fremd) + Aktions-Liste unten (fremd:
  Antworten/Kopieren, eigen: Kopieren/Löschen)
- ChatBubble: Long-Press → measure + Menu, Reaction-Pills unter Bubble,
  Tombstone "Nachricht gelöscht"; ersetzt @expo-action-sheet
- dm.tsx: optimistisches Reaction-Toggle + Delete-Confirm + Realtime-Refetch
  (Reaction-Changes + Partner-Soft-Delete)
- useChatRealtime: DM-Hook lauscht zusätzlich auf reactions + message-UPDATE
- PostCommentsSheet: optimistisches Herz + Realtime-Subscription + größeres Icon
- i18n (de/en/fr/ar): chat.delete/message_deleted/delete_confirm_* + public_domain

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 11:18:51 +02:00
chahinebrini
4135e388ff ui(deploy): reorder progress bar so %, time stay visible on narrow terms
Old layout truncated the critical right-side (1m23s/~3m) on long labels.
New layout puts %, bar, and elapsed/eta on the LEFT, label+subtitle on
the RIGHT where truncation does no harm. Bar shrunk 20→15 chars.
2026-05-30 10:25:24 +02:00
chahinebrini
8f871611f1 feat(deploy): round ETA display (~8m statt ~8m00s)
Baseline aus tmp/.deploy-runtimes wird weiterhin per Run aktualisiert
(genauer Sekunden-Wert im Cache, nur Display gerundet).
2026-05-30 10:21:51 +02:00
chahinebrini
adc506291a fix(deploy): ERR-trap survives set -u (FUNCNAME may be unbound at top-level) 2026-05-30 10:16:11 +02:00
chahinebrini
6255006cad feat(deploy): clean Ctrl+C handler kills background xcodebuild
Without this, Ctrl+C would kill deploy.sh but leave xcodebuild
running as orphan, eating CPU and locking DerivedData.
2026-05-30 10:14:01 +02:00
chahinebrini
24a52a5bae fix(deploy): run_quiet works on bash 3.2 — drop subshell, toggle set -e around wait
- Background subshell '( cmd ) &' + 'wait' interacted badly with
  set -euo pipefail on macOS bash 3.2, killing the script silently
  before the progress-bar's error branch could run
- New approach: just 'cmd &' (no subshell), bracket the whole bg+wait
  region with 'set +e' / 'set -e', then check rc explicitly
- Also adds ERR-trap with call stack + RUN_QUIET_DEBUG=1 fallback
  (streams output directly via tee, useful for debugging build failures)
2026-05-30 10:12:10 +02:00
chahinebrini
9f8e99d287 fix(deploy): preserve rc under set -e (run_quiet swallowed errors silently)
'wait $pid' triggered 'set -e' before 'local rc=$?' could capture exit code,
so xcodebuild failures vanished without any log dump. Use 'wait $pid && rc=0 || rc=$?'
idiom to keep the script alive and let the error-dump branch run.
2026-05-30 10:05:10 +02:00
chahinebrini
061bd2d799 fix(deploy): allow flags before subcommand (./deploy.sh --skip-pods)
Treat $1 as subcommand only if it doesn't start with '-', else default to 'all'.
2026-05-30 10:03:11 +02:00
chahinebrini
77407f9d63 feat(deploy): --skip-pods flag + quiet clean-ios
- clean-ios.sh: new --skip-pods (or SKIP_PODS=1) skips prebuild+pod install
  (use when Pods are already fresh — saves ~30s)
- clean-ios.sh: new --quiet (or REBREAK_QUIET=1) suppresses end-of-run
  'Nächste Schritte' tips (cluttered deploy output)
- deploy.sh: new --skip-pods flag, auto-passes --quiet to clean-ios.sh
2026-05-30 10:01:41 +02:00
chahinebrini
b15ee42a85 fix(deploy): subtitle fallback + realistic runtime seeds
- subtitle now falls back to last non-empty log line when no Compile/Build
  action matched yet (so user sees activity during xcodebuild setup phase
  instead of empty bar at 0%)
- realistic seeds: 22min → 8min for xcarchive (typical RN archive)
2026-05-30 09:55:53 +02:00
chahinebrini
e6e1bab35a fix(deploy): single-line progress bar (no fragile cursor moves)
- previous 2-line render with \033[1A broke when xcodebuild crashed
  early — left mangled output and lost stderr
- new render_progress: ONE line, truncated to $COLUMNS, no cursor moves
- indeterminate mode (no baseline) shows ping-pong bar instead of spinner
- removes 2-line reserve + clear logic in run_quiet
2026-05-30 09:51:49 +02:00
chahinebrini
32d270ccad feat(deploy): brew-style time-based progress bar with runtime-cache
- new render_progress() draws ████████░░░░░░░░ 42% (1m23s / ~3m18s) bar
- runtime_lookup/save persist step durations in tmp/.deploy-runtimes
  (gitignored — auto-learns from successful runs)
- first run = spinner mode (no baseline yet), subsequent runs show real %
- still shows live xcodebuild action (Compiling X.swift) as subtitle
- format_duration helper: 45s / 1m23s readable output
2026-05-30 09:47:48 +02:00
chahinebrini
f48df2a968 chore(deploy): require ASC API-Key, drop app-specific-password fallback, brew-style spinner with live build action
- removes APPLE_APP_SPECIFIC_PASSWORD legacy branches (it never worked for xcodebuild -exportArchive anyway, only altool-upload)
- ASC API-Key now hard-required via require_asc_api_key preflight (fails fast with clear msg + path hint)
- run_quiet: spinner now tails the build log and shows current action (Compiling X.swift, Linking, CodeSign, etc.) as live subtitle — feels like brew/homebrew progress
- .env.deploy.local.example: drop unused fallback section
2026-05-30 09:46:38 +02:00
chahinebrini
b029c00413 chore(deploy): persist iOS auth via .env.deploy.local + ASC API-Key
- deploy.sh auto-sources apps/rebreak-native/.env.deploy.local (gitignored)
  and ~/.config/rebreak/deploy.env as fallback
- new helper xcodebuild_auth_args() injects -allowProvisioningUpdates +
  -authenticationKeyPath/ID/IssuerID into archive + both exportArchive calls
- ASC API-Key (free, .p8 from appstoreconnect.apple.com) is now the
  required path for exportArchive — app-specific-password no longer works
  for export since Xcode 14 (still used as altool-upload fallback)
- .env.deploy.local.example template added with one-time setup steps
- .gitignore: add *.p8 (.env*.local already covered)
2026-05-30 09:39:46 +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
38df6fc79d feat(chat): push notifications for DMs + rooms
Backend:
- Prisma PushToken model + chat_push_enabled flag on profiles
- Migration 20260530_add_push_tokens (push_tokens table + profile flag)
- Service sendChatPush with expo-server-sdk (auto-disable invalid tokens)
- Fire-and-forget push trigger in sendDirectMessage + createRoomMessage
- API POST /users/me/push-token (upsert) + DELETE (soft-disable)

Client (rebreak-native):
- usePushTokenRegistration hook: permission, getExpoPushTokenAsync,
  Android channel 'chat', POST to backend; idempotent per session
- Notification tap deep-link: dm -> /dm?userId, room -> /room?roomId

Deploy:
- run_quiet spinner for silent altool/xcodebuild/gradle phases
- Release-notes pipeline (--notes flag / NEXT_RELEASE.md / interactive)
  archived to CHANGELOG.md, printed with ASC + Play Console links
- Default version bump ON (--no-bump opt-out), build cleanup
- NEXT_RELEASE.md with push-notification release note
2026-05-30 08:16:45 +02:00
chahinebrini
e5eff6778f fix(ci): expo-blur in package.json behalten — Lockfile-Mismatch beheben
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>
2026-05-30 00:21:42 +02:00
chahinebrini
2cb1f8ad6e feat(binder-mac): SwiftUI Wizard für Self-Bind End-to-End-Flow
apps/rebreak-binder-mac/ — neue macOS-App die User durch den kompletten
Self-Bind-Prozess führt: Welcome → Preflight → Supervise → Enroll →
Configure (MDM-Push + Pre/Post-Check) → Sideload Lock-Profile (AirDrop).

3-Layer Smart-Resume: supervised? + Enrollment-Profil installed (cfgutil
Ground-Truth)? + MDM-Ack fresh (NanoMDM-DB via ssh+psql)?

Services: DeviceDetector (ideviceinfo + cfgutil), SuperviseRunner
(spawnt supervise-magic CLI), MDMClient (PUT /v1/enqueue?push=1, Apple
XML-Plist, identisch zum server-watcher-Format), MDMStatus (DB-Real-
Check + ManagedApplicationList-Result-Read).

Plus:
- fix(supervise-magic): EOF nach ProcessMessage Response (ErrorCode=0)
  ist Success, nicht Error — vermeidet false-fail bei iPhone-Restore-
  Reboot
- feat(mdm-profiles): rebreak-content-filter-mdm.mobileconfig als
  MDM-Push-Variante (ohne ConsentText, ohne globales allowAppRemoval=
  false — per-app via managed-state)

End-to-End validiert: App-Push via Ad-Hoc-Manifest (silent), Managed-
State via ManagedApplicationList-Query, NEFilter-Mode nach App-Force-
Quit, Lock-Profile non-removable nach Sideload.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 08:37:14 +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
8f2ef2cc98 feat(mdm,vip): MDM-VPN-Pivot + Layer-2-Country-Curated + Custom-Domain-Refactor
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>
2026-05-25 07:11:47 +02:00
chahinebrini
b6b1f68940 chore(release): URLFilter-Extension raus + Android versionCode 11 2026-05-22 22:19:52 +02:00
chahinebrini
9a22bcd114 fix(android-vpn): Blocklist-Self-Heal — kein Neustart nach erster Aktivierung
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>
2026-05-22 22:08:24 +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
0a0de3b75b feat(vip): Curated-Domain Suggest-UI
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>
2026-05-22 21:13:11 +02:00
chahinebrini
9455fec52b docs(faq): Schutz-FAQs auf Zwei-Schichten-Struktur aktualisiert
blocker.faq* + help.faq*: Layer 1 (URL-Filter ~330k) + Layer 2
(VIP-Liste/Zweitschutz), Custom-Domain-Permanenz; veraltete Einschicht-
und Free-Tier-Texte raus. de/en/fr/ar.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 21:09:00 +02:00
chahinebrini
27ad05b13b fix(vip): Swap-Dialog-Polish — Inline-Button, Sortierung, Badge-Farben
- VipSwapSheet: Ersetzen-Button inline an der gewählten Domain-Zeile
  statt Modal-Footer-CTA
- VipDomainList: zu ersetzende Domain nach oben sortiert, Cooldown-Badge
  deutlicher (Icon + Border)
- Status-Badge einheitlich grün, Swap-Domain orange (kein Blau mehr)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 21:09:00 +02:00
chahinebrini
f555c5e4d8 feat(vip): Tunesien (TN) als VIP-Land + kuratierte Starter-Liste
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>
2026-05-22 20:52:20 +02:00
chahinebrini
48a8bbc4af fix(chat): Conversation auf inverted FlatList — Scroll-to-bottom bulletproof
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>
2026-05-22 20:33:34 +02:00
chahinebrini
517ce8658f fix(vip): VipSwapSheet erst nach AddDomainSheet-Dismiss präsentieren
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>
2026-05-22 20:29:36 +02:00
chahinebrini
bee1d9900a feat(vip): VIP-Slot-Replace Frontend — Swap-Dialog + Cooldown-Badge
- 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>
2026-05-22 20:07:36 +02:00