513 Commits

Author SHA1 Message Date
chahinebrini
7ce5e58f8d fix(backend): fix imapflow bundling — inline-Funktion statt fragiler Regex
Die inline-Regex /^(?!imapflow)/ griff nur gegen bare Specifier, nicht
gegen aufgelöste absolute Pfade (z.B. /node_modules/.pnpm/imapflow@x/…).
Nitro prüft inlineMatchers VOR externalMatchers — bei absoluten Pfaden
startete der String mit "/" → Lookahead ^(?!imapflow) schlug durch →
imapflow wurde gebundelt → imap-flow.mjs-Chunk → util.inherits-Crash
→ scan-internal 500 (Incident 2026-06-05, Regression in 1493752).

Fix: inline als Funktion mit expliziter blocklist (imapflow, expo-server-sdk,
@supabase/supabase-js). Deckt bare Specifier UND aufgelöste node_modules-Pfade.
Lokal verifiziert: imap-flow.mjs-Chunk weg, alle 3 Packages in server/package.json.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 15:35:52 +02:00
chahinebrini
c4fe7d356f fix(rebreak-native): iOS app-lock freeze (Face ID stuck, needs hard-quit)
LockScreen could latch on the locked screen with no working Face ID
prompt until a hard-quit. Two coupled causes:
- inFlight/busy could stay latched if authenticateAsync hung when called
  mid active↔inactive transition → gate tryUnlock on AppState 'active'
  and release the latch on background (iOS tears down the sheet anyway).
- foreground recovery missed background→inactive→active (prev was
  'inactive' at the active event) → track "was backgrounded since last
  active" instead, so re-prompt fires reliably (this is why only
  hard-quit recovered it). The Face ID sheet's own active→inactive→active
  no longer re-triggers (only real 'background' arms the flag).
- appLock.authenticate wraps authenticateAsync in try/catch so a native
  reject always settles (the LockScreen finally relies on it).

cancelAuthenticate() is Android-only (no iOS native impl) — fix works by
prevention + self-heal, not cancellation. Frontend-only; needs a native
rebuild, no deploy.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 15:11:12 +02:00
chahinebrini
adb686d9a3 ci(deploy-staging): bump Node heap to 4GB for nitro build (fix OOM)
nitro build OOM'd on the runner (exit 134, "ineffective mark-compacts
near heap limit") — Node's default ~2GB heap was exceeded as the build
grew. Runner has 7GB RAM, so raise --max-old-space-size to 4096. This
unblocks the backend deploy (IMAP crash + dm-push fixes).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 15:06:34 +02:00
chahinebrini
1493752634 fix(backend): IMAP ECONNRESET crash-loop + dm-push ESM interop
- mail/scan{,-internal}.post.ts + imap-idle: attach imap.on('error')
  + targeted uncaughtException/unhandledRejection guards so a
  connection-level IMAP error (ECONNRESET / TLS disconnect) can no
  longer propagate to a process-level uncaughtException and kill the
  Nitro API (root cause of the staging 502 crash-loop)
- services/push.ts: lazy dynamic-import expo-server-sdk (singleton,
  like voip-push.ts) to fix "Class extends value [object Module]"
  (ESM/CJS undici interop) that broke DM push notifications;
  + nitro.config externals safety net

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 14:59:37 +02:00
chahinebrini
1f20056ef3 chore(graphify): graph.html untracken (Hook-unmaintainbar bei >5k Knoten), manifest syncen
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 13:35:27 +02:00
chahinebrini
861b830330 chore(graphify): Graph-Memory nach Hook-Rebuild syncen + Backup-Dirs ignorieren
- graph.json (27 Traceability-Kanten verifiziert erhalten) + GRAPH_REPORT.md aktuell
- .gitignore: dated Hook-Backup-Ordner (graphify-out/YYYY-MM-DD/) ausgeschlossen
Commit mit GRAPHIFY_SKIP_HOOK=1 (kein Rebuild-Loop).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 13:34:32 +02:00
chahinebrini
6937ff155f docs(claude): Git-Hook (post-commit) als Auto-Update-Mechanismus dokumentiert
Schliesst die Stale-Luecke bei fremden Sessions (VS Code etc.): graphify hook
rebuildet den Graph nach jedem Commit/Branch-Wechsel. Hook ist pro Maschine/Clone.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 13:30:35 +02:00
chahinebrini
f81978362e docs(claude): graphify als Hartregel — Graph-first Memory + Auto-Update ohne explizite Aufforderung
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 13:21:07 +02:00
chahinebrini
c82c94e457 chore(graphify): Graph als Memory syncen, Scratch-/Cache-Muell untracken
- 751 versehentlich getrackte Scratch-/Cache-Dateien (.chunk_*, .graphify_*, cache/ast/)
  aus Git entfernt; graphify-out/.* + graphify-out/cache/ in .gitignore
