551 Commits

Author SHA1 Message Date
chahinebrini
c8f5bfc82e fix(backend): make user_devices migration idempotent for fresh DBs
Add CREATE TABLE IF NOT EXISTS for rebreak.user_devices to the
hardware_id migration so fresh databases can migrate despite the
alphabetical ordering mismatch with 20260430_add_user_devices.
Also apply Prettier formatting to mdm.ts.
2026-06-18 04:04:05 +02:00
chahinebrini
7bf26a3138 test(backend): add unit tests for MDM health-check DB helpers 2026-06-18 03:53:26 +02:00
chahinebrini
15b4441deb feat(backend): add MDM health check cron 2026-06-18 03:42:18 +02:00
chahinebrini
74784fc4da feat(backend): add bulk MDM health status helpers
Add MdmEnrollmentStatus, UserDeviceMdmHealthRecord types and helpers:
- getLinkedUserDevices() to load iOS devices with NanoMDM UDIDs
- getMdmEnrollmentStatusesByUdids() for bulk NanoMDM lookups
- updateUserDeviceMdmHealth() to mirror status on UserDevice

Also fix PoolConfig option casing: queryTimeout -> query_timeout.
2026-06-18 03:36:13 +02:00
chahinebrini
b107262d60 feat(mdm): add NanoMDM health columns migration for UserDevice
Adds mdm_id, mdm_enrolled, mdm_supervised, mdm_last_seen_at and an
index on mdm_id. Uses IF NOT EXISTS to stay idempotent because mdm_id
was added manually before the migration was created.
2026-06-18 03:29:30 +02:00
chahinebrini
9065543b5a feat(backend): extend UserDevice with MDM health columns
Add mirrored NanoMDM enrollment, supervision and last-seen fields
(mdm_enrolled, mdm_supervised, mdm_last_seen_at) to UserDevice model.
Migration will follow in a separate task.
2026-06-18 03:15:09 +02:00
chahinebrini
943efe4b45 fix(backend): remove hardwareId references, use deviceId only 2026-06-18 00:06:39 +02:00
chahinebrini
a60def33d9 Merge feat/magic-ios-section into main 2026-06-17 23:51:29 +02:00
chahinebrini
6245fc4573 fix(magic): real MDM supervised state, mdmId matching, MDM status for unknown USB devices 2026-06-17 23:47:33 +02:00
chahinebrini
75d1b06105 feat(magic): iOS device card warning badge, USB hint, split backend/local cards and auto-sync 2026-06-17 23:32:41 +02:00
chahinebrini
b87ec08431 feat(mdm): remove mdm_lock type, derive lockProfileInstalled from nefilter state 2026-06-17 22:32:40 +02:00
chahinebrini
5b0a4d03d2 feat(magic): identify current device via hardwareId, migrate existing devices 2026-06-17 17:18:40 +02:00
chahinebrini
e4b28be5be feat(magic): dedicated iOS section in dashboard with on-demand sync 2026-06-17 07:44:24 +02:00
chahinebrini
48af756a86 tmp(magic): debug logging for current device matching 2026-06-17 03:19:10 +02:00
chahinebrini
10ca1c4bc5 tmp(backend): debug bypass code 000000 -> charioanouar session 2026-06-17 03:13:36 +02:00
chahinebrini
b829f9ba3e fix(magic): match current device by session deviceId, remove confusing overall status card 2026-06-17 03:06:38 +02:00
chahinebrini
4c46ac69c9 fix(magic): remove device-release from Magic sheet, Magic does not remove devices 2026-06-17 02:59:05 +02:00
chahinebrini
81c516b831 fix(magic): robust hostname matching for current device detection 2026-06-17 02:52:00 +02:00
chahinebrini
6b44fb2927 chore(prisma): add migration_lock.toml for deploy 2026-06-17 02:38:44 +02:00
chahinebrini
026c319b30 fix(magic): robust device parsing, dashboard only protects current device 2026-06-17 02:29:35 +02:00
chahinebrini
37f3173539 chore: add scripts/dev.sh for local app dev against server 2026-06-16 22:05:53 +02:00
chahinebrini
2f1d5ec83c fix(magic): use solid heroicon for star rating 2026-06-16 20:55:25 +02:00
chahinebrini
298a0089bb feat(magic): redesign status dashboard with hero cards and device sheet 2026-06-16 20:53:39 +02:00
chahinebrini
b5e89b5973 feat(magic): add DeviceDetailSheet component 2026-06-16 20:49:39 +02:00
chahinebrini
0258c818f3 feat(magic): add DeviceHeroCard and DeviceListItem components 2026-06-16 20:45:43 +02:00
chahinebrini
118269a8c9 feat(magic): add IosStarRating and CooldownCountdown components 2026-06-16 20:42:59 +02:00
chahinebrini
8953e1f7d6 feat(magic): add useDeviceStatus composable 2026-06-16 20:41:53 +02:00
chahinebrini
8f5e34ae67 feat(magic): expose cooldown commands and extend device types 2026-06-16 20:35:20 +02:00
chahinebrini
97977011ae feat(backend): include status, cooldownUntil, lastSeenAt and sleep flag 2026-06-16 20:28:21 +02:00
chahinebrini
4ee6849f3f feat(backend): add cooldown and cancel-cooldown endpoints 2026-06-16 20:24:57 +02:00
chahinebrini
b9bd577e47 feat(backend): add magicCooldownUntil to UserDevice 2026-06-16 20:13:23 +02:00
chahinebrini
1a270739bc feat(coach): Lyra-LLM auf direkten Anthropic Haiku 4.5 + SOS-Fallback-Kette; ElevenLabs reaktiviert
- Chat (message.post.ts): neuer nativer anthropic-Branch in tryModel
  (api.anthropic.com/v1/messages, x-api-key, system top-level), führt die
  Fallback-Kette claude-haiku-4-5 → gemini-flash-lite → gemini-flash → gpt-4o-mini.
- SOS (sos-stream.get.ts): Dispatch-Refactor mit buildUpstream() + Kandidaten-
  Fallback-Kette (anthropic → gemini → openai). Behebt strukturell den Bug
  "SOS liefert nur Krisen-Text" (vorher single fetch ohne Fallback). Nativer
  Anthropic-Stream: Delta-Parser liest content_block_delta.delta.text.
- nitro.config.ts: anthropicApiKey deklariert (ANTHROPIC_API_KEY).
- plan-features.ts: Legend-Voice zurück auf ElevenLabs eleven_turbo_v2_5
  (Cartesia-Übergang nach Zahlungsproblem 2026-06-08 aufgehoben, neuer Key).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 22:41:59 +02:00
chahinebrini
21c1e31877 docs(diga): Nacht-Session — Eval-Records, Akte 10/11, Magic-Scope-Entscheidung
- Lyra-Eval Live-Runs (2x): Crisis-Recall-Gate auf Produktionsmodell
  (Groq llama-3.3-70b) BESTANDEN (6/6=100%); gpt-4o-mini-Fallback 83%
  -> Modellwahl sicherheitsrelevant -> Model-Pinning vorgeschlagen.
  Records unter docs/specs/diga/eval-records/.
- 05d: Mail- + Anonymitäts-Strang (+18 Zeilen); username-GAP verifiziert
  + Fix dokumentiert. 04 (R-LYRA-01, R-DATA-07) + 05b nachgezogen.
- Dok 07 Gebrauchsanweisung, Dok 09 PMS-Plan, Dok 10 QMS-Templates (v0).
- Scope-Entscheidung Gründer 2026-06-11: RebreakMagic (inkl. Desktop)
  vorerst NICHT im zertifizierten DiGA-Scope (01/03/07 umgesetzt).
- graphify-Artefakte (Hook-Rebuild) mitgenommen.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 06:36:33 +02:00
chahinebrini
444688b6e9 test(eval): Concurrency-Limit + 429-Retry für Lyra-Eval-Suite
LYRA_EVAL_CONCURRENCY (Batch statt Promise.all-30-parallel) und
LYRA_EVAL_TIMEOUT_MS als Env-Schalter, plus 429-Retry mit Backoff —
nötig für Live-Runs gegen Provider mit niedrigem TPM-Limit (Groq
on-demand 12k). Default-Verhalten unverändert; Prompts/Regeln/
System-Prompt nicht angefasst.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 06:36:32 +02:00
chahinebrini
2c33ba55a4 fix(backend): username (Login-Identifikator) aus öffentlichen Payloads entfernt
community/posts.get.ts + social/profile/[userId].get.ts lieferten neben
nickname auch username an fremde Clients — username ist der Login-
Identifikator ({username}@rebreak.internal) und verletzt die Nickname-
Anonymitäts-Invariante (REQ-COMM-005 / R-DATA-07) + exponiert das halbe
Login-Credential-Paar. Frontend rendert das Feld nirgends (verifiziert);
totes Typ-Feld in stores/community.ts entfernt.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 06:36:16 +02:00
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
df3c4fafa3 fix(mail): force full-sweep on idle-daemon recovery (cold-start + downtime>5min)
Eine global-blocklistete Casino-Mail (mpmgame.com) rutschte in die Inbox,
obwohl die Absender-Domain Layer-2-Hard-Block ist. Ursache: kein Erkennungs-
Fehler, sondern ein Scan-Gap. Der idle-daemon crash-loopte heute (Port-3016-
Race, ~49 Restarts) und machte nach jedem Reconnect/Cold-Start nur einen
INKREMENTELLEN Scan (lastUid). Mails, die waehrend einer Downtime ankamen,
fielen damit dauerhaft durch das last-seen-UID-Raster.

Fix in backend/imap-idle/index.mjs:
- Cold-Start (Prozess-/pm2-Restart): immer forceFullSweep
- Reconnect nach Error mit Downtime > 5min (RECOVERY_SWEEP_THRESHOLD_MS):
  forceFullSweep beim naechsten erfolgreichen Connect
- Routinemaessiger IDLE-Renew (10min Close-Reopen): bleibt inkrementell
  -> kein teurer Dauer-Sweep im Normalbetrieb
- triggerScan(conn, {forceFullSweep}) + Coalesce-State "full"/"incremental"
  (ein full-Trigger wird nicht von einem spaeteren inkrementellen degradiert)

scan-internal.post.ts wertet body.forceFullSweep bereits korrekt aus.
Im Zweifel Full: eine global-blocklistete Domain darf nie durchrutschen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 21:58:00 +02:00
chahinebrini
8697fee2e8 fix(deploy): warte auf Backend-Port 3016 vor idle-Daemon-Restart
Deploy-Race: rebreak-idle-staging wurde direkt nach pm2-restart von
rebreak-staging gestartet, bevor der Nitro-Server auf Port 3016
lauschte. Der Daemon startete sofort Initial-Scans fuer alle Accounts
-- jeder triggerScan()-Call scheiterte mit "fetch failed" (ECONNREFUSED).
Kein Crash, aber Error-Log-Burst (N Fehler pro Mail-Account) und
verpasster Initial-Sweep.

Fix: curl-Preflight in Step 5b wartet bis Port 3016 antwortet (max
60s, 12x alle 5s, --retry-connrefused). Bei Timeout: WARN im Log,
kein Deploy-Abbruch (best-effort fuer optionalen Service).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 15:47:24 +02:00
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