機能 #10306
未完了PathCollector MVP実装:Web行動記録基盤(PC・iPhone・Android対応/MCP連携)
0%
説明
目的¶
PathCollectorを、対象Webサイト上の行動を端末横断で時系列記録し、個人情報を意味付きマスクしたうえで保存し、MCP経由で検索・取得・解析できる画面なしのサービスとして実装する。
最上位ゴールは「Web行動記録」であり、GA4/GSCの代替ではなく、DOM状態・DOM変化・ユーザー操作・遷移・エラーを後から解析可能な形で保持すること。
担当ワーカー¶
Claude Code(主担当)
推奨モデル: Sonnet
MVPスコープ¶
1. Collector SDK¶
- TypeScript + rrweb
- PC / macOS / iPhone / iPad / Android対応
- Chrome / Edge / Firefox / Safari / iOS Safari / iOS Chrome / Android Chrome
- 初期DOMスナップショット
- DOM追加・削除・属性・テキスト変更
- click / tap / input / change / submit / scroll / navigation / focus / blur
- visibilitychange / pagehide / orientationchange / online / offline
- JavaScript error / unhandled rejection
- SPA遷移対応
- touchend・pointerup・clickの重複排除、モバイル操作をtapへ正規化
- 5秒、50イベント、256KB、visibilitychange、pagehideでflush
- 終了時はsendBeacon優先、失敗時fetch keepalive
2. プライバシー¶
- クライアント側とサーバー側の二段階マスキング
- [MASKED_NAME] / [MASKED_EMAIL] / [MASKED_PHONE] / [MASKED_ADDRESS] / [MASKED_CARD_NUMBER] / [MASKED_PASSWORD] / [MASKED_TOKEN] 等
- input type、name、id、autocomplete、aria-label、周辺ラベル、CSS selector、正規表現による検出
- hidden input value、password、script/style本文、認証情報を保存しない
- data-pc-mask、data-pc-ignore対応
- URL query/fragment内の機微情報除去
- 生のPIIをログへ出力しない
- ブラウザフィンガープリント禁止
- IPを行動分析DBへ保存しない
3. 同意・オプトアウト¶
- ConsentState: unknown / granted / denied / essential_only
- consentMode: explicit / implicit
- optOut / optIn / isOptedOut
- オプトアウト時は録画停止、未送信バッファ破棄、visitor/session ID削除
- pc_opt_outをファーストパーティCookieまたはlocalStorageへ保存
4. API¶
Collector API:
- POST /v1/collect
- POST /v1/consent
- POST /v1/opt-out
- GET /v1/sdk/config/{write_key}
Analysis API:
- GET /v1/projects/{project_id}/sessions
- GET /v1/projects/{project_id}/sessions/{session_id}
- GET /v1/projects/{project_id}/sessions/{session_id}/timeline
- POST /v1/projects/{project_id}/analysis/pages
- POST /v1/projects/{project_id}/analysis/events
- POST /v1/projects/{project_id}/analysis/paths
- POST /v1/projects/{project_id}/analysis/funnel
- POST /v1/projects/{project_id}/analysis/dropoffs
- POST /v1/projects/{project_id}/analysis/scroll-depth
Privacy API:
- DELETE /v1/projects/{project_id}/sessions/{session_id}
- DELETE /v1/projects/{project_id}/visitors/{visitor_id}
System API:
- GET /health
- GET /ready
- GET /metrics
5. MCP Server¶
画面なし。MCPを主要操作面とする。
実装ツール:
- pathcollector_search_sessions
- pathcollector_get_session
- pathcollector_get_web_behavior
- pathcollector_get_timeline
- pathcollector_summarize_web_behavior
- pathcollector_get_page_metrics
- pathcollector_analyze_paths
- pathcollector_analyze_funnel
- pathcollector_analyze_dropoffs
- pathcollector_get_errors
- pathcollector_delete_session
- pathcollector_delete_visitor
- pathcollector_get_schema
- pathcollector_health
通常はrrweb生イベントではなく正規化済みuser_actionsを返す。生DOM取得はraw_events:read scope保有時のみ許可する。
6. DB¶
PostgreSQL 16以上。
最低限のテーブル:
- organizations
- projects
- project_domains
- api_credentials
- privacy_rules
- anonymous_visitors
- sessions
- page_views
- event_batches
- raw_events
- user_actions
- action_effects
- privacy_findings
- daily_metrics
- deletion_requests
- audit_logs
主識別単位はsession_id、補助識別単位はvisitor_id。複数端末をまたぐ人物統合は行わない。
7. 操作と画面変化の関連付け¶
ユーザー操作後2秒以内に発生したDOM変更をaction_effect候補として関連付ける。
記録項目:
- added_node_count
- removed_node_count
- changed_attribute_count
- changed_text_count
- navigation_occurred
- validation_error_occurred
- javascript_error_occurred
- affected_selectors
技術スタック¶
- pnpm workspace
- Node.js + TypeScript
- Fastify
- rrweb
- PostgreSQL 16
- Drizzle ORM推奨
- Zod
- MCP TypeScript SDK
- Vitest
- Playwright
- Docker Compose
- Nginx
- JSON構造化ログ
リポジトリ構成¶
pathcollector/
├─ apps/api
├─ apps/mcp
├─ apps/worker
├─ apps/collector-sdk
├─ packages/database
├─ packages/domain
├─ packages/privacy
├─ packages/schemas
├─ packages/logger
├─ packages/config
├─ migrations
├─ tests/integration
├─ tests/e2e
├─ tests/privacy
├─ docs/api
├─ docs/mcp
└─ docker-compose.yml
実装順序¶
Phase 1:
- リポジトリ初期化
- PostgreSQLスキーマとmigration
- プロジェクト/APIキー登録CLI
- Collector SDK
- rrweb取得
- クライアントマスキング
- /v1/collect
- サーバー再マスキング
- raw_events保存
Phase 2:
- visitor/session/page_view生成
- click/tap/scroll/navigation/error正規化
- user_actions保存
- action_effects生成
- TTL削除
Phase 3:
- MCP認証
- セッション検索
- Web行動タイムライン
- ページ指標
- パス/ファネル/離脱分析
- 削除ツール
Phase 4:
- E2E
- PII漏えいテスト
- レート制限
- metrics
- バックアップ・復元手順
- API/MCPドキュメント
必須受入条件¶
- Docker Composeで起動できる
- /health と /ready が正常
- テストサイトへ1タグで設置可能
- Windows Chrome、macOS Safari、iPhone Safari、iPhone Chrome、Android Chromeで計測可能
- タップが重複せず1操作として保存される
- DOMスナップショット・DOM変更・スクロール・遷移・エラーを記録できる
- SPA遷移を記録できる
- 入力値を保存せず、入力行為とフィールド種別のみ記録できる
- email、電話番号、氏名、password、tokenが保存前にマスクされる
- data-pc-ignore配下が保存されない
- 同意拒否・オプトアウト時に送信しない
- MCPからWeb行動を時系列取得できる
- MCPから「入口、閲覧順、スクロール、操作、DOM変化、離脱点、エラー直前行動」を取得できる
- 権限外project_idを参照できない
- TTL削除が稼働する
- 削除操作が監査ログへ記録される
- READMEに導入、起動、設定、テスト、MCP接続方法を記載
完了定義¶
対象サイトへJavaScriptタグを設置することで、PC・iPhone・Android・タブレット上のWeb行動を取得し、個人情報をマスクした状態で時系列保存し、MCP経由で検索・取得・解析できること。
開発上の制約¶
- 管理画面は作らない
- セッションリプレイUIは作らない
- ヒートマップUIは作らない
- 生PIIを一切永続化しない
- 問題発生時は原因、影響、暫定対応
Redmine Admin さんが18日前に更新
- ステータス を 新規 から 実行中 に変更
Claude Code(Sonnet)ワーカー agent-pool-1 に実装を委譲し、Phase 1を開始しました。ロック: pathcollector-implementation。初回成果は、リポジトリ初期化、DB migration、health/ready、最小Collector SDK、POST /v1/collect、マスキングテスト、Docker Compose起動、コミットまでです。
Redmine Admin さんが18日前に更新
【Phase1 状況確認】着手前に既存資産を調査した結果、/root/projects/pathcollector に既存リポジトリを発見(新規作成はしません)。Epic #10236 配下の旧サブタスク(#10237 PC1-01 DBスキーマ, #10238 PC1-02 プロジェクト/APIキーCLI, PC1-03 Collector SDK, PC1-04 rrweb統合, PC1-06 /v1/collect)で既に相当量が実装済みでした。
内訳:
- master: リポジトリ骨格、DBスキーマ+migration(Drizzle, packages/database)、プロジェクト/APIキー登録CLI(apps/api/src/cli.ts) — マージ済み
- ブランチ pc-track-b-sdk (worktree, 未マージ): Collector SDK基本実装+rrweb統合+クライアント側マスキング(apps/collector-sdk)
- ブランチ pc-track-a-backend (worktree, 未マージ): POST /v1/collect実装(write_key検証・Origin検証・レート制限・gzip・HMAC・Zod検証・raw保存)
未実装/未存在: /health, /ready エンドポイント、docker-compose.yml(root)、apps/api用Dockerfile、packages/privacy(サーバー側再マスキングパッケージ、現状はcollector-sdk内のクライアント側マスキングのみ)。
競合サービス確認: 同VPS上に track-gufu (Redmine #9882, track.gufu.jp) という類似のFastify+PostgreSQLトラッキング基盤が別プロジェクトとして稼働中ですが、PathCollectorとは別プロジェクト・別ドメイン・別DBであり競合や統合の必要はないと判断しました(参考としてDocker構成パターンのみ踏襲)。
方針: 新規リポジトリは作らず、既存2ブランチをmasterへマージした上で不足分(/health, /ready, docker-compose, サーバー再マスキング接続)を実装し、Phase1初回成果として仕上げます。
Redmine Admin さんが18日前に更新
【Phase1 初回成果 完了】/root/projects/pathcollector master へコミット済み(HEAD: e70d5c5)。
実施内容:
- 既存の未マージブランチ pc-track-b-sdk(Collector SDK基本実装+rrweb統合)と pc-track-a-backend(POST /v1/collect)を master へマージ(競合なし)
- apps/api/src/routes/health.ts を新規追加: GET /health(liveness、DB非依存)と GET /ready(readiness、SELECT 1でDB到達性確認、失敗時503)
- docker-compose.yml + apps/api/Dockerfile + docker/api-entrypoint.sh を新規追加(PostgreSQL+APIの最小構成、起動時にmigration自動適用)
- docs/adr/0001-0005 を追加(曖昧点の安全側決定を記録)
テスト結果:
- apps/collector-sdk: vitest 72 tests pass (masking.test.ts 16件含むマスキング単体テスト)
- apps/api: typecheck pass、test:unit 74 tests pass、test:integration(ephemeral Docker Postgres) 33 tests pass(新規 health.test.ts 3件含む)
- docker compose up -d --build で db healthyヺapi起動を確認。curlで /health ・ /ready が実際に200を返すことを確認
- コンテナ内CLIでregister-projectを実行しwrite_keyを発行、そのwrite_keyでHMAC署名付きPOST /v1/collectを実行し202を確認、event_batchesに1件保存されることをDB直接確認
- docker compose restart api 後もmigration再適用がエラーなく完了(冪等性確認)。検証後 docker compose down で後片付け済み
主な設計判断(詳細はdocs/adr):
- ADR-0001: 新規リポジトリは作らず既存を継続利用
- ADR-0002: 未マージブランチをマージ(再実装すると手戻りのため)
- ADR-0003: Dockerの既定アドレスプールが枚枚の既存サービスで枯渇していたため 10.77.0.0/24 を明示指定。DBはホスト非公開、APIは127.0.0.1:3210のみ(ポート・サブネット競合確認済み)。公開ドメイン未指定のためproxy-network未参加
- ADR-0004: /healthはliveness(DB不問)、/readyはreadiness(DB確認)で明確に分離
- ADR-0005: packages/databaseがworkspace未使用+TS直接参照のため、Docker内もtsx直接実行を踏襲(ビルド手順を増やさない安全側選択)
競合サービス: 同VPS上のtrack-gufu(#9882)は別ドメイン・別DBの別プロジェクトであり競合なしと判断(ADR-0001参照)。
残作業(Phase1完了に向けて): チケット本文Phase1項目8「サーバー再マスキング」はapps/api側に未実装(現状はクライアントマスキング済みのペイロードをそのままevent_batchesに保存)。項目9「raw_events保存」はバッチ単位(event_batches)までで、raw_eventsテーブルへのイベント単位展開は未着手(既存実装のコミットメッセージでPC1-08相当として明記済み)。障害はなし。次ステップとしてこの2項目を実装しPhase1を完了させたい。
なお、旧worktree(/root/.worktrees/pc-track-a-backend, pc-track-b-sdk)はマージ済みのため不要ですが、他作業との競合を避けるため今回は削除せず残してあります。