- Getrackt bleibt die echte Memory: graph.json (mit 27 Traceability-Kanten),
  GRAPH_REPORT.md, graph.html, manifest.json, memory/ (gespeicherte Query-Antworten)
- CLAUDE.md: graph-first Workflow geschaerft (Graph nennt Dateien -> gezielt 1-2 lesen
  statt grep) = der eigentliche Token-Spar-Mechanismus

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 13:16:52 +02:00
chahinebrini
802a7cb9a3 docs: CLAUDE.md mit graphify-Nutzungs- + Token-Disziplin-Regeln
Knowledge-Graph (graphify-out/graph.json) fuer Codebase-/Architektur-/Traceability-
Fragen konsultieren; graph.json nie in Kontext laden; --update code-only halten.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 12:49:23 +02:00
chahinebrini
577a478c2d docs(diga): Dok 05 (Architektur+SOUP) + 05d (Traceability-Matrix) v0
- 05: Software-Lifecycle/Architektur/SOUP, code-belegt aus package.json-Manifesten
  + graphify-Graph; IEC-62304-Klassifizierungs-Vorschlag (B, ggf. C Krisen-Pfad)
- 05d: Traceability Anforderung<->Risiko<->Code fuer Lyra- + Schutz/Selbstbindungs-
  Strang (27 graph-abgeleitete traceability-Kanten)
