Discourse Bounce Guard プラグイン

リポジトリ: GitHub - overgrow/discourse-bounce-guard · GitHub
ライセンス: MIT

&tldr; 提供される機能: エラーログの整理(存在しないアドレスへの毎時の再試行が停止する)。Discourse が届かないメールの送信を停止する。そしてユーザーの満足度向上:無効なアドレスが適時に有効なアドレスに置き換えられるため、アカウントへの再ログインが可能になる。

送信メールが独自のリレー(Postfix、Exim、ほとんどのセルフホスト環境など)を介して処理されている場合、/logs に以下のようなペアが埋め尽くされているのを確認したことがあるかもしれません:

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

そのドメインは既に存在せず、復活する見込みもありません。しかし、理論上DNSルックアップが一時的に失敗する可能性があるため、リレーは一時的なエラーコード(450)で応答します。Discourseは一時的なエラーをすべて「1時間後に再試行する」と解釈し、Sidekiqは何週間も再試行を続けます。コアのバウンス検出は発火しません。それはバウンスメッセージ(メール(VERP)またはメールプロバイダーからのWebhook)が返ってきた場合のみ反応しますが、ここではメッセージがその場で拒否されるため、バウンスメッセージは存在しません。バウンススコアはゼロのまま、再試行は続き、ユーザーはパスワードリセットを含むあらゆるメールを受信できないアドレスを保持し続けます。

これについて、組み込みの解決策なしで何度か話題に上がっています。例えば 存在しないドメインへのメールの処理ハードバウンスしたユーザーの無効化メールを受信していないユーザーのアカウントを無効にする方法 などです。当方のフォーラムでも同様の課題に直面していたため、プラグインを作成しました。

機能

Bounce Guard は送信パス自体にフックし、すべてのSMTP拒否を分類します:

  • 不正な受信者拡張ステータス(5.1.15.1.25.2.1 など)を伴う 5xx 応答は、ハード失敗としてカウントされます。
  • 設定可能なフレーズリスト(“domain not found”、“user unknown” など)に一致する応答は、コードに関係なくハード失敗としてカウントされます。これにより、450 で永続的な条件をソフト失敗として返すリレーも捕捉できます。
  • グレーリスティング、満杯のメールボックス、レート制限には干渉しません。コアの再試行動作は変更されません。

記録されたハード失敗は、以下の段階を登っていきます:

  1. それぞれがハードバウンスとしてコアのバウンススコアに反映されます(デフォルトで有効)。2回目で、コア独自のしきい値がトリガーされ、Discourse はそのユーザーへのメール送信を停止します。ここでログのノイズは止まります。さらに何も有効にしなくてもです。
  2. 設定可能な回数(デフォルト3回)が、設定可能な期間(デフォルト48時間、短時間の障害で誰かが無効化されないようにするため)にわたって発生すると、プラグインが動作します。デフォルトのアクションは log_only で、ユーザーが無効化されたはずであることを記録するスタッフアクションログエントリを作成します。信頼できると判断したら deactivate に切り替えてください。
  3. 無効化により、ユーザーは次のログイン時に標準的なアクティベーションフローを経由することになり、ここでアドレスの変更が組み込まれています。有効なメールを検証し、アカウントを維持したまま続行できます。ポイントは回復可能性です:唯一の復旧手段が死んだメールボックスであるアクティブなアカウントは、ロックアウト待ちの状態です。

サイトがバウンス(VERPまたはWebhook)を受信している場合、コアのバウンススコアが選択したレベル(コアが送信を停止するスコアより高い値)を超えた際にプラグインに動作させることもできます。どこでもいくつかの安全ルールが適用されます。スタッフとボットには決して干渉しません。毎時再試行される1つのスタックメールは、クールダウンウィンドウのおかげで単一の失敗としてカウントされます。失敗は、サーバーが拒否したアドレスがそのユーザーの現在のアドレスである場合のみ、そのユーザーに対してカウントされます。また、記録されたすべての失敗は90日間保持されるため、何が起きたかを確認できます(README内のData Explorerクエリ参照)。

インストール

標準的なプラグインのインストール

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

bounce_guard_enabled を有効にし、bounce_guard_action は1〜2週間 log_only のままにして、フラグが立てられた内容を確認してから、無効化について判断してください。設定の詳細はREADMEを参照してください。

サーバーサイドのみで、テーマやJSコンポーネントは不要です。現在のコア(2026.8)に対して構築・テスト済み、31個のスペック、CIは標準のdiscourse-pluginワークフローを実行します。フィードバックをお待ちしています。特に、デフォルトのフレーズリストが捕捉できない他のリレーからの拒否フレーズについて。