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

212 lines
8.9 KiB
TypeScript

import { ExpoConfig, ConfigContext } from "expo/config";
import pkg from "./package.json";
// ─── Dev-Variant-Flag ─────────────────────────────────────────────────────────
// REBREAK_DEV=1 → separates Bundle-org.rebreak.app.dev, App-Name "ReBreak Dev".
// Ermöglicht parallele Installation neben der MDM-managed Dist-App.
// Produktions-Builds (deploy-adhoc.sh, deploy-tf.sh, EAS) setzen REBREAK_DEV
// NICHT → landen automatisch auf dem Prod-Bundle.
//
// Verwendung: REBREAK_DEV=1 ./dev-build.sh
const IS_DEV = process.env.REBREAK_DEV === "1";
const PROD_BUNDLE = "org.rebreak.app";
const DEV_BUNDLE = "org.rebreak.app.dev";
const MAIN_BUNDLE = IS_DEV ? DEV_BUNDLE : PROD_BUNDLE;
export default ({ config }: ConfigContext): ExpoConfig => ({
...config,
name: IS_DEV ? "ReBreak Dev" : "ReBreak",
slug: "rebreak",
version: pkg.version,
orientation: "portrait",
icon: "./assets/icon.png",
scheme: IS_DEV ? "rebreak-dev" : "rebreak",
userInterfaceStyle: "automatic",
newArchEnabled: true,
splash: {
// Branded Full-Bleed-Splash (Chain-Break-Logo auf Navy-Gradient) statt der
// weißen icon.png-Box. backgroundColor matcht die dunklen Ecken von splash.png
// → Letterbox blendet nahtlos.
image: "./assets/splash.png",
resizeMode: "cover",
backgroundColor: "#0f172a",
},
ios: {
supportsTablet: true,
bundleIdentifier: MAIN_BUNDLE,
buildNumber: "48",
// Apple Sign-In Entitlement — Pflicht für expo-apple-authentication nativen
// signInAsync()-Flow. Ohne flag generiert Expo's prebuild den
// com.apple.developer.applesignin-Entitlement nicht in die .entitlements.
usesAppleSignIn: true,
config: {
usesNonExemptEncryption: false,
},
infoPlist: {
ITSAppUsesNonExemptEncryption: false,
NSMicrophoneUsageDescription:
"Rebreak nutzt das Mikrofon für Sprachnachrichten an Lyra.",
NSPhotoLibraryUsageDescription:
"Rebreak greift auf Fotos zu, damit du sie in deinen Posts teilen kannst.",
NSPhotoLibraryAddUsageDescription:
"Rebreak speichert Bilder in deine Foto-Mediathek.",
NSFaceIDUsageDescription:
"Rebreak nutzt Face ID, um die App zu entsperren — damit niemand außer dir sie öffnen kann.",
},
},
android: {
package: "org.rebreak.app",
versionCode: 37,
adaptiveIcon: {
// Foreground muss in der ~66%-Safe-Zone bleiben (Launcher-Mask clippt den
// Außenring) → adaptive-foreground.png ist das Logo auf transparentem
// 1024er-Canvas mit ~19% Padding. Hintergrund weiss → matcht den
// Play-Console-Look (dunkles Logo auf Weiss), statt dunkel-auf-dunkel.
foregroundImage: "./assets/adaptive-foreground.png",
backgroundColor: "#ffffff",
},
permissions: [
"INTERNET",
"ACCESS_NETWORK_STATE",
"BIND_VPN_SERVICE",
"FOREGROUND_SERVICE",
"POST_NOTIFICATIONS",
"BIND_ACCESSIBILITY_SERVICE",
"RECORD_AUDIO",
],
},
plugins: [
"expo-router",
"expo-localization",
"expo-font",
"expo-web-browser",
[
"expo-build-properties",
{
ios: {
deploymentTarget: "15.1",
useFrameworks: "static",
},
android: {
minSdkVersion: 26,
compileSdkVersion: 36,
targetSdkVersion: 36,
},
},
],
// Xcode 16 + RN 0.79 fmt consteval workaround
"./plugins/with-fmt-consteval-fix",
// Xcode 14+ resource-bundle-signing fix (needed because useFrameworks: static)
"./plugins/with-resource-bundle-signing-fix",
// Phase 5: NEFilter Extension + Family Controls Entitlements (iOS)
"./plugins/with-rebreak-protection-ios",
// Phase 5: VpnService + AccessibilityService (Android)
"./plugins/with-rebreak-protection-android",
// Rive-Asset (lyra-avatar.riv) als Android raw-resource bundlen
"./plugins/with-rive-asset-android",
// MaterialComponents-Theme-Fix für BadgeDrawable in react-native-bottom-tabs
"./plugins/with-material-theme-android",
// Release-Signing-Block in build.gradle (liest android/key.properties — nicht committen)
"./plugins/with-release-signing-android",
],
experiments: {
typedRoutes: true,
},
extra: {
eas: {
projectId: "a4f2186e-8ca5-4d38-921d-82ae96c9c086",
// EAS muss VOR dem Build wissen, dass es eine App-Extension gibt — sonst
// generiert es nur Credentials für die Haupt-App und der Xcode-Build kippt
// mit "No profiles for 'org.rebreak.app.URLFilterExtension' were found".
// Bundle-ID + Entitlements müssen exakt zu plugins/with-rebreak-protection-ios.js
// und modules/rebreak-protection/ios/RebreakURLFilterExtension/ passen.
build: {
experimental: {
ios: {
appExtensions: [
{
// Layer 1 (Unsupervised-Pfad) — Packet-Tunnel-DNS-Filter.
// Bundle-ID + Entitlements müssen exakt zu
// plugins/with-rebreak-protection-ios.js (PT_BUNDLE_SUFFIX)
// und modules/rebreak-protection/ios/RebreakPacketTunnelExtension/
// passen, sonst kippt der EAS-Build mit
// "No profiles for '...PacketTunnelExtension'".
// IS_DEV: → org.rebreak.app.dev.PacketTunnelExtension
targetName: "RebreakPacketTunnelExtension",
bundleIdentifier: `${MAIN_BUNDLE}.PacketTunnelExtension`,
entitlements: {
"com.apple.developer.networking.networkextension": [
"packet-tunnel-provider",
],
"com.apple.security.application-groups": [
"group.org.rebreak.app",
],
},
},
{
// Layer 1 (Supervised-Pfad) — klassisches NEFilterDataProvider.
// Wird auf MDM-supervised Geräten statt PacketTunnel aktiviert,
// damit der User keinen VPN-Toggle in iOS-Settings hat. Bundle-ID
// + Entitlements müssen zu plugins/with-rebreak-protection-ios.js
// (CF_BUNDLE_SUFFIX) und modules/rebreak-protection/ios/
// RebreakContentFilter/ passen.
// IS_DEV: → org.rebreak.app.dev.ContentFilterExtension
targetName: "RebreakContentFilter",
bundleIdentifier: `${MAIN_BUNDLE}.ContentFilterExtension`,
entitlements: {
"com.apple.developer.networking.networkextension": [
"content-filter-provider",
],
"com.apple.security.application-groups": [
"group.org.rebreak.app",
],
},
},
],
},
},
},
},
// Family Controls (denyAppRemoval via ManagedSettings) braucht ein
// DISTRIBUTION-Entitlement das noch bei Apple zur Freigabe liegt. Ohne das
// Entitlement blockt iOS den ManagedSettingsAgent-XPC auf Sandbox-Ebene
// (NSCocoaErrorDomain:4099 "Sandbox restriction" — verifiziert in Build 11).
//
// Der Wert MUSS mit dem Entitlement-Gating im Plugin übereinstimmen:
// plugins/with-rebreak-protection-ios.js setzt das family-controls-Entitlement
// mit derselben Bedingung. Family Controls ist Kern-Funktion und Apples
// Distribution-Entitlement ist freigegeben (v0.3.4) → DEFAULT AN. Nur ein
// explizites `REBREAK_ENABLE_FAMILY_CONTROLS=0` schaltet es ab (Escape-Hatch
// für FC-lose Test-Builds). Konsequenz: jeder Build braucht ein
// Provisioning-Profil mit FC-Capability — für den ReBreak-Apple-Account gegeben.
familyControlsEnabled: process.env.REBREAK_ENABLE_FAMILY_CONTROLS !== "0",
// iOS-NEURLFilter / PIR-Server. Der Auth-Token kommt aus der Build-Umgebung
// (Infisical staging/PIR_AUTH_TOKEN) — NIEMALS committen. Lokaler Dev-Build:
// `PIR_AUTH_TOKEN=… npx expo run:ios --device`.
pirServerURL: process.env.PIR_SERVER_URL || "https://pir.staging.rebreak.org",
pirAuthToken: process.env.PIR_AUTH_TOKEN || "",
apiUrl:
process.env.EXPO_PUBLIC_API_URL ||
process.env.API_URL ||
"https://staging.rebreak.org",
// TEMP: Staging Anon-Key + URL hardcoded für lokales Dev-Testing.
// Anon-Key ist designed für Client-Ship (RLS protectiert DB). Trotzdem:
// BFF-Migration kommt in Phase 5 — dann fliegen diese 2 Zeilen wieder raus.
supabaseUrl:
process.env.EXPO_PUBLIC_SUPABASE_URL ||
process.env.SUPABASE_URL ||
"https://db-staging.rebreak.org",
supabaseAnonKey:
process.env.EXPO_PUBLIC_SUPABASE_ANON_KEY ||
process.env.SUPABASE_KEY ||
process.env.SUPABASE_ANON_KEY ||
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsImF1ZCI6ImF1dGhlbnRpY2F0ZWQiLCJyb2xlIjoiYW5vbiIsImV4cCI6MjA5MTAxODk1NSwiaWF0IjoxNzc1NjU4OTU1fQ.93d2r3pft2E-alf1JezqueD0l0n1dim7dGvhBN0l1Cs",
},
});