- 00: 05 + 05d im Dossier-Plan registriert
- 05b: Gap 'Fehlende Traceability-Matrix' verweist jetzt auf 05d
Alle v0-Entwuerfe; Regulatory-/QM-Validierung ausstehend.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 12:49:23 +02:00
chahinebrini
80b6e2399b fix(marketing): leere /preview-Seite (nacktes <template> rendert nicht)
Beim Entfernen des Client-Gates blieb ein <template> ohne Direktive als Wrapper
um den gesamten Inhalt stehen. Vue 3 rendert ein direktivenloses <template> an
dieser Stelle nicht → komplett leere Seite. Wrapper entfernt, Inhalt liegt jetzt
direkt im Root-<div>. Verifiziert: Header + Hero rendern in Safari.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 05:27:24 +02:00
chahinebrini
5fb441817f feat(magic): RE-hardening Quick Wins (ACL, #if DEBUG guards, rate-limit)
Härtung der öffentlich downloadbaren Magic-Apps gegen Reverse Engineering
(Assessment: docs/specs/magic-re-hardening.md):
- Windows: protection.json per ACL auf SYSTEM+Admins (DNS-Token nicht mehr von
  Standard-Usern lesbar) — setup.rs
- Mac: MacProfileInstaller.remove() + Debug-Supervision-Modi/Reset nur noch
  #if DEBUG (kein Removal-/Debug-Pfad im Release-Binary)
- Mac: staging-URL einmal als Konstante statt 4x Literal; interne Infra-Notizen
  aus String-Literalen raus
- Backend: Rate-Limit (10/IP/min) auf /api/magic/pair/redeem

NUR Backend-Teil deployt via Push; Mac/Win brauchen Xcode-/Cargo-Release-Build
(zied) + Smoke-Tests vor Release. MagicAPIClient.swift trägt etwas vorbestehenden
WIP mit (gleiche Magic-Client-Domäne).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 05:19:10 +02:00
chahinebrini
8da782339e refactor(marketing): /preview-Zugriffsschutz via nginx Basic-Auth statt Client-Gate
Client-seitiges Passwort-Gate entfernt: schrieb das Passwort ins offen
erreichbare JS-Bundle (= Leak, identisch zum nginx-PW) und verursachte doppelte
Passworteingabe. Schutz läuft jetzt serverseitig via nginx auf /preview (Seite +
Bilder). Ein Passwort, kein Leak.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 05:03:14 +02:00
chahinebrini
9fdb0d3a2e feat(marketing): passwortgeschützte /preview App-Vorschau für FAGS/Ilona
Private, noindex App-Tour mit Client-seitigem Passwort-Gate (Cookie). 16 echte
Screenshots (Onboarding, Blocker, Block-Seite, Lyra, Atemübung, Mail-Schutz +
-Connect, Community-Feed/Chat/Anruf, Streak, Geräte, Eigene Domains, Admin-
Moderation) via PreviewPhone-Mockups. Admin-Shot als Handy-Rahmen (PreviewPhone).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 04:39:02 +02:00
chahinebrini
4987d05f10 fix(native): per-device bypass gate + 300k copy + locale/marketing polish
- protection.ts: gate recoveringFromBypass on a local 'everActiveHere' flag,
  set after a successful activateUrlFilter(). The 'protection off' sheet + tamper
  push no longer fire on fresh devices/sims where protection was never activated
  locally. Root cause: backend protectionShouldBeActive is the account DEFAULT
  (only false after a held cooldown), not an 'ever active' signal.
- locales: blocklist size 208k -> 300k (native de/en/fr/ar + marketing de/en).
- bundles already-deployed prior polish living in these files: native wording
  (clean->spielfrei, DiGA wording), marketing de-AI pass (live on prod).
- NEXT_RELEASE.md seeded for the next app release.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 03:05:16 +02:00
chahinebrini
547f86187b fix(magic): createAdGuardClient idempotent — 400 → clients/update
Verwaiste AdGuard-Clients (magic_<deviceId> existiert, aber DB-Row fehlt nach
Crash zwischen clients/add und DB-Upsert) führten beim Re-Register zu 400 → 502.
Jetzt: bei 400 auf clients/update zurückfallen und den bestehenden Client auf
die frisch generierte clientId umbiegen. Behebt Magic-Register-502.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 02:45:28 +02:00
chahinebrini
579eb5b5e0 fix(deploy): ENOTEMPTY-Halbwipe von android/ verhindern
clean-ios.sh rief 'expo prebuild --clean' ohne --platform → wollte auch android/
löschen. Hält ein Gradle-Daemon android/build|.gradle offen, failt 'rmdir android'
mit ENOTEMPTY und hinterlässt ein halb-gewischtes android/ (Source weg, gelocktes
build/ bleibt) → späterer Release-Build failt mit "autolinking.json doesn't exist".

- clean-ios.sh: 'prebuild --clean --platform ios' → android/ wird beim iOS-Clean
  gar nicht mehr angefasst.
- deploy.sh: release_android_locks() (gradlew --stop + GradleDaemon-kill + rm
  build/.gradle) läuft vor dem android-prebuild --clean in ensure_native_dir.

Ergänzt den ensure_native_dir-Self-Healing-Fix (fe6a63b): jetzt wird der Halbwipe
nicht nur erkannt+repariert, sondern an der Quelle verhindert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 08:00:20 +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
0bff819b09 fix(tts): Legend übergangsweise auf Cartesia (ElevenLabs payment_issue 401)
ElevenLabs-Account hat ein Zahlungsproblem → Legend-TTS bekäme 502.
Leite Legend bis zur Klärung auf Cartesia sonic-3 (unlimited), damit
Legend-User Sprachausgabe behalten. Revert-Config steht im Kommentar.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 03:04:17 +02:00
chahinebrini
ca72437f18 fix(native): zwei Circles + animierter Gesamt-Verteilungs-Balken drunter
Statt Half-Donut (Höhen-Mismatch mit Circles): zwei volle Circles (Mobil/Computer)
+ darunter ein eigener animierter Balken (grün/blau-Segmente, gleiche Easing/Dauer
wie die Ringe) mit Legende. Kein native-Default.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 00:36:04 +02:00
chahinebrini
c3478f4743 fix(native): Gesamt-Verteilung als Half-Donut, 2 Circles + 1 Donut (gleiche Größe)
DeviceSlotDonut bekommt half-Modus. Reihenfolge: Mobil-Circle, Computer-Circle,
Gesamt-Half-Donut (Mobil/Computer-Anteil als zwei Bögen). Alle SIZE×SIZE.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 00:30:08 +02:00
chahinebrini
77ce5e5a80 feat(native): dritter 'Gesamt'-Ring mit Mobil/Computer-Verteilung
DeviceSlotDonut auf Segment-API umgestellt (Mehr-Segment-Bögen). Gesamt-Ring
zeigt belegte Slots gesamt + farbliche Verteilung (Mobil grün / Computer blau).
Labels gekürzt (Mobil/Gesamt/Computer) für 3-across-Layout.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 00:25:40 +02:00
chahinebrini
227c30c3c9 fix(native): Slot-Ringe kleiner+dicker+langsamer, 'Lückenloser Schutz'-Text raus
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 00:18:41 +02:00
chahinebrini
e2e5a1003c feat(native): Geräte-Slots als Progress-Ringe + Status-Pill in der Liste
- Slots: zwei animierte volle Progress-Circles (Mobil/Computer) statt Balken,
  via react-native-svg (keine neue Lib)
- Status-Zeile pro Gerät: Online (grün) / Cooldown · noch Xh (amber, aus
  releaseRequestedAt) / Ungeschützt (rot) — ersetzt Footer + StatusBadge

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 00:17:05 +02:00
chahinebrini
e0eb1711db feat(native): Geräte-Liste informativ — Trash/Menü raus, chevron-forward → Detail
Entfernen passiert am Gerät selbst (Cooldown, win-App/Mac), nicht aus der
Liste gesteuert. Row-Pfeil öffnet nur das Info-/Detail-Sheet.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 00:05:23 +02:00
chahinebrini
db6db547ff fix(magic): DNS-Token auf 48 hex kürzen (AdGuard 63-char clientid-Limit)
randomBytes(32).hex = 64 Zeichen → AdGuard lehnt clientid ab
('hostname label is too long: got 64, max 63') → 'DNS-Provisioning
fehlgeschlagen' bei jeder Magic-Registrierung. Live gegen AdGuard
reproduziert: 64→HTTP400, 62→HTTP200.

- register: randomBytes(24).hex = 48 Zeichen (192 bit, ≤63)
- status: Token-Regex {64} → {1,63} (Länge nicht hart koppeln)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 23:33:56 +02:00
chahinebrini
4dfcfc4012 feat(marketing): 14-Tage-Trial-Klarheit + OS-aware Magic-Download
- Hero/Final-CTA: 'kostenlos starten' -> '14 Tage kostenlos testen' + Subline
  'danach ab 3,99€/Monat, jederzeit kündbar' (löst free-vs-Preis-Verwirrung;
  Trial existiert bereits Stripe-seitig + Pricing nennt ihn)
- useOS()-Composable + OS-aware RebreakMagic-Download: Windows-Besucher ->
  /download/windows, Mac/sonst -> /download/rebreakmagic

Deployed: rebreak.org via deploy-marketing.sh

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 23:03:37 +02:00
chahinebrini
e0cb0517fc feat(marketing): cross-device pricing-hero + Header-Nav statt Tabbar (live prod)
- Pricing: Device-Hero-Reihe (iPhone/Android/Mac/Windows) + Tagline 'ein Abo,
  alle Geräte' — heroicons (offline gebündelt)
- Layout: Floating-Tabbar raus -> Header-Nav (Desktop) / Hamburger (mobil)
- Locales: cross_device_tagline + Geräte-Matrix-Texte

Deployed: rebreak.org (marketing-prod) via deploy-marketing.sh

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 22:56:14 +02:00
chahinebrini
7ad8625d8e feat(marketing): Windows-PC-Schutz Download-Seite + Installer
- /download/windows: eigene, korrekt geframte Windows-Seite (PC-DNS-Schutz,
  nicht iPhone-Supervision)
- Installer in public/downloads/RebreakMagic-Setup.exe (3,7 MB, aus CI-Artefakt)
- deploy-marketing.sh: --info=progress2 -> --progress (macOS-rsync-kompatibel)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 22:40:25 +02:00
chahinebrini
2c1eecd1f7 feat(native): geräte-spezifische PNG-Icons (iphone/android/macbook/computer)
deviceImage()-Helper mappt Plattform→assets/devices/*.png; ersetzt Ionicons
in Geräte-Rows, MagicSheet und Detail-Sheet.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 22:40:25 +02:00
chahinebrini
a95e66560d feat(magic): Hard-Lock + Geräte-UX (Push, Realtime, Detail-Sheet, Offline-Removal)
Devices/Magic:
- Offline-Profil-Enroll deaktiviert (410) — Lock-PW würde im Klartext im
  Download landen; stationärer Schutz läuft jetzt nur über Rebreak Magic
- Mac-DNS-Template: ProhibitDisablement (Filter nicht abschaltbar)
- Push "Neues Gerät verbunden" an mobile Geräte bei neuer Bindung
- Realtime auf user_devices → Settings aktualisiert Magic-Bindings live
- Geräte-Detail-Sheet (Tap auf Gerät): Status, verbunden-seit, Schutz-Donut

Hard-Lock (server-gehaltenes Removal-PW, User sieht es nie):
- magic_removal_password generiert/gespeichert + in Profil injiziert (Lazy-Backfill)
- Reveal NUR bei Account-Löschung (user/delete) + Kündigung (stripe webhook),
  per Resend-Mail + in-Response
- Signing config-gated (inaktiv ohne Cert; Lock greift auch unsigniert)

Migrations: user_devices-Realtime-Publication + magic_removal_password-Spalten

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 22:26:25 +02:00
chahinebrini
869d8afd30 fix(magic-win): keyring plattform-spezifisch (apple-native bricht Windows-Build)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 13:45:04 +02:00
chahinebrini
771af0faf1 ci(magic-win): pnpm-version aus packageManager statt explizit (fix Multiple-versions)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 13:42:53 +02:00
chahinebrini
4b4b9fc63b feat(magic-win): ReBreak Magic Windows-App (Tauri) + CI-Build
Tauri 2 DoH-Schutz für Windows: PowerShell-DoH-Takeover, Tamper-Service
(SYSTEM, windows-service), Browser-Policies (Chromium built-in-DNS + eigenes
DoH aus → OS-Resolver), 24h-Cooldown via bestehende magic/*-Endpoints.
GitHub-Actions baut den x64-NSIS-Installer auf windows-latest.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 13:39:52 +02:00
chahinebrini
80be124592 fix(dns): blocklist als ||domain^ statt apex-only → blockt Subdomains
Apex-only-Format (bare domain) blockte nur z.B. bet365.com, NICHT
www.bet365.com — Casino-Content + Google-Ad-Links liegen aber auf www.
Adblock-Syntax ||domain^ deckt Domain + alle Subdomains ab.
Betrifft alle DNS-Plattformen (Mac/Windows/DoH-Server).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 13:26:16 +02:00
chahinebrini
7f529c3be3 feat(privacy): Coach-Payload an LLM-Provider pseudonymisieren (Art.9/DSGVO)
Schliesst hans-muellers K1-Befund (Datenschutz-Audit): der Coach-Prompt
sendete Identifier + Art.9-nahe Daten an US-LLMs (Gemini/OpenAI/Anthropic).

- message.post.ts: Geburtsjahr/exaktes Alter -> Altersgruppe (Dekaden-Bucket);
  Stadt komplett entfernt (Bundesland bleibt). Geschlecht/Familienstand/Beruf/
  Nickname unveraendert (gewollte Personalisierung; Nickname = Pseudonym).
- lyraMemoryExtract.ts: Extraction-Prompt reduziert Dritt-Klarnamen auf Rolle
  ("Frau Maria" -> "seine Frau"), keine Orte/Arbeitgeber im Memory-Content.
- 08-datenschutz-audit: Payload-Audit-Platzhalter durch Vorher/Nachher-Tabelle
  ersetzt, K1 erledigt, ZDR-Update (DPA/SCCs deemed-signed, TIA offen).

Pseudonymisierung zaehlt jetzt als zweite Schutzmassnahme neben ZDR fuer die TIA.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 08:35:13 +02:00
chahinebrini
96e1b8368c feat(lyra): deterministisches Krisen-Sicherheitsnetz (R-LYRA-01)
LLM-unabhaengiges Sicherheitsnetz fuer Lyras SOS-Pfad, schliesst das
Top-Risiko der Risiko-Akte (verpasste Krise, ISO 14971 R-LYRA-01).

Backend:
- crisis-filter.ts: deterministische Krisen-/Suizid-Erkennung (DE primaer,
  EN/FR/AR Grundabdeckung) auf den letzten User-Nachrichten, synchron, kein LLM
- sos-session.post: liefert crisisLevel sofort an die App (vor Stream-Start)
- sos-stream: sendet bei Krise zuerst 'crisis_chips' (BZgA/112/Telefonseelsorge);
  Fallback an 3 Stellen (LLM-Fehler/Abbruch/keine Chips) -> nie leerer Screen
- 43/43 Unit-Tests (crisis.json positiv, harmless.json False-Positive-Guard)

Frontend (urge.tsx):
- permanente rote Krisen-Bar oben, durch LLM-Chips nicht ueberschreibbar
  (eigener State-Slot), Hotline-Chips als tel:-Links
- neue Locale-Strings DE/EN

Risiko-Akte: R-LYRA-01 Restrisiko HOCH -> MITTEL.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 07:56:34 +02:00
chahinebrini
ac05e255da feat(diga): Technische-Akte Runde 1+2 — Requirements, Risiko-Akte, Datenschutz-Audit, Lyra-Eval
DiGA-Dossier weiter aufgebaut (docs/specs/diga/):
- 03 Requirements (57 REQ-IDs aus dem Code, Traceability-Anker)
- 04 Risiko-Akte (ISO 14971 Erstliste; R-LYRA-01 = verpasste Krise als Top-Risiko)
- 05b Test-Verifikation (Maestro/Vitest-Inventar, IEC-62304-Luecken)
- 05c Lyra-Eval (Suite-Doku)
- 08 Datenschutz-Audit (hans-mueller; Groq/Art.9, DSFA-Pflicht, Mail-Agent, Anonymitaet)
- 00 Dossier-Plan Status aktualisiert

Lyra-Eval-Suite: backend/tests/eval/ (30 Prompts, 5 Kategorien, Vitest-Runner,
Mock-Modus ohne Key; Live-Run misst Crisis-Recall).

Konvergenter Befund aller 3 Agents: Lyras Krisen-Pfad haengt zu sehr am LLM
(R-LYRA-01 + fehlender SOS-Handler-Fallback) -> deterministisches Sicherheitsnetz noetig.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 07:38:11 +02:00
chahinebrini
90f63eb21e feat(diga): Regulatory-Doku-Agent + Technische-Akte-Start (Zweckbestimmung)
DiGA/MDR-Vorbereitung als eigener Workstream:
- Neuer Agent 'diga-regulatory' (Dr. Marlene Brandt) — owns docs/specs/diga/,
  draftet die Technische Akte (Zweckbestimmung, MDR-Klassifizierung, ISO 13485/14971,
  IEC 62304, klinische Bewertung, Labeling, PMS, QMS-Templates). Ergänzt hans-mueller
  (Datenschutz) + rebreak-strategist (Business); validiert nicht selbst (Profi-Grenze).
- docs/specs/diga/00-dossier-plan.md — Plan + Arbeitsteilung (Claude draftet, Gründer
  entscheidet, Profi validiert) + 10-Dokumente-Landkarte mit Status.
- docs/specs/diga/01-zweckbestimmung-v0.md — Keystone-Entwurf (Intended Use, Indikation
  F63.0, 3-Ebenen-Wirkmechanismus, Abgrenzung kein Therapieersatz, Klassifizierungs-Hinweis).

Naechste Aufgaben fuer den Agent (aus dem Code, ohne Gruender-Input): 03 Requirements,
04 Risiko-Erstliste, 05 SOUP + Architektur.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 06:39:13 +02:00
chahinebrini
eb95258b5e docs(marketing): Rebreak Magic (Selbstbindung) als 4. USP in alle 3 Mails
iOS no-erase Supervision als therapeutisches Selbstbindungs-/Commitment-Device-
Argument formuliert (Schutz im Drang-Moment nicht löschbar, nur via Vertrauens-
person/Cooldown, ohne Geräte-Reset). Maßvoll positioniert: in DE/für Glücksspiel-
sucht einzigartig, gratis im Legend-Tier (vs. kostenpflichtige US-Tools wie TechLockdown).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 06:08:17 +02:00
chahinebrini
db8e38ae6c docs(marketing): FAGS+NLS Förder-/Partnerschafts-Plan + 3 Mail-Entwürfe
Konkreter Go-to-Market-/Funding-Plan für die Marketing-Phase:
- Strategie: 2 Knoten (FAGS/Ilona + NLS Hannover), Geld-Mechanik (Modellprojekt →
  gemeinnütziger Träger → Rebreak als bezahlter Partner), Win-Win pro Partner.
- Sequenz: erst echte Daten + Fachstellen-Validierung (Phase 1), dann Förderung
  via NLS/NDS-Modellprojekt (Phase 2). Daten machen den Förder-Pitch glaubwürdig.
- Plan B/C: nicht single-threaden auf Ilona — STEP gGmbH / Lukas-Werk / NLS parallel.
- GmbH-Reframe: keine 25k Cash nötig (UG/Sachgründung), Kredit erst später.
- 3 sende-fertige Mails: Ilona (warm, ohne Förder-Ask), Lukas-Werk/STEP (Testphase
  = echte Daten), NLS (digitale Ergänzung zu abgezockt! + Modellprojekt).
- Gamban-Vergleich + USP-Hervorhebung (IMAP-IDLE-Mail-Schutz, Lyra).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 06:02:54 +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
6a3c1e13da feat(lyra): admin DiGA-reminder post category
New 'erinnerung' topic for manual Lyra community posts that gently remind
users they can add optional, anonymous profile details. Wording stays
jargon-free (no 'DiGA'/'data'/'study'). Manual-only, not in the auto-cron
catalog.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 00:11:01 +02:00
chahinebrini
b757486579 fix(mail): forceFullSweep on domain-add + 30s idle tick
Domain/display-name adds now force a full re-scan so newly-added gambling
senders are caught immediately instead of waiting for the incremental UID
window. IMAP-idle NOOP tick lowered 2min -> 30s to close the Junk-folder gap
faster (Outlook drops straight into Junk, which IDLE does not watch).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 00:11:01 +02:00
chahinebrini
d31e45e2a8 feat(streak): protection-coverage metric (DiGA core) replacing broken streak
The old streak was non-functional: streaks.current_days was always 0 (never
computed/incremented), and the profile page read me.streak (0) + account
created_at as the "since" date — showing "0 days protected since <signup>"
for everyone. This is the DiGA key metric, so it had to be rebuilt.

New model: optimistic protection-coverage based on actual VPN/MDM protection
state, never resets to 0.
- backend: append-only protection_state_log + migration; POST /api/protection/event
  (ingestion, deduped) + GET /api/protection/coverage (read-time compute, no cron);
  server-side cooldown_disable event on cooldown resolve. Generous >6h-off/day rule.
- frontend: report protection on/off transitions (initial + flips, deduped) from
  useProtectionState; rewrote profile StreakSection → half-donut (protected vs
  unprotected) + progress bar (current streak → personal record) + empty state.
- coverage starts fresh from deploy (no historical backfill — clean data for DiGA).
- spec: docs/specs/protection-coverage-streak.md (shared contract).
- old streaks/streak_events/profiles.streak left intact (coach/scores consumers).

Also adds go-to-market one-pagers under docs/marketing/.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 10:54:55 +02:00
chahinebrini
ac1d33afb8 fix(native): phantom/zombie incoming calls (iOS) + DM online dot
Calls: an incoming call that ended without the in-app /call screen ever
mounting (iOS shows the native CallKit banner, not our screen) left the
call store stuck in 'ended' forever — the ended→idle reset only lived in
the /call screen. A stuck 'ended' then blocked every subsequent incoming
call (RING + VoIP push were received but dropped by the status!=='idle'
guard), so accepting from the banner produced a phantom CallKit call that
ticked as active with no connection, and the caller saw a missed call.
- store self-heals back to 'idle' after a call ends (teardown fallback)
- receiveIncoming + ring handler tolerate a stale 'ended' state
- onAnswer ends the native CallKit call when store has no incoming call
- RNCallKeep.endAllCalls() on launch clears leftover CallKit zombies

DM online dot: the green avatar dot used follow-gated presence while the
"online" text used raw presence → dot hidden for non-followed partners
even when online. DM header avatar now uses raw presence (rawPresence
prop) → consistent with the text on both platforms.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 10:03:27 +02:00
chahinebrini
084f821bc5 fix(mail): incremental scan UID search returned seq-nums not UIDs
Der inkrementelle Scan-Pfad rief imap.search({ uid: 'X:*' }) ohne das
zweite { uid: true }-Argument auf → ImapFlow sendet "SEARCH UID X:*"
statt "UID SEARCH UID X:*" → Server antwortet mit Sequence-Numbers.
Die nachfolgende fetchAll(..., { uid: true }) interpretiert diese als
UIDs → fetcht die falschen (alten) Mails → neu eingegangene Gambling-
Mail (höchste echte UID) wird nie klassifiziert/gelöscht (>15min Lag).
Auch Ursache des "Command Error. 10" Log-Spams (Mega-UID-Liste).

Fix: { uid: true } als zweites search()-Argument.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 08:43:59 +02:00
chahinebrini
1f73bd8d8d fix(mail): BigInt-Serialisierung blockierte Phase-2-Persistierung
imapflow.status() liefert uidValidity als BigInt. Der Code reichte den BigInt in
JSON.stringify (patchFolderScanState) → 'TypeError: Do not know how to serialize
a BigInt' → vom stummen connection-level catch verschluckt → weder
patchFolderScanState noch markFullSweepDone liefen je → folder_scan_state blieb
{} + last_full_sweep_at NULL → inkrementeller Scan aktivierte nie (immer Full-Sweep).

Fix:
- serverUidValidity: Number((status).uidValidity ?? 0) — BigInt → number vor JSON.
- Stumme catches (auth/lock/conn) loggen jetzt; Persist-Calls (patchFolderScanState
  x2, markFullSweepDone) in eigene try/catch mit console.error — Diagnostik bleibt
  drin für Post-Deploy-Verify.

Lokal verifiziert: Build EXIT 0, imapflow extern.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 12:16:09 +02:00