Plugin Discourse Bounce Guard

Repo: GitHub - overgrow/discourse-bounce-guard · GitHub
Licenza: MIT

&tldr; Cosa ottieni: un log degli errori più pulito (cessano i tentativi di invio orari a indirizzi inesistenti). Discourse smette di inviare email che non possono essere consegnate. E utenti più felici: possono rientrare nel proprio account, perché un indirizzo non valido viene sostituito in tempo con uno funzionante.

Se la posta in uscita passa attraverso il tuo relay (Postfix, Exim, la maggior parte delle configurazioni self-hosted), probabilmente avrai visto /logs riempirsi di coppie come questa:

SMTP Error Net::SMTPServerBusy with message: 450 4.1.2 <someone@gone-domain.com>: Recipient address rejected: Domain not found
Job exception: Net::SMTPServerBusy

Il dominio è sparito e non tornerà. Ma il relay risponde con un codice di errore temporaneo (450), perché in teoria una ricerca DNS può fallire per un momento. Discourse interpreta qualsiasi errore temporaneo come “riprova tra un’ora”, e Sidekiq continua a tentare per settimane. Il rilevamento dei bounce del core non si attiva mai. Reagisce solo quando torna un messaggio di bounce, sia come email (VERP) che come webhook dal tuo provider di posta, e qui il messaggio viene rifiutato sul posto, quindi nessun messaggio di bounce esiste mai. Il punteggio di bounce rimane a zero, i tentativi continuano e l’utente mantiene un indirizzo che non può ricevere nulla, incluso il reset della password.

Questo è stato sollevato qui diverse volte senza una risposta integrata, ad esempio Gestione delle email a domini inesistenti, Disattiva utente con hard bounce e Come disattivare gli account di utenti che non ricevono email. Abbiamo avuto lo stesso problema sul nostro forum, quindi abbiamo creato un plugin.

Cosa fa

Bounce Guard aggancia il percorso di invio stesso e classifica ogni rifiuto SMTP:

  • Le risposte 5xx con uno stato esteso di destinatario non valido (5.1.1, 5.1.2, 5.2.1 e simili)
    contano come fallimenti definitivi (hard failure).
  • Qualsiasi risposta che corrisponda a un elenco di frasi configurabile (“domain not found”, “user unknown”, …)
    conta come definitivo (hard) indipendentemente dal codice. Questo cattura i relay che falliscono in modo temporaneo (soft-fail) condizioni permanenti con un 450.
  • Greylisting, caselle piene e limiti di velocità vengono lasciati intatti. Il comportamento di retry del core non viene toccato.

I fallimenti definitivi registrati salgono poi una scala:

  1. Ciascuno di essi alimenta il punteggio di bounce del core come hard bounce (attivo per impostazione predefinita). Dopo due, la soglia del core stesso scatta e Discourse smette di inviare email all’utente. Il rumore nei log finisce qui,
    anche se non attivi nulla di ulteriore.
  2. Dopo un numero configurabile (predefinito 3) distribuito su un intervallo configurabile (predefinito 48
    ore, in modo che un’interruzione breve non possa escludere nessuno), il plugin agisce. L’azione predefinita
    è log_only: una voce nel log delle azioni dello staff che registra che l’utente sarebbe stato
    disattivato. Passa a deactivate quando ti fidi.
  3. La disattivazione instrada l’utente attraverso il flusso di attivazione standard al loro prossimo login,
    dove la modifica dell’indirizzo è integrata. Verificano un’email funzionante e continuano con
    il loro account intatto. Il punto è la recuperabilità: un account attivo il cui unico
    canale di recupero è una casella di posta morta è un blocco in attesa di accadere.

Se il tuo sito riceve bounce (VERP o webhook), puoi anche far agire il plugin quando
il punteggio di bounce del core supera un livello che scegli, superiore al punteggio al quale il core smette
di inviare. Vanno applicate ovunque alcune regole di sicurezza. Lo staff e i bot non vengono mai toccati. Una
email bloccata che riprova ogni ora conta come un singolo fallimento, grazie a una finestra di raffreddamento. Un
fallimento conta contro un utente solo quando l’indirizzo rifiutato dal server è l’indirizzo
corrente di quell’utente. E ogni fallimento registrato viene conservato per 90 giorni in modo da poter verificare cosa
è successo (query Data Explorer nella README).

Installazione

Installazione plugin standard:

hooks:
  after_code:
    - exec:
        cd: $home/plugins
        cmd:
          - git clone https://github.com/discourse/docker_manager.git
          - git clone https://github.com/overgrow/discourse-bounce-guard.git

Abilita bounce_guard_enabled, lascia bounce_guard_action su log_only per una o
due settimane, rivedi cosa segnala, poi decidi sulla disattivazione. Il riferimento alle impostazioni è nella
README.

Solo lato server, nessun componente di tema o JS. Costruito e testato contro il core attuale
(2026.8), 31 specifiche, CI esegue il workflow standard discourse-plugin. Feedback benvenuti,
soprattutto le formulazioni di rifiuto da altri relay che l’elenco di frasi predefinito non cattura.