
2026/06/03 2:40
Show HN: RePlaya – セルフホスト型ブラウザセッションリプレイでライブテール機能付き
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
RePlaya は、S2 プロトコルに基づいて構築されたセルフホスト型のセッションレプレイプラットフォームであり、各セッションは単一の S2 ストリームとして保存され、別途データベースやオブジェクトストレージを必要としない。S2 ストリームはリアルタイムで尾部を追跡(tail)でき、マウントされたプレーヤーを通じてアクティブなセッションのライブレプレイおよび完了したセッションの再生を可能にする。
インストールには、S2 アクセストークンと Basin が必要であり、
.env.local で S2_ACCESS_TOKEN、S2_BASIN といった変数およびセルフホストされた s2-lite 用のエンドポイントを指定するオプションが設定できる。開発環境では、ローカル API は http://localhost:8787 で動作し、Vite がダッシュボードを http://localhost:5173 で提供する。
レコーディングを開始するためのドロップイン型のレコーダースニペットが提供されており、オプションのメタデータフィールド「source」と
distinctId/userId タギングをサポートする。デフォルトでは、すべての入力要素(input/select/textarea)の値は rrweb maskAllInputs を使用してマスクされ、生の状態をキャプチャするには maskAllInputs: false を渡す必要がある。機密性の高い UI 領域を除外するには、CSS クラス名 replaya-block または replaya-ignore を追加する。
本番環境では、公開の書き込み専用コレクタールート(例:
/recorder.js、/events)とアクセスレイヤー(VPN および oauth2-proxy など)の背後にあるプライベートダッシュボード/読み取り API に分離してデプロイされる。セキュリティは NODE_ENV=production の際に強制され、安定した REPLAYA_APPEND_TOKEN_SECRET が必要であり、デフォルトではオリジンレスのインゲストが無効化される。有用な設定には REPLAYA_SESSION_CREATE_RATE_LIMIT(60/分)、REPLAYA_MAX_EVENTS_PER_BATCH(100)、および REPLAYA_JSON_BODY_LIMIT(8MB)が含まれる。
Docker デプロイメントでは、コンパイル済みの単一のサーバーイメージ(
node dist-server/server/index.js)を使用し、非ルートユーザーとして実行し、ヘルスチェック用の組み込みのエンドポイント /api/health を提供する。
テスト用には、S2 の必要のない
pnpm test によるユニットテストおよび煙突测试を実行するか、または S2_TEST_ENDPOINT を使用して実際の s2-lite インスタンスに対する統合テストを実行する。本文
RePlaya セッションリプレイシステム
S2(StreamStore)を基盤とした、セルフホスト対応のセッションリプレイ機能です。
概要と特徴
- アーキテクチャ: 各セッションは単一の S2 ストリーム として保存され、これがシステム全体のバックエンドとなります。
- データベース、メッセージバス、オブジェクトストア、検索インデックスの別途設置は不要です。
- リアルタイム性: S2 ストリームは書き込みながらテール(追跡)可能です。
- 来訪者がページ内にいる間にもライブでセッションを再生できます。
- 完了したセッションも巻き戻して表示可能です。
- 導入の簡単さ: サイトにレコーダーのスクリプトを追加するだけで、セッションはストリーム形式で保存され、後からライブテールやフィルタリング、エクスポートが可能です。
デモ
- リストに新しいセッションが出現し、その S2 ストリームからライブテールされています。
- 来訪者がアプリを使用するにつれて、再生画面とアクティビティフィードもリアルタイムで更新されます。
クイックスタート
初期設定 (.env.local
)
.env.localS2 アクセストークンとバジンを準備し、以下を記述してください:
S2_ACCESS_TOKEN=replace-with-an-s2-access-token S2_BASIN=replaya-your-name PORT=8787
- バシン(データ蓄積領域): 初回使用時に RePlaya のデフォルトストリーム設定で自動作成されます。
- ヘルスチェック: ダッシュボードのインジケーターに、利用可能なバシンと実効的な S2 エンドポイントが表示されるため接続確認が可能です。
- 他社製品との互換性:
や他の互換環境を使用する場合、以下のようにエンドポイントを明示的に設定してください:s2-lite
S2_ACCOUNT_ENDPOINT=http://localhost:7070 S2_BASIN_ENDPOINT=http://localhost:7070
サーバー起動とポート情報
依存関係をインストールし、開発サーバーを起動します。以下のポートで動作します:
- API:
http://localhost:8787 - Vite ダッシュボード:
http://localhost:5173
テスト用録画(計器化なし)
他のアプリケーションを計器化せず、テスト用のレコーディングを行うには、以下の URL をブラウザで開いてください:
http://localhost:8787/recorder-test
本番環境風のローカル実行
Express で单一ポートからすべてのコンポーネントをビルド・提供する場合:
pnpm build pnpm start
その後、
http://localhost:8787 を開いてください。
ドロップインレコーダー
任意のページに以下のスクリプトを追加することで、レコーディングを開始できます(例:RePlaya のホスト URL 接続):
<script> !function(w,d,s,u){w.replaya=w.replaya||function(){(w.replaya.q=w.replaya.q||[]).push(arguments)};var e=d.createElement(s);e.async=1;e.src=u;d.head.appendChild(e)}(window,document,"script","https://replaya.example.com/recorder.js"); replaya("init", { apiHost: "https://replaya.example.com", source: "web-app" }); </script>
ローカル開発環境:
- ホスト:
http://localhost:8787 - 専用パス:
(上記スクリプトを使用して録画)/recorder-test
カスタマイズオプション
: アプリ、サイト、環境、テナントごとにキャプチャをグループ化するためのメタデータ。source
/distinctId
: セッションにアプリケーション識別子をタグ付けするために渡す ID。userId- セキュリティ設定 (
):maskAllInputs- デフォルトでは、入力値(パスワード、メールアドレスなど)は一切サーバーに送信されません(rrweb の機能)。
- 生状態キャプチャが必要な場合は、以下のいずれかを実装してください:
replaya("init", ..., { maskAllInputs: false }) // または HTML アтриビュートで設定 <script data-mask-all-inputs="false">...</script>
- 除外設定:
- マスキングは入力値のみをカバーします。DOM にレンダリングされたテキストは記録されます。
- 機密情報を除外するには
クラス、子ツリーの変更をスキップするにはreplaya-block
クラスを使用してください。replaya-ignore
- 本番環境推奨: ダッシュボードと読み込み専用 API はプライベートにし、収集(collect)専用のルーティングのみをパブリックに公開してください。
S2 上で動作する仕組み
セッション録画はログの一種であり、追加専用かつ順序付け・タイムスタンプ付与されたイベントの列です。RePlaya は各セッションを単一の S2 ストリームとして保存し、同様に読み出すため、通常複数のシステムに分散させる必要な機能を一つのプロセスで完結させます。
主要機能
- ストレージ:
- rrweb イベントは、S2 プライダー API を介してセッションストリームの末尾に追加されます。
- バッチ処理、バックプレッシャー制御、永続化確認が行われます。
- オブジェクトストアの他に独立したバッファリングを行いません。大きなイベントは S2 レコードに分割され、読み込み時に再構成されます。
- タイムライン:
を採用しており、rrweb がキャプチャした時刻が各イベントレコードの S2 タイムスタンプとして書き込まれます。timestamping.mode: client-require- 巻き戻し(スクラブ)タイムラインとして読み出され、Create/Stop/Heartbeat レコードはサーバー側の壁時計時間を使用します。
- リスト表示:
- S2 はストリームを辞書式順序で管理するため、データベースによる順序維持は不要です。
- ストリーム名形式:
。sessions/<反転したタイムスタンプ>
パージングにより、新規セッション開始時のリスト更新もベストエフォートなサイドカーインデックスで対応します。startAfter
- ライブテール:
- GET
はスナップショットの末尾から S2 を読み出し、SSE(Server-Sent Events)を介して新しいレコードをブラウザにブリッジします。/api/sessions/:id/live - 同じストリームが歴史的スクラブと現在のライブエッジの両方を提供します。
- GET
- 並行処理:
- ストリームの作成および停止はアクティブ/停止フェンシングトークンを発行します。
- イベントおよびハートビートの追加はアクティブフェンシング下で実行されるため、完了したセッションが後から書き込まれることはありません。
アーキテクチャの利点
- ストリームの自動作成: 初回追加時(
)に作成され、バジンのデフォルト設定を継承するため、プロビジョニング管理は不要です。createStreamOnAppend - 完全なセルフホスト: S2 クラウドまたは s2-lite のいずれかに対応し、すべての処理が自分のインフラストラクチャ内で動作します。
- セキュリティ: ブラウザには S2 トークン自体は渡されず、すべての読み書きは RePlaya サーバーを介して行われます。
一般的なリプレイバックエンドとの比較
| 機能 | 一般的なリプレイバックエンド | RePlaya |
|---|---|---|
| 実行サービス数 | メッセージバス、分析ストア、関係型 DB、オブジェクトストア、検索インデックス | ノードサーバー 1 つ + S2 |
| ライブセッション | インゲスト/フラッシュの遅延後に再生 | 同じストリームからアクティブセッションをライブテール |
| 保存済み録画 | オブジェクトストレージのバイナリ+分散データベースのメタデータ | セッションごとの順序付き S2 ストリーム |
| セルフホスト足跡 | マルチサービスクラスタ(通常 Kubernetes) | サンプルプロセス 1 つ + S2(または s2-lite) |
全体設計については ARCHITECTURE.md を参照してください。
設定とデプロイメント
サーバー側のみを S2 に依存する設定は、以下の通り
.env.local などに記述します。
- 本番環境: RePlaya は収集用インターフェースをパブリックにし、ダッシュボードおよび読み取り専用 API をプライベートとして扱います。
セキュリティとアクセス制御
-
API の公開制限:
- 読み込み用 API(
,GET /api/sessions*
など)はインターネットに公開しないでください。SSO やアクセスクонтроールレイヤー(VPN, Cloudflare Access など)の背後に置きます。/api/health - 公開側には収集用エンドポイントのみを公開し、以下のルートを厳密に制限してください:
GET /recorder.jsGET /vendor/rrweb.min.js- 書き込み専用エンドポイント(
,POST /api/sessions
,/events
,/heartbeat
)/stop
これらを環境変数で制御します:
REPLAYA_ALLOWED_CAPTURE_ORIGINS=... REPLAYA_PROJECT_KEY=pk_live_... - 読み込み用 API(
-
本番環境設定:
を設定し、インゲスト認証を強制します。NODE_ENV=production- トークンなしでのサーバー起動はブロックされます。
- リバースプロキシの背後で動作する場合は
を設定し、クライアント IP によるレート制限に正確に対応させてください。REPLAYA_TRUST_PROXY=true
推奨される環境変数(本番)
NODE_ENV=production REPLAYA_PROJECT_KEY=pk_live_replace_with_public_write_key REPLAYA_APPEND_TOKEN_SECRET=replace-with-a-long-random-secret REPLAYA_ALLOWED_CAPTURE_ORIGINS=https://app.example.com,https://www.example.com REPLAYA_TRUST_PROXY=true
- プロジェクトキー: レコーダーの初期化時に
と共に渡されます。apiHost - セッション作成: 短いライフタイムを持つ追加トークンが返され、イベント書き込み時に送信されます。
レート制限と制御パラメータ
: デフォルト 60 回/分/クライアント × プロジェクトREPLAYA_SESSION_CREATE_RATE_LIMIT
: デフォルト 600 回/分/クライアント × セッションREPLAYA_SESSION_APPEND_RATE_LIMIT
: デフォルト 100REPLAYA_MAX_EVENTS_PER_BATCH
: デフォルト 8 MBREPLAYA_JSON_BODY_LIMIT
: デフォルト 86400000 ms(1 日)REPLAYA_APPEND_TOKEN_TTL_MS
: すべてのリクエストに対してアクセスログを出力(開発:ON、本番:OFF;エラーは常に記録)。REPLAYA_LOG_REQUESTS
: デフォルト 10,000 ms。停止時に進行中のリクエストをドレインし、滞っているストリームを放棄します。REPLAYA_SHUTDOWN_GRACE_MS
境界制御の詳細については ARCHITECTURE.md を参照してください。
Docker
ビルドと実行
docker build -t replaya . docker run --rm -p 8787:8787 \ -e NODE_ENV=production \ -e S2_ACCESS_TOKEN=... \ -e S2_BASIN=... \ -e REPLAYA_PROJECT_KEY=pk_live_... \ -e REPLAYA_APPEND_TOKEN_SECRET="$(openssl rand -hex 32)" \ -e REPLAYA_ALLOWED_CAPTURE_ORIGINS=https://app.example.com \ replaya
- 実行ユーザー: イメージは単一コンパイルされたサーバーを非ルートユーザーとして実行します。
- 公開ルートの制限: 収集用とレコーダー用ルートのみに限ってください。
セcrets の扱い方
- シークレットは
またはシークレットマネージャーから渡してください。-e VAR=value - ローカルの
を再利用しないでください。.env
を使用する場合は、クォート付きの値(例:docker --env-file
)がそのままコンテナ内へ到達しないよう注意が必要です。"..."
スクリプトコマンド
: API および Vite(ダッシュボード)を起動pnpm dev
: クライアント/サーバーの型チェックを行い、フロントエンドをビルドpnpm build
: ESLint を実行pnpm lint
: ユニットテストおよびスモークテストを実行(レコーダー不変性+HTTP スモーク)。S2 は必須ではありません。pnpm test
: 実在する s2-lite に対し、S2 往復テストを実行pnpm test:integration
テスト
ユニットテスト (pnpm test
)
pnpm test- レコーダーの不変性検証とサーバーの HTTP スモークテストを行います。
- 配信機能、セキュリティヘッダー、インゲスト認証拒否などの確認が含まれます。
- S2 がなくても実行できます。
インテグレーションテスト (pnpm test:integration
)
pnpm test:integration- 「作成 → 追加 → 再生 → 削除」という実際のパスを網羅します。
を指定しない場合、テストはスキップされます。S2_TEST_ENDPOINT
s2-lite の起動方法:
docker run -d -p 8080:80 ghcr.io/s2-streamstore/s2-lite
実行コマンド:
S2_TEST_ENDPOINT=http://localhost:8080 pnpm test:integration
CI ジョブ構成
CI では以下の 2 つのジョブが実行されます:
- ビルド/リンター/ユニットテスト: Node 20 と 24 で実行。
- インテグレーション: s2-lite を起動し、往復テストスイートを実行。