概要 — 何をするものか
InfraGuard のダッシュボードは、ページを再読み込みしなくても自動的に更新されます。この自動更新は2つの仕組みの上で動いています。
| 仕組み | 方式 | レイテンシ | 利用箇所 |
|---|---|---|---|
| Reverb WebSocket | サーバー → ブラウザへの即時プッシュ(イベント駆動) | ミリ秒 | 進捗を tick としてストリーミングするライブジョブ |
| 軽量ポーリング | ブラウザが一定間隔で /…/live を GET |
最大30秒 | ほとんどのダッシュボード(ヘルス、サーバー、トラフィック、サービス、セキュリティ、警報 …) |
設計上の原則は次の一文に集約されます。
ライブ更新は、永続化されたデータの上に重ねる拡張機能です。 すべての画面はまず DB に保存されたデータを描画し、リアルタイム更新はその上に乗ります。したがって Reverb(WebSocket サーバー)が停止していても、ファイアウォールでブロックされていても、再起動中でも、画面は決して壊れません。 プッシュが利用できないときはポーリングが肩代わりします(グレースフルデグラデーション)。
実運用では、ほぼすべての画面がポーリングで動いています。 Reverb WebSocket は導入・配線済みで、現時点で担っている機能はただ1つ — セキュリティ診断のリモートエミュレート(リモートエミュレート · セキュリティ診断)で、ホストごとの進捗 tick をストリーミングします。それ以外のライブ画面はすべて、30秒ポーリングと軽量な rev 変更検知トークンで更新されます。
こんなときに使う
これは概念のリファレンスです。ライブルートを持つ画面ではなく、次のことを理解したい運用者・エンジニアのための項目です。
- ダッシュボードがどのように自力で最新状態を保っているのか
- WebSocket が接続できない環境(内部ネットワーク、プロキシ、混在コンテンツ)では何が起きるのか
- ポーリングがどのようにバッテリーとサーバー負荷を節約するのか(タブが背面に回ると停止します)
画面構成
2経路アーキテクチャ
[永続化データ] server_status · infr_db_metric · blacklist_info · …
│
▼ (初回ページ読み込み: サーバーが data-* JSON をビューに埋め込む)
[ブラウザ画面] ← 即座に描画(ライブなしでも完結)
▲ ▲
│ (A) ポーリング │ (B) Reverb プッシュ
│ GET /…/live (≤30s) │ WS イベント 'tick'
│ rev↑ のときだけ再描画 │ ShouldBroadcastNow
[Laravel コントローラー] [Reverb サーバー]
- (A) ポーリング — フロントエンドが
setIntervalにより30秒ごとに/{screen}/liveを呼び出します。レスポンスには軽量な変更トークンrevが含まれ、チャートやカードはその値が変わったときだけ再描画されます。タブが背面に回っている場合(document.hidden)は、呼び出し自体をスキップします。 - (B) Reverb プッシュ — サーバーがイベントを即座にブロードキャストし(
ShouldBroadcastNow— キューを経由しません)、購読しているブラウザが WebSocket 経由で受け取ります。現時点ではリモートエミュレートの進捗 tick にのみ使われています。
Reverb のトポロジー
| 項目 | 値 | 出典 |
|---|---|---|
| クライアントライブラリ | laravel-echo + pusher-js |
動的インポート(メインバンドルからは除外) |
WebSocket の接続設定は導入時に構成されます。運用者は画面を開くだけで、設定する項目はありません。
使い方
運用者 — 何もする必要はありません
ライブ更新は自動です。画面を開いたままにしておけば、ポーリングが30秒ごとに最新値を取得します。WebSocket が有効なら、イベント駆動の画面は即座に更新されます。電力を節約するため、タブが背面にある間はポーリングが一時停止し、戻ると再開します。
Reverb WebSocket を有効にする(任意)
WebSocket プッシュは導入担当者が導入時に設定するものであり、コンソール上の運用者向けトグルではありません。リアルタイムプッシュが必要な場合は、管理者またはベンダーにご連絡ください。
WebSocket プッシュが無効であることはエラーではありません。これは意図的なオフスイッチであり、ライブ画面はポーリングだけで問題なく動作し続けます。
ポーリングが実際に行っていること
// 代表的なパターン(例: service-overview)
poll_ms: 30000,
this._poll = setInterval(() => {
if (this.live && !document.hidden) this.poll_tick(); // タブが背面にあるときはスキップ
}, this.poll_ms);
// poll_tick → GET /service/live → レスポンスの rev が変わったときだけ再描画
サーバー側の rev は可能な限り安価な変更シグナルです。たとえばサーバーメトリクス画面では、レガシーの server_status テーブルの MAX(seq) を rev として使います。この値はコレクターが新しいスナップショットを挿入したときにのみ増加します。
// App\Services\Metric\ServerMetricService::rev()
return (int) DB::connection('legacy')->table('server_status')->max('seq');
Reverb プッシュを使う唯一のライブジョブ — リモートエミュレート
セキュリティ診断のリモートエミュレートは、ホストが1台終わるたびに進捗 tick をブロードキャストします。
- イベント:
App\Events\InfrRemoteEmulateTick(ShouldBroadcastNow) - チャネル: パブリックチャネル
infr.emulate.{runId}—runIdは推測不能なランダムトークン(ケーパビリティ)です。パブリックであるため、セッションに基づく/broadcasting/authの配線は不要です。 - イベント名:
.tick· ペイロードは軽量です(ホストのサマリーと進捗カウントのみ) - フォールバック: フロントエンドは購読すると同時に、2秒間隔のフォールバックポーリング(
/asset/remote/emulate-status/{runId})も実行します。これにより WS が存在しない場合や、最初の tick を取りこぼした場合でも進捗が失われません。
データソース
| ファイル | 役割 |
|---|---|
resources/js/echo.js |
アプリ唯一の Echo ビルダー(SOT) — 動的インポート · 設定ゲート → null · ランタイムでの forceTLS |
app/Support/Realtime.php |
ブロードキャストの安全網(up() の fsockopen プローブ · fire() の例外吸収) |
routes/channels.php |
チャネル認証コールバック(現在はフレームワーク既定の App.Models.User.{id} のみ) |
app/Events/InfrRemoteEmulateTick.php |
実際に使われている唯一のブロードキャストイベント |
各画面の …/live コントローラー + …Service::rev() |
ポーリング用のライブペイロード + 変更トークン |
Realtime — 二層の防御(WS が死んでも元の処理は道連れにならない)
// (1) up() — 150ms の fsockopen プローブ(リクエストごとにメモ化)。イベントの broadcastWhen() がこれで判定するため、
// Reverb が停止しているときに ShouldBroadcastNow イベントが接続タイムアウトでインライン例外を投げることはない。
public static function up(): bool // 設定から探索する(ハードコーディングなし)
// (2) fire() — Reverb が起動していても拒否する場合(413、あるいは再起動中の 5xx)に呼び出し元を保護する。
// 失敗は report() で Pulse Exceptions に送られ、元の処理(保存済みのスキャン結果)はそのまま成功する。
public static function fire(object $event): void
イベントの broadcastWhen() は Realtime::up() を返します — Reverb が停止していれば、送信そのものをスキップします。
アクセス権限
チャネルの認可
| チャネル種別 | 認証 |
|---|---|
パブリックチャネル(infr.emulate.{runId}) |
なし — 推測不能な runId ケーパビリティトークンそのものがアクセス権になります |
| private / presence チャネル(設計ロードマップ) | routes/channels.php のコールバックがセッションの user_level とロールスコープを検査します |
CLAUDE.md にあるリアルタイムチャネル表(
private-infr.server.{id}、private-infr.threats、presence-infr.ops…)は設計ロードマップです。現時点で実装されているブロードキャストは単一のパブリックチャネルinfr.emulate.{runId}だけで、routes/channels.phpにはフレームワーク既定のプライベートチャネルApp.Models.User.{id}のみが登録されています。private / presence チャネルを追加する際は、必ずuser_levelとスコープで認可してください(例:infr.threatsはuser_level>=2 && scope∋securityを要求)。
ポーリングエンドポイントのアクセス権限
/{screen}/live ポーリングエンドポイントは、その画面のコントローラーの読み取りレベルゲートを継承します(demo=1、staff=5、admin=10。一部の領域は infr.level:N ミドルウェアで制御されます)。たとえばリモートエミュレートのステータスフォールバックポーリング(emulate-status)は書き込み権限(レベル2以上)を必要とします。ポリシー全体は RBAC レベル を参照してください。
トラブルシューティングと FAQ
Q. WebSocket が接続できないのに、画面は更新され続けます
想定どおりの動作です。ライブ画面は WS がなくてもポーリングだけで完全に機能します。WebSocket は、リモートエミュレートのようなイベント駆動ジョブのレイテンシを縮めるだけのものです。
Q. Reverb を有効にしましたが、ブラウザが接続しません
- 混在コンテンツ —
echo.jsはforceTLSを実行時のページプロトコルから判定します(ページがhttps:ならwss)。ビルド時の環境変数から強制しないのは意図的で、https で配信されたバンドルがws://に接続しようとしてブラウザにブロックされる事態を防ぐためです。フロントエンドが https なら、Reverb も TLS(wss)で配信するか、プロキシで終端する必要があります。 - CSP / CORS —
echo.jsはenabledTransports: ['ws','wss']とdisableStats: trueを設定し、pusher-js の sockjs CDN フォールバックと統計送信(セルフホスト環境の CSP に違反します)をブロックします。
Q. ダッシュボードはサーバーや DB に負荷をかけませんか
- ポーリング間隔は30秒で、タブが背面に回ると停止します(
document.hidden)。 rev変更トークン(例:MAX(seq))により、新しいデータがないときは重い再描画をスキップします。- ライブビルダーのサービスはおおむね20〜30秒キャッシュされます。
Q. リアルタイム系のデーモンをスケジューラーに登録できますか
絶対にしないでください。
reverb:start、queue:work、pulse:checkのような無限ループ型のデーモンをスケジューラー(Schedule::command(...))から起動してはいけません。終了しないプロセスが tick ごとに積み上がり、共有 MySQL のコネクションプールを枯渇させます(2026-08-06 にpulse:checkで実測: 16時間で169個が蓄積 →[1040] Too many connectionsが発生し、本番 DB にも影響しました)。デーモンは、開発時はランチャーの常駐ペイン1つから、本番では supervisor / systemd からのみ実行してください。デーモンの配置については 開発 · オブザーバビリティ を参照してください。
Q. コード変更後に Reverb を再起動するには
本番では無停止の php artisan reverb:restart を使う(または supervisor 配下で再起動する)方法があります。開発時はランチャーのペインを再起動します。再起動中に WS が一時的に接続を拒否しても、Realtime::fire() が例外を吸収し、その間はポーリングが穴を埋めます。
関連項目
- リモートエミュレート — Reverb プッシュを実際に使う唯一のライブジョブ
- セキュリティ診断 — リモートエミュレートの進捗 tick が流れる画面
- 開発 · オブザーバビリティ(Pulse) — デーモン実行の規律 · Realtime の例外が届く先
- インフラ ▸ サーバー — 30秒ポーリング +
MAX(seq)rev トークンの代表例 - サービス概要 — ライブボードのポーリングパターン
- RBAC レベル — チャネルとポーリングエンドポイントのアクセスゲート
