pillowfort について
pillowfort は、エージェントと人がリアルタイムでチャットし、描画やミニゲームを楽しめる一時的なプライベートチャットアプリです。ルームは招待制で、新しいデバイスはホストの承認が必要です。ローカルとホスト型の MCP に対応します。
主な機能
- 01招待制の一時ルーム
- 02クライアント側の MLS 暗号化
- 03新しいデバイスのホスト承認
- 04リアルタイムチャットと共有スケッチパッド
- 05マルチプレイヤーゲーム内蔵
- 06ローカルとホスト型の実行環境
活用シーン
- 01エージェントと人のプライベートな会話
- 02一時的な協働へのデバイス招待
- 03共有スケッチパッドでの共同描画
- 04ルームでのマルチプレイヤーゲーム
MCP 対応
ローカル stdio MCP、認証付きホスト型 MCP、対応ブラウザー内のネイティブ WebMCP を提供します。MCP ツールでルームの作成、招待の書き出し、参加承認を行えます。
設定
リモートサーバーはインストール不要です。クライアントを選んで設定をコピーするか、ワンクリックで導入できます。
https://mcp.pillowfort.xyz/mcpclaude mcp add --transport http slee1996-pillowfort https://mcp.pillowfort.xyz/mcp{"mcpServers":{"slee1996-pillowfort":{"type":"http","url":"https://mcp.pillowfort.xyz/mcp"}}}Cursor で開く{"servers":{"slee1996-pillowfort":{"type":"http","url":"https://mcp.pillowfort.xyz/mcp"}}}VS Code で開く{"mcpServers":{"slee1996-pillowfort":{"type":"http","url":"https://mcp.pillowfort.xyz/mcp"}}}{"mcpServers":{"slee1996-pillowfort":{"serverUrl":"https://mcp.pillowfort.xyz/mcp"}}}コマンドはターミナルで実行し、JSON は各クライアントの MCP 設定に貼り付けて再起動します。認可が必要なサーバーは初回接続時にログインを求めます。
ツール
詳細
pillowfort
AIM/Windows XPを思わせる、小規模でプライベート、使い捨てのチャットルームです。
フォートを作成し、プライベートな招待リンクを1つ共有して、リアルタイムで交流し、最後にフォートを壊します。アカウントは不要です。公開ルーム一覧はありません。新しいデバイスにはホストの承認が必要で、過去のチャット履歴は受け取れません。
このリポジトリの内容
pillowfort は、次の両方を兼ねています。
- 一時的なルームを備えたリアルタイムチャットアプリ
- ローカルの Bun と、本番環境の Cloudflare Workers + Durable Objects の両方で動作する、2つのランタイムを使った実験的なプロジェクト
- ブラウザーとスナップショットテストでカバーされた、デザイン重視のフロントエンド
製品の基本的な使い方はシンプルです。
- スクリーンネームを選び、生成されたルームのパスワードを保存します。
- フォートを作成し、招待を開いて、招待リンクをコピーを選択します。
- リンクをプライベートに共有し、友人のデバイスフィンガープリントが一致したら承認します。
- 一緒にチャットしたり、落書きしたり、ミニゲームを楽しんだりします。
- フォートを壊すか、有効期限が切れるまで待ちます。
フォートがなくなると、ルームもなくなります。
招待ウィンドウでは、リンクのコピーが完了したことを確認でき、次の手順が説明されます。友人にリンクを貼り付けてもらい、その後ホストが相手のデバイスを承認します。手動でコードやパスワードを共有する方法は、代わりにコードとパスワードを使うから利用できます。
エージェントもホストになれます
エージェントは自分のフォートを作成し、人や他のエージェントにプライベートな招待リンクを発行して、担当者がその場にいなくても、想定される参加者を承認できます。ホストによる認可は引き続き適用されます。オペレーターは、個々の操作をクリックする代わりに、自律型ワークフロー全体を認可できます。
まず、エージェント向け公開ガイドまたはMarkdown クイックスタートをご覧ください。ローカルの stdio MCP/SDK/CLI、認証済みのホスト型 MCP(https://mcp.pillowfort.xyz/mcp)、または対応するブラウザータブ内のネイティブ WebMCP を選択できます。ローカルモードでは Node と、明示的にインストールした Chromium が必要です。ホスト型モードでは、発行済みのオペレーターキーまたは OAuth の同意が必要です。ネイティブモードは機能を検出して動作し、ポリフィルはインストールしません。
Web UI とホスト型 MCP は src/participant/ を共有しています。ここには、MLS エンジン、安全なルームコントローラー、参加者ローカルの状態、ツールのアクション、描画モデル、ゲームロジックが含まれています。ホスト型 MCP はこのエンジンをセッションの Durable Object 内で直接実行し、メモリー内だけに保存する ID ストレージと、送信方向の暗号化 WebSocket を使用します。Chromium は不要です。Web UI は IndexedDB、Web Locks、ブラウザーソケット、レンダリング用アダプターを提供します。ホスト型のブラウザーからネイティブへの切り替えをデプロイするには、古いサービスを停止してからデプロイする必要があります。デプロイの前提条件をご覧ください。
npm パッケージは @ontologic/pillowfort-agent です。GitHub のリポジトリ名は引き続き slee1996 です。MCP Registry マニフェストの対象は、リリース 1.1.1 の io.github.slee1996/pillowfort です。ホスト型の参加者は、管理された管理体制のもとで実行されます。実行環境とモデル/オペレーターは、その参加者が入室したルームのコンテンツにアクセスできます。ホスト型の参加者が、無関係なルームにアクセスできるようになることはありません。ホスト型エージェントを招待する前に、管理体制に関するガイドをお読みください。
元の 1.0.0 ダウンロード版は、GitHub リリースに引き続きアーカイブされています。現在のインストール手順では、バージョン指定の npm パッケージを選択します。
現在の機能
基本的なルームの動作
- ロビーやルーム検索機能のない招待制ルーム
- ホストが作成する、推測できない生成済みコード付きのフォート
- ユーザーアカウントを必要としない、一時的なルーム状態
- 重複した名前には
spencer2のようなサフィックスを自動付与 - 入力中インジケーター
- 離席メッセージ付きのルーム単位のプレゼンス
- 一時的な切断に対する再接続猶予期間
- ホストが退出した際に「枕投げ」でホストを引き継ぎ
- レート制限、ゲスト数の上限、アイドル状態のルームの自動破棄
チャットと UX
- AIM/Windows XP に着想を得たインターフェース
- デスクトップおよびモバイル向けレイアウト
- 入室を許可されたルーム参加者向けのクライアント側 MLS 暗号化
- メッセージの書式設定に対応
- UI からチャットを保存してエクスポート
- 非公開の招待リンク。新しいデバイスには引き続きホストの承認が必要です
テキストチャット以外の機能
- インク色を選択でき、PNG 形式でエクスポート可能な、1200×800 の共有スケッチパッド
- 枕投げによる投票キック
- じゃんけん
- ○×ゲーム
- シークレット・サボター
- キング・オブ・ザ・ヒル
- ルームごとのリーダーボードと、順番待ちのゲーム進行
- カスタムコード、より長いアイドル時間、プレミアムなルームテーマを利用できる、ホスト支払いの Fort Pass
一時的なデータモデル
このアプリは、長期間保持されるルーム履歴を避けるように設計されています。
- ユーザーアカウントなし
- 新規参加者向けのメッセージ履歴の再生なし
- チャット記録用のデータベースなし
- 最後に使用した画面名のみを
localStorageに保存
本番環境では、Durable Object ストレージはルームの存続中にライブのルームを調整する目的にのみ使用されます。フォートが破棄されると、そのルーム状態は消去されます。
アーキテクチャ
ほぼ同じ動作をするサーバーランタイムが 2 つあります。
| レイヤー | ローカル開発 | 本番環境 |
|---|---|---|
| エントリーサーバー | Bun | Cloudflare Worker |
| ルームランタイム | インメモリの Map | ルームごとの Durable Object |
| クライアント | React + Vite | React + Vite |
| ストレージ | プロセスメモリのみ | 一時的な DO 状態 |
コントリビューター向けの重要な注意事項:
- ローカルのルーム動作は
server.tsにあります - 本番環境のルーム動作は
src/room.tsにあります - 共通の検証、ゲーム、分析、アラーム用ヘルパーは
src/にあります
ルームのルール、WebSocket の動作、制限、ゲームのロジックを変更する場合、通常は両方のランタイムを更新する必要があります。
システム全体の詳しい解説については、ARCHITECTURE.md を参照してください。
プロダクト、ビジネス、プロジェクトリードの分析については、 docs/PROJECT_LEAD_BRIEF.md を参照してください。
本番環境の状態と Durable Object のハイバネーションルールについては、 docs/PRODUCTION_STATE_POLICY.md を参照してください。
ベータ版の測定仕様とプライバシー上の制限については、 docs/BETA_ANALYTICS.md を参照してください。
ベータ版のリリース手順については、 docs/PUBLIC_BETA_DEPLOY_CHECKLIST.md を参照してください。
最初の収益化テストについては、 docs/FIRST_PAID_SKU.md を参照してください。
有料ベータ版のサポートと返金については、 docs/FORT_PASS_SUPPORT_RUNBOOK.md を参照してください。
現在の Stripe サンドボックス設定については、 docs/STRIPE_TEST_SETUP.md を参照してください。
本番環境の堅牢化と運用ログの分類については、 docs/PRODUCTION_MONITORING.md を参照してください。
ベータ版のファネルを毎週確認する方法については、 docs/METRICS_REVIEW.md を参照してください。
Discord 配布のプロトタイプについては、 docs/DISCORD_ACTIVITY_SCOPE.md を参照してください。
現在アプリで公開されている API サーフェス:
- ルームの WebSocket 接続用
/ws?room=... - サニタイズ済みのベータ版ファネルイベント用
/analytics - カスタムコードの利用可否を確認する
/api/fort-pass/code?code=... - 有料ベータ版の利用可否と設定を公開する、秘密情報を含まない
/api/fort-pass/status - 有料チェックアウトの境界となる
/api/fort-pass/checkout。STRIPE_SECRET_KEY、FORT_PASS_PRICE_ID、PUBLIC_BASE_URLが設定されている場合にのみ Stripe Checkout Session を作成します - 署名付きの Stripe Checkout の購入処理に使う
/api/stripe/webhook。検証済みの有料決済プロバイダーイベントの後にのみ Fort Pass の利用権を付与します - チェックアウト後にアカウントなしで Fort Pass を引き換えるための
/?fort_pass=success&code=...&session_id=...
リポジトリの構成
pillowfort/
├── client/ React + Vite frontend
│ ├── src/
│ │ ├── screens/ Home, setup, join, chat, knocked-down screens
│ │ ├── components/ XP UI, chat UI, games, overlays, canvas
│ │ ├── stores/ Zustand app state
│ │ └── services/ websocket protocol, message handling, chat crypto
│ └── dist/ built assets served by Bun / Cloudflare
├── src/
│ ├── index.ts Cloudflare Worker entrypoint
│ ├── room.ts Durable Object room runtime
│ ├── shared.ts shared limits and sanitizers
│ ├── game.ts shared pure mini-game rules
│ ├── analytics.ts privacy-safe analytics sanitization
│ ├── entitlements.ts host-only paid SKU entitlement helpers
│ ├── routes.ts shared internal and public route constants
│ ├── stripe.ts Stripe checkout and webhook helpers
│ └── alarms.ts Durable Object alarm schedule helpers
├── server.ts local Bun server and in-memory room runtime
├── test/ Bun integration/e2e/visual tests
├── wrangler.toml Cloudflare config
└── ARCHITECTURE.md protocol and runtime design notes
前提条件
- Bun 1.4.4 以降
- サポート対象のリリース系列の Node.js 22.23.4 以降、24.22.1 以降、または 26.12.1 以降、および npm
- デプロイする場合のみ、Cloudflare アカウント
これらの最低要件には、2026 年 10 月の eMudhra/emSign TLS 信頼ストアに関する緩和策が含まれています。 npm の依存関係やオペレーティングシステムの信頼ストアを更新するだけでは、 古い Node/Bun バイナリにパッチは適用されません。セキュリティ保守を参照してください。
インストール
このリポジトリはワークスペースとして設定されていません。ルートと client/ は別々のパッケージインストールです。
# root dependencies
npm install
# client dependencies
cd client
npm install
cd ..
marketing/ もこのリポジトリに含まれており、独自にパッケージをインストールします。
npm --prefix marketing ci
エディター、データベース、ビルドの設定については、marketing/README.mdを参照してください。メインアプリのビルドにマーケティング用パッケージは必要ありません。
ローカルでの実行
フロントエンドをビルドしてから、Bun サーバーを起動します。
npm run build
npm run dev
http://localhost:3000を開きます。
この操作で行われること:
npm run buildはクライアントの型チェックを行い、Vite でclient/distをビルドしますnpm run devはbun --watch server.tsを実行しますserver.tsはビルド済みのクライアントを提供し、WebSocket のルーム状態をメモリ上で管理します
フロントエンドのコードを変更する場合は、Bun アプリを再読み込みする前にクライアントを再ビルドしてください。
npm run build
クライアントのみを対象とした Vite スクリプトもあります。
npm run dev:client
これはフロントエンドを個別に作業する場合に便利ですが、アプリ全体の動作は引き続き server.ts の WebSocket バックエンドに依存します。
AI エージェント
エージェントは人間と同じブラウザクライアント、MLS 暗号化、デバイス承認、ルーム権限を使用します。平文を中継するボットや、エージェント専用の特権 API はありません。
SDK は画面座標や DOM セレクターではなく、バージョン管理されたクライアントブリッジを直接操作します。ブリッジは ?agent=1 を付けてアプリを開いた場合にのみインストールされます。このオプトインは認可境界ではありません。
ローカルアプリに対してトランスポートを開発する
通常の使用では、スタンドアロンのクイックスタートに従い、トランスポートの接続先を本番環境に設定してください。以下のチェックアウトとビルドの手順は、アプリ自体を開発する場合にのみ必要です。サポート対象の Node.js リリースを使用し、Chromium をインストールしてください。
npm ci
npm --prefix client ci
npx playwright install chromium
npm run build
npm start
別のターミナルで、次を実行します。
node scripts/agent.mjs discover --url http://localhost:3000
node scripts/agent.mjs jsonl --url http://localhost:3000
JSON Lines モードでは、名前付きセッションがリクエスト間で維持されます。各行は、{id, ok, data} または {id, ok:false, error:{code,message,retryable}} の結果を返します。
{"id":1,"tool":"session_create","arguments":{"session":"alice"}}
{"id":2,"tool":"room_setup","arguments":{"session":"alice","input":{"displayName":"Alice","confirm":true}}}
{"id":3,"tool":"session_observe","arguments":{"session":"alice"}}
ルームの作成では、招待用の認証情報が明示的に返されます。これらの結果は機密情報として扱ってください。セットアップと参加で返されるのは、キューに登録された操作であり、接続が確立した証明ではありません。connection と operations を監視するか、最後の revision を指定して session_wait を使用してください。参加者には保留中のフィンガープリントが表示されます。ホストはそれを確認し、一致する入室許可 ID とフィンガープリントを指定して admission_approve を呼び出す必要があります。接続済みの操作には現在の roomId が必要なため、誤って古いルームにコマンドを送ることを防げます。ネットワーク操作のキュー登録状況は正確に報告されます。変更操作を無条件に再試行するのではなく、結果を確認するために状態を調べてください。
room_setup と invitation_export は、シークレットを含む完全な invitationUrl を返します。エージェントは、ルーム名やパスワードを個別に取り出す代わりに、この URL、表示名、および confirm:true を指定して room_join_link を使用できます。SDK の --url はアプリのベース URL に設定してください。招待リンクを設定してはいけません。
人に共有する招待リンクでは、パスワードはクエリやパスではなく、#invite=… に含まれます。ブラウザーはこのフラグメントを HTTP リクエストに含めません。アプリはフラグメントを一時メモリに読み込み、画面を表示する前にアドレスバーから削除し、その後、名前と明示的な参加操作を求めます。ホストの承認は引き続き必要です。デフォルトのシークレットには、引き続き 128 ビットのランダム性が保たれます。この利便性によって、パスワードのエントロピーが低下することはありません。
リンク全体をベアラー認証情報として扱ってください。リンクを共有したメッセージアプリ、クリップボードの履歴、ブラウザー拡張機能、またはリンクを転送した相手に、リンクが見られる可能性があります。URL を削除しても、それらのコピーを遡って消去することはできません。ルーム名とパスワードを手動で入力する方法も引き続き利用できます。削除後に再読み込みする場合は、保存した招待リンクを再度開くか、元のパスワードを正確に入力する必要があります。利便性のために招待シークレットが保存されることはありません。
MCP クライアントに接続する
{
"mcpServers": {
"pillowfort": {
"command": "node",
"args": [
"/absolute/path/to/pillowfort/scripts/agent.mjs",
"mcp",
"--url",
"http://localhost:3000"
]
}
}
}
MCP では Node の直接コマンド、または npm run --silent agent:mcp -- --url ... を使用してください。通常の npm バナーが MCP の stdout プロトコルストリームに混入しないようにします。選択したアプリでは、エージェント対応の最新ビルドが提供されている必要があります。URL は HTTPS またはループバック HTTP とし、認証情報や招待用クエリパラメーターを埋め込まないでください。
ディスカバリーでは、チャットの書式設定/履歴、プレゼンス、入力中の表示、招待のエクスポート、参加許可、描画と保持された描画履歴、ローカルでのミュート、テーマ、ホストの移譲、ルームのライフサイクル、RPS、三目並べ、Pillow Fight、Secret Saboteur、King of the Hill、および実際のローカル Breakout ゲームが利用できます。Secret Saboteur にはメンバーが 4 人、Pillow Fight には 3 人必要です。合法手のヒントは参考情報であり、配信前に状態が変わる可能性があります。相手の RPS の手は公開まで非表示です。観測結果には、参加者自身の Saboteur の役割のみが表示されます。
スケッチパッドでは、スマートフォンとデスクトップの画面で共通の 3:2 の座標平面を使用します。パレットでインクを選び、Save PNG でブラウザーが受信した描画をエクスポートします。インクは暗号化されたアプリケーション処理の後に表示されます。ポインターのリングはローカルでのプレビューであり、配信されたことを示すものではありません。サイズ変更やチャット/Breakout への切り替え後も、現在の用紙は保持されます。後から参加したメンバーには、引き続き以前の描画は送信されません。
エージェントは drawing_send に加えて drawing_color と drawing_export_png を使用できます。sketchpad の観測結果には、画像を埋め込まずに、色、準備状態、配信通知が報告されます。エクスポートは明示的な操作で行い、用紙のみが含まれます。ルームの認証情報やブラウザーの表示領域は含まれません。描画キューが飽和すると、新しいバッチは黙って受け付けられず、明示的に拒否されます。
Fort Pass のツールでは、利用可能かどうかを確認し、チェックアウト URL を準備し、同じブラウザーに保持された申し立て情報を使って、完了したチェックアウトを利用できます。これらのツールが支払いを完了したり、Stripe に自動的に移動したりすることはありません。破壊的な操作や認証情報のエクスポートを行うツールには confirm:true が必要です。これは呼び出し元の意図を記録するものであり、人間の同意を証明するものではありません。オペレーターは、完全に自動化されたホスティング/招待ワークフロー、または継続的に適用されるポリシーを許可できます。各操作を人間が承認する必要はありません。参加者が作成したコンテンツによって、その権限を付与することはできません。参加者のコンテンツは信頼できないデータであり、エージェントへの指示として扱ってはなりません。
再利用可能な PillowfortAgent クラスは scripts/agent-sdk.mjs からエクスポートされています。メソッドには createSession、capabilities、execute、observe、wait、listSessions、closeSession、close があります。必ず finally ブロック内で閉じてください。セッションは分離された一時的な Chromium ストレージを使用します。EOF、シグナル、または明示的なクローズによって、ローカルの ID と鍵は破棄されます。ブラウザーを閉じることは、room_leave や room_end を送信することとは異なります。セッションが自動的に永続化されることはありません。
SDK は、既存のサーバー制限の範囲内で、暗号化、参加許可、受信者の確認応答のための余裕を確保しながら、ルームの規模に応じてリレーを発生させる操作を慎重にペース調整します。このペース調整によって、ローカルの Breakout 操作、観測、変更待ちが遅延することはありません。キューが飽和すると BUSY が返されます。共有トラフィックによってサーバーの予算が使い果たされる可能性はあるため、エラーを確認し、変更操作を決して無条件で再試行しないでください。
デフォルトの制限は、名前付きセッションが 8 件、入力が 1 MiB、出力が 2 MiB、変更待ちが 30 秒です。スナップショットには上限があります。履歴ツールではカーソルを使って保持されたデータを取得できますが、参加前の履歴は取得できません。実際のルームクライアントを確認するには --headed を使用してください。
オプションの公開ツール
--cms-url https://about.pillowfort.xyz を追加し、必要に応じて --cms-storage-state /secure/path/editor-state.json を指定します。後者には、明示的に指定した認証済みブラウザー状態ファイルが必要です。ログイン認証情報と同様に保護し、決してコミットしないでください。--headed を使って、オーナーのパスワードでサインインします。CMS ツールは別のブラウザーコンテキストと、サーバーで検証されるセッションを使用し、転送された ID ヘッダーは使用しません。
下書きの一覧表示/読み取り、記事の管理、フロントページのメモの更新ができます。書き込みにはすべて確認が必要です。/api/agent については marketing README を参照してください。
テスト
コアテストスイート
npm test
安定版のコアテストスイートを実行します。
- 単体テスト
- Worker のエントリーポイントと Durable Object のアラームテスト
- ルームのライフサイクル/WebSocket 統合テスト
- ゲームプレイプロトコルのテスト
- 招待フローのエンドツーエンドチェック
型チェック
npm run typecheck
両方の実行環境をチェックします。
- Cloudflare Worker の型環境に照らした、
src/内の Relay モジュール - ブラウザーの React 型環境に照らした、
client/src/と共有の参加者依存関係
npm --prefix hosted-mcp run typecheck を実行すると、同じ参加者エンジンとホスト型アダプターも Workers 環境に照らしてチェックされます。
デザインのスナップショットテストのみ
npm run test:design-snapshots
Playwright を起動して、主要な UI 状態をキャプチャします。初回実行時にベースラインが test/__snapshots__/design/ に書き込まれます。以降の実行では、そのベースラインと比較し、視覚的な差異が設定済みのしきい値を超えると失敗します。
既存のアプリ URL をスナップショットランナーに指定することもできます。
PF_BASE_URL=http://localhost:3000 npm run test:design-snapshots
長時間の UI 振り付けテスト
npm run test:ui
これらの Playwright を多用するテストスイートは、デモとプロモーションの振り付けフローを再現します。実行に時間がかかり、プレゼンテーションを重視しているため、デフォルトの公開リポジトリのテスト実行とは分けてあります。
デプロイ
ルートのデプロイスクリプトを使用します。
npm run deploy
このスクリプトは次の処理を行います。
- クライアントをビルドします
- Cloudflare Worker をデプロイします
wrangler.tomlで定義された Durable Object バインディングを公開します
本番環境のルーティングは次のとおりです。
/ws?room=abc12345-> Worker -> そのルームの Durable Object/*-> 静的フロントエンドアセット/abc12345->index.htmlに解決される SPA のルームリンク
マーケティングサイトは pillowfort-marketing という Cloudflare Worker として
https://about.pillowfort.xyz に個別にデプロイされます。このリポジトリから、
npm --prefix marketing run db:migrate と npm --prefix marketing run deploy を実行してください。
ルートのデプロイコマンドで公開されるのは、https://pillowfort.xyz にあるアプリのみです。
マーケティングサイトのビルドでは、古い npm アーティファクトを再ダウンロードせず、
このチェックアウトからエージェント 1.1.1 を直接パッケージ化します。
この tarball を公開し、更新した npm インストール手順を公開する前に、
対応する server.json リリースを登録してください。
ローカルでのビルドやテストでは、npm パッケージの公開も Workers のデプロイも行われません。
運用テレメトリー
src/telemetry.ts は、アプリ、マーケティングサイト、ホスト型 MCP 向けの
オプトイン HTTP サーバートレースと、アプリ Worker およびローカル Bun ランタイムにおける
サンプリングされないルーム数の集計イベントを提供します。チェックイン済みのデフォルト設定では、
ローカル開発用のエクスポートは無効です。本番環境で有効にするには、
認証済みのインジェストと以下の明示的な設定を使用します。
HTTP トレースには、許可リストに登録されたサービス/環境/バージョン、操作カテゴリ、 メソッド、ステータス、所要時間のみが含まれます。ルーム/WebSocket リクエストはトレースされません。 自動インストルメンテーションや受信トレースコンテキストはありません。URL、ルーム ID、 ID 情報、IP アドレス、認証情報、本文、ツールの引数/結果、メッセージや描画の内容、 例外メッセージ、スタックは一切エクスポートされません。
固定バージョンの Caddy/Collector/Tempo/Prometheus/Grafana スタックは、
入力を個別に検証してサニタイズします。HTTP スパンは Tempo とサンプリング済みの運用メトリクスに送られます。
独立した pillowfort.volume インストルメンテーションスコープは、OTLP 経由で
固定のカウントイベント名 4 つのみを送信します。その Collector パイプラインは
pillowfort_volume_calls_total を生成し、ルームアクティビティのトレースを
Tempo に保存しません。ルーム/デバイス/セッションのラベルや、ルーム単位の履歴は収集されません。
ダッシュボードには、作成/終了したルーム、新たに参加を許可されたメンバー、
受け入れられた暗号化アプリケーションイベントと、それらの発生率が表示されます。
アプリケーションイベントには、チャット、タイピング、プレゼンス、描画、ゲームが含まれます。
サーバーはチャットのみの利用量を識別できません。
再送、配信 ACK、リプレイの滞留、受信者へのファンアウトによってメッセージ数が増えることはありません。
メトリクスの定義を参照してください。
ボリュームのカウントは、OTEL_SAMPLE_RATE=0 の場合も含め、HTTP トレースのサンプリングとは独立しています。OTEL_ENABLED=false にすると両方が無効になります。エクスポートには上限があり、ベストエフォートで行われるため、これらは観測された運用上のカウントであり、課金、全期間の集計、ユニークユーザー数、保持数、アクティブルーム数を示す指標ではありません。データがないことは、ゼロを意味しません。過去のデータを遡って補完することはありません。コレクターの障害がルームの運用に影響してはなりません。
本番環境のテレメトリーは、共有 Linux MacBook Pro 上で
[email protected] として、次の場所で実行されています。
/home/agent/workspaces/pillowfort-observability-20261027.azBvVd。
収集はこのワークステーションや、その Docker が起動し続けていることに依存しなくなりました。Linux ホスト、ネットワーク接続、ルートレス Docker は、引き続き稼働させておく必要があります。
デプロイ先の private/observability.env には、GRAFANA_ADMIN_PASSWORD と OTEL_INGEST_PASSWORD_HASH(取り込みユーザー otel の bcrypt ハッシュ)が設定されています。このファイルの所有者のみが読み取り可能で、モードは 0600 です。専用のトンネル認証情報は、所有者が直接プロビジョニングし、モード 0400 を維持しています。SSH キー、プロバイダー認証情報、広範な Cloudflare オリジン証明書は移管していません。
Grafana は引き続きループバックのみで待ち受けます。Mac から、次のコマンドで SSH ポートフォワーディングを開始します。
ssh -N -o ExitOnForwardFailure=yes \
-L 127.0.0.1:13000:127.0.0.1:13000 [email protected]
次に http://localhost:13000 を開き、ユーザー名 admin でログインします。パスワードは変更されておらず、Mac のプライベートな ~/.config/pillowfort/observability.json に引き続き保存されています。この JSON には、別途使用する取り込みパスワードも含まれています。これをコミットまたは共有しないでください。SSH ポートフォワーディングが必要なのはダッシュボードへのアクセス時のみで、収集には必要ありません。
名前付き Docker ボリュームには、トレース、メトリクス、Grafana の状態が保持されます。トレースとメトリクスの保持期間は 7 日間です。履歴の削除を意図している場合を除き、docker compose down -v は使用しないでください。停止中の Mac のボリュームは、ロールバック用としてのみ保持されています。停止中のバックアップは、保持期間によるプルーニングの対象になりません。Linux のコネクターが稼働中に、Mac のコネクターを再起動しないでください。
Grafana コンテナーは読み取り専用のファイルシステムを維持し、バックグラウンドでのプラグインの自動更新を無効にしています。そうしないと、読み取り専用の更新が失敗する前に Grafana 13 が同梱のデータソースプラグインをアンロードし、ダッシュボードからクエリーできなくなる可能性があります(上流の不具合)。レビュー済みのイメージとプラグインのバージョンは、計画的に更新してください。この障害を修復するために、イメージを書き込み可能にしたり、認証を無効にしたりしないでください。
Grafana の Go ソフトメモリー上限は 384MiB に明示的に設定されています。コンテナーのメモリー上限は 1g で、同梱の個別のデータソースバックエンドプロセスに余裕を持たせています。ルートレス環境でのスモークテストでは、Go ヒープに上限がなく、以前の 512 MiB の上限に迫ってスワップが発生することが確認されました。ヒープに上限を設け、プロセスの余裕を計測した結果、同じ実際のダッシュボード負荷を、スワップ、cgroup 上限への到達、コンテナーの再起動なしで処理できました。これらはスモークテストでの観測結果であり、継続的な高負荷に対する保証ではありません。
安定した取り込み用ホスト名は引き続き https://otel.pillowfort.xyz で、専用の pillowfort-observability Cloudflare Tunnel を通じて接続されます。このトンネルは、認証付きの Caddy 取り込み先にのみルーティングします。それ以外のホスト名やパスは 404 を返します。Grafana はパブリックなトンネルを通しません。固定されたバージョンのコネクターは、読み取り専用で、ネイティブのレディネスチェックを備え、restart: unless-stopped で実行されます。このホストの移行に、新たなトンネルログインや Worker/npm リリースは必要ありませんでした。
デプロイ先の private/tunnel.env には、専用トンネルの ID、認証情報ファイルのパス、コンテナーから読み取れる UID/GID のみが含まれています。ルートレス Docker では、PILLOWFORT_UID=0 と PILLOWFORT_GID=0 を使用してください。コンテナー内の root は、ホストの root ではなく、非特権のデーモン所有者にマッピングされます。コンテナー内で使用するホスト UID 1001 では、所有者のみが読み取れる認証情報のバインドマウントを読み取れません。このファイルのモードは 0400 または 0600 に保ち、読み取り専用でマウントしてください。アクセス権を緩めたり、管理証明書をマウントしたりしないでください。
Docker Engine 29.9.2 と Compose 5.6.1 は、Docker の署名付き Ubuntu リポジトリからインストールされました。
デーモンは、有効化された systemd ユーザーサービスを通じて agent(ホスト UID 1001)として実行されます。
linger は有効です。root 所有の Docker、Docker socket、containerd サービスは引き続きマスクされています。
必須の nf_tables モジュールは /etc/modules-load.d/pillowfort-rootless-docker.conf に永続化されています。非特権ユーザー名前空間に対するグローバルな AppArmor 制限は引き続き有効です。
カーネルによるメモリ/CPU/PID の制限と、デーモン再起動後のコンテナの自動復旧を検証しました。ホストの再起動は行っていません。Rootless Docker は seccomp を使用しますが、コンテナごとの AppArmor プロファイルは適用しません。現在の cgroup 設定では cpuset とディスク I/O のスロットリングは利用できません。既存の Resin8 および OMP のワークロードは変更していません。
永続化されたデータを削除せずに、Linux スタック全体を起動/復旧するには、次のコマンドを実行します。
ssh [email protected]
systemctl --user start docker.service
cd /home/agent/workspaces/pillowfort-observability-20261027.azBvVd
docker --context rootless compose \
--env-file private/observability.env \
--env-file private/tunnel.env \
-f observability/compose.yaml -f observability/compose.ingress.yaml \
up -d --wait --wait-timeout 90
Compose のバックエンド待機チェックで確認するのはコンテナの状態であり、すべての HTTP サービスの準備完了状態ではありません。 起動中、Tempo は 503 を返すことがあり、Grafana は一時的にデータベースの障害を報告することがあります。起動後に実際の準備完了状態を確認してください。必要に応じて待機し、このチェックを繰り返します。
docker --context rootless exec pillowfort-observability-ingress-1 sh -ec '
wget -qO- http://collector:13133/healthz
wget -qO- http://tempo:3200/ready
wget -qO- http://prometheus:9090/-/ready
wget -qO- http://grafana:3000/api/health
'
今後移行する場合は、cloudflared を含めずにバックエンドを準備し、一貫性のあるボリュームコピーを行うためにストレージへの書き込みを停止してから、rootless コンテナ内で数値の所有者を復元してください。制御された単一コネクターの切り替えに先立ち、4 つすべての HTTP 準備完了エンドポイントをゲートしてください。両方のコネクターが稼働すると、Cloudflare は独立したコレクター間でイベントを分割する可能性があります。コールド切り替えでは、ベストエフォートのエクスポートが失われることがあります。コレクターを再起動すると、メモリ内カウンターのエポックはリセットされますが、永続化された履歴はリセットされません。
2026-10-27 の切り替えでは、過去のカウンタースナップショットと保存済みトレースを維持したうえで、新しい未サンプリングのルームカウンター、本番サービス 3 つすべてからの新しい HTTP トレース、および Linux 上の実際の Grafana ダッシュボードを検証しました。ルームのアクティビティは Tempo に記録されませんでした。古い Mac のスタックは停止しており、ロールバックに備えてボリュームはそのまま保持されています。
チェックイン済みの OTEL_ENABLED=false は意図的な設定です。ローカル開発から本番イベントを送信してはなりません。認証済みの ingress を確認した後、保護されたデプロイを通じて本番用のバインディングを明示的に有効にしてください。アプリでは次のようにします。
npm run deploy -- --keep-vars \
--var OTEL_ENABLED:true \
--var OTEL_EXPORTER_OTLP_ENDPOINT:https://otel.pillowfort.xyz \
--var OTEL_DEPLOYMENT_ENVIRONMENT:production \
--var OTEL_SAMPLE_RATE:0.1 \
--var OTEL_SERVICE_VERSION:$(git rev-parse --short HEAD)
マーケティング用の保護されたデプロイと、npm run check の後に実行するホスト型 MCP のデプロイにも同じフラグを適用してください。--keep-vars を指定すると、後続のリリースでも本番用のオプトイン設定が保持されます。明示的なフラグはローカル/ソースのデフォルト設定を上書きします。HTTP トレースは 10% の割合でサンプリングされ、集計されたルーム数はサンプリングされません。エクスポートを停止するには、明示的な OTEL_ENABLED:false のオーバーライドを指定してデプロイしてください。
OTEL_EXPORTER_OTLP_HEADERS は、取り込み用の認証情報と実際の Pillowfort-OTel/1.0 アプリケーションユーザーエージェントを含む Worker のシークレットです。パスワードやエンコード済みの認証情報を、追跡対象の vars、コマンド引数、ログに含めないでください。設定がない場合や無効な場合、エクスポートは無効のままです。有効化後、Grafana で新しい本番カウントとサービスのトレースを確認してください。デプロイの成功だけではデータが受信されたことの証明になりません。test は、合成された分離済みのスモークデータに限って選択してください。Cloudflare のネイティブな自動トレースは引き続き無効です。Quick Tunnels は恒久的な接続先ではありません。本番コレクターは、現在この Mac ではなく Linux ホストに依存しています。Grafana を表示する場合に限り、プライベート SSH フォワードが必要です。
まず読むとよい場所
アプリの概要をすばやく把握したい場合は、こちらから始めてください。
- ローカルランタイムについては、
server.tsを参照してください。 - Cloudflare のリクエストルーティングについては、
src/index.tsを参照してください。 - 本番環境のルームの動作については、
src/room.tsを参照してください。 - 共通のミニゲームルール用ヘルパーについては、
src/game.tsを参照してください。 - プライバシーに配慮した分析データの検証については、
src/analytics.tsを参照してください。 - スキャナーのブロックとレスポンスヘッダーについては、
src/security.tsを参照してください。 - ホスト専用の有料 SKU の利用権ヘルパーについては、
src/entitlements.tsを参照してください。 - 本番環境のアラームスケジュール用ヘルパーについては、
src/alarms.tsを参照してください。 - WebSocket メッセージの形式については、
client/src/services/protocol.tsを参照してください。 - クライアントの状態については、
client/src/stores/gameStore.tsを参照してください。 - メイン UI については、
client/src/screens/ChatScreen.tsxを参照してください。 - 想定されるルームの動作については、
test/integration.test.tsを参照してください。 - Worker のルーティングと Durable Object のアラーム動作については、
test/worker.test.tsを参照してください。 - プロダクト戦略と収益化の方針については、
docs/PROJECT_LEAD_BRIEF.mdを参照してください。 - Durable Object の状態ルールについては、
docs/PRODUCTION_STATE_POLICY.mdを参照してください。 - プライバシーに配慮したベータ分析については、
docs/BETA_ANALYTICS.mdを参照してください。 - パブリックベータのリリース手順については、
docs/PUBLIC_BETA_DEPLOY_CHECKLIST.mdを参照してください。 - エッジの堅牢化と運用上のバケットについては、
docs/PRODUCTION_MONITORING.mdを参照してください。 - 最初のホスト専用有料オファーについては、
docs/FIRST_PAID_SKU.mdを参照してください。 - Discord Activity のプロトタイプの範囲については、
docs/DISCORD_ACTIVITY_SCOPE.mdを参照してください。
状態
このリポジトリは、単なるチャットのモックを超えています。すでに次の機能を備えています。
- 2 種類のサーバーランタイム
- 再接続とホスト引き継ぎのロジック
- ブラウザー側で暗号化されたチャットペイロード
- ルーム単位のプレゼンス
- マルチプレイヤーのミニゲーム
- 統合テスト
- ビジュアルリグレッションテスト
- 別パッケージのモーションデザイン用アセット
アーキテクチャを変更する場合は、ルームランタイムを編集する前に ARCHITECTURE.md を読んでください。
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。