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.
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.
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.
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.
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>
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>
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>
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>
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>
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>
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>
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>
- 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>
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>
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>
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>