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>
rebreak-imap-idle
Standalone IMAP IDLE Daemon für Rebreak.
Was er macht
- Hält pro aktiver
MailConnection(DB) eine persistente IMAP-IDLE-Session - Reagiert in Echtzeit auf
EXISTS-Events (neue Mail im Postfach) - Feuert bei jedem Event
POST /api/mail/scan-internalgegen das lokale Backend - Das Backend entscheidet ob und welche Mails gelöscht werden (Gambling-Keywords + Blocklist)
- Aktualisiert alle 5 min die Connection-Liste (neue User → neue Sessions, entfernte → geschlossen)
- IDLE wird alle 25 min erneuert (RFC 3501 Server-Timeout liegt bei 29 min)
Env-Vars
| Variable | Pflicht | Beschreibung |
|---|---|---|
DATABASE_URL |
ja | Postgres-Connection-String (Supabase Pooler oder direkt) |
ADMIN_SECRET |
ja | Shared Secret für /api/mail/scan-internal Header |
ENCRYPTION_KEY |
ja | AES-256 Key (identisch zum Backend-Key, 32+ Zeichen) |
BACKEND_URL |
nein | Default: http://127.0.0.1:3016 (staging) / 3015 (prod) |
NODE_ENV |
nein | production → BACKEND_URL default port 3015 |
Lokal starten (Entwicklung)
cd backend/imap-idle
npm install
DATABASE_URL=<...> ADMIN_SECRET=<...> ENCRYPTION_KEY=<...> node index.mjs
Via Infisical (analog zu start-staging.sh):
infisical run --env=staging -- node index.mjs
PM2 (Produktion)
Wird via ecosystem.config.js gestartet — siehe docs/internal/MAIL_DAEMON_DEPLOYMENT.md.
Logs (pm2)
[idle/<email>] connected (imap.gmail.com:993)
[idle/<email>] exists-event received (new mail)
[idle/<email>] scan-triggered → scanned=12 blocked=1
[idle/<email>] idle renewing (25min threshold)
[idle/<email>] reconnecting in 5s (attempt 2)
[idle/db] refreshed — 47 active connections, 47 sessions