PAssist ドキュメント

このPCの「1つのウィンドウだけ」を、ブラウザだけ・アカウント不要で、離れた相手に一時的にリモート操作してもらうツール。デスクトップ全体は決して映りません。

📘1. 概要

PAssist は Windows 向けのリモート操作ツールです。共有するのは選んだ 1 つのウィンドウだけ。操作する相手はブラウザを開くだけ(インストール・アカウント不要)。映像と操作は WebRTC で暗号化されて流れ、デスクトップ全体は構造的に映りません。

ねらい:① 開いている任意のウィンドウを1つ選ぶ → ② 共有URL/QRを発行 → ③ 相手がブラウザだけで一時操作 → ④ デスクトップ全体は絶対に見せない → ⑤ アカウント作成なしで入れる。

2. できること

要件実装
任意のウィンドウを1つだけ選ぶ開いているウィンドウ一覧から選択。キャプチャ元は window のみ列挙し screen は一切扱わない。
URL/QRを発行選択すると共有URL(…/s/<token>)を自動発行。スマホ用にQRコードも表示。
ブラウザだけで一時操作WebRTC で映像、DataChannel でマウス/キー操作。相手はブラウザのみ。
複数人で同時に同時接続を設定可能。操作できるのは常に1人(最初の接続者)、ほかは閲覧。全員「閲覧のみ」にも切替可。
デスクトップ全体は出さない選んだ1ウィンドウの映像のみ。構造的に画面全体を渡せない設計。
アカウント不要URLのトークンだけで接続。ログインなし。
一時利用セッションに有効期限(既定30分、変更可・無期限も可)。期限切れで自動失効。
検証できるサーバが「公開コードと同一」であることをブラウザが自動で暗号学的に検証(詳細)。

🧩3. アーキテクチャ

  ┌──────────────────────── ホストPC (PAssist) ────────────────────────┐
  │  Electron アプリ(タスクトレイ常駐)                                 │
  │   ├─ ウィンドウ選択 / 管理UI(renderer)                            │
  │   ├─ シグナリング/配信サーバ(Node: express + ws)※内部起動         │
  │   └─ 入力注入(nut.js:座標クランプ+危険キー遮断)                  │
  └───────────┬───────────────────────────────────────┬───────────────┘
              │ WebRTC 映像(DTLS-SRTP)                  │ シグナリング(WSS)
              │ + DataChannel 操作                      │ + ページ配信
              ▼                                         ▼
        ┌──────────────┐                         (UPnP / Cloudflare トンネル)
        │  操作する人    │  ブラウザでURLを開くだけ
        │ ブラウザのみ   │  iframe 埋め込みも可
        └──────────────┘
          
  • ホストhost/・Electron):ウィンドウのキャプチャ(getDisplayMediasetDisplayMediaRequestHandler)、WebRTC の offer、入力注入。シグナリングサーバを内部で起動。
  • サーバserver/・Node express + ws):セッション/トークン管理、SDP/ICE 中継、ビューア配信、TURN クレデンシャル発行。ホスト内蔵のほか、独立サーバとしても運用可(Docker image を配布)。
  • ビューアserver/public/viewer.html):ブラウザの素のページ(WebRTC の answer 側)。映像表示・入力送信・サーバ検証。
  • 暗号化:映像・操作は WebRTC(DTLS-SRTP)で常に暗号化。シグナリング/ページは https/wss 化可能。

🚀4. 使い方

ホスト(共有する人)

  1. PAssist.exe をダブルクリック(開発時は npm start
  2. 共有したいウィンドウをクリックして選択
  3. 表示された共有URLをコピー、またはQRコードを相手に見せる
  4. 接続リクエストが来たら「許可」(「この端末を信頼」で次回から自動承認)

ゲスト(操作する人)

  1. 送られたURLをブラウザで開く(スマホはQRを読み取るだけ)
  2. 「クリックして操作を開始」→ マウス/キーボードで対象ウィンドウを操作。終了はブラウザを閉じるだけ。

タスクトレイ常駐

  • ウィンドウの「×」は終了せずトレイに格納。トレイアイコンのクリックで再表示。
  • トレイ右クリック →「管理画面を開く」「ログイン時に自動起動」「終了」。
未署名のため、初回は SmartScreen 警告が出ることがあります(「詳細情報 → 実行」)。常用ならコード署名を推奨。

🔑5. アクセス制御と信頼

「誰が繋げるか」をセッションごと(または既定値 ACCESS_MODE)で選べます。

モード挙動強さ
approve(既定)ホストが「許可」するまで接続不可。映像も流れない。最も安全
pinセッションごとに発行される6桁ワンタイムPINが一致すれば接続(ホスト承認なし)。
invite招待リンク(信頼クレデンシャル付きURL)を持つ相手のみ。リンクが無いと接続不可。中〜高
tokenURLのトークンだけで即接続(承認なし)。手軽だが最も緩い。緩い

信頼済み端末(事前承認)

初回に「この端末を信頼」、または「招待リンク」を渡すと、その端末は次回から自動承認されます。仕組みは端末ごとの clientId + secretホストは secret のハッシュ(SHA-256)だけを保存し(userData/passist-trust.json)、平文はビューアの localStorage のみに置かれます。招待リンクは …/s/<token>#k=<clientId>.<secret> の形。

同時接続・閲覧のみ・有効期限

  • 同時接続:人数を設定可能。操作できるのは常に1人(最初の接続者)、ほかは自動的に閲覧のみ。
  • 閲覧のみ:全員を操作不可(画面共有だけ)に切替可能。
  • 有効期限:既定30分。30分/1時間/3時間/6時間/無期限から選択。期限切れで全接続を自動切断。

🌍6. インターネット公開

方法A:UPnP 自動公開(公開IP+UPnP有効が前提)

起動時にアプリが Webポート(TCP 8443)を UPnP で自動開放し、共有URLを http://<公開IP>:8443/s/… に自動設定します。

npm start
# ログに「[host] インターネット公開: http://<公開IP>:8443 …」が出れば成功
# 同一LAN内のみにしたい場合: $env:PASSIST_PUBLIC="0"; npm start

方法B:Cloudflare トンネル(https・ポート開け不要・推奨)

独自ドメイン(例 passist.example.com)で正規の https URL になります。Cloudflare 側の公開ホスト名は タイプ=HTTP / URL=localhost:8443 / パス=空

$env:PASSIST_PUBLIC="0"; $env:PUBLIC_BASE_URL="https://passist.example.com"; npm start
# 共有URL = https://passist.example.com/s/…(ブラウザで開くだけ)
映像/操作は WebRTC の P2P(STUN)で流れ、トンネルは通りません。厳しい NAT の相手とは TURN サーバが必要になることがあります(→ セルフホスト・運用)。

🛡️7. セキュリティ

観点対策
1ウィンドウ限定キャプチャ元に screen を列挙せず、選択IDに固定。デスクトップ・他ウィンドウは映らない。
一時利用セッションに有効期限(既定30分)。期限切れで自動失効。
勝手に繋がせない既定でホスト承認制。許可するまで映像は流れない。PIN/招待リンクのモードも有り。
事前承認(信頼端末)端末ごとのクレデンシャル。ホストはハッシュ(SHA-256)のみ保存。
操作範囲の限定入力座標を対象ウィンドウの矩形にクランプ。窓外には出さない。
デスクトップへの脱出防止Win / Alt+Tab / Alt+F4 / Ctrl+Esc / タスクマネージャ 等のキーをブロック。
暗号化映像・操作は常に DTLS-SRTPAES-128-GCM / ECDHE)。鍵はホストと相手だけが持ち、サーバ・TURN は中身を見られない。
サーバの真正性稼働中サーバが公開コードと同一であることを暗号学的に検証可能(次節)。

🔐8. 検証・署名

signaling サーバの Docker image は、GitHub Actions で Reproducible Build(再現可能ビルド)され、Cosign keyless 署名(GitHub OIDC → Sigstore Fulcio の短期証明書 → Rekor 透明性ログ)と SLSA provenance + SBOM が付きます。秘密鍵は運営者が保持しません。

ビューア/ホストの「PAssist について」を開くと、ブラウザ自身がその場で Rekor の Merkle 証明・署名・イメージ digest・証明書の発行元を検証し、 を表示します(専門知識・追加インストール不要)。CLI でフル検証する場合:

cosign verify ghcr.io/paps-jp/passist-signaling@<digest> \
  --certificate-identity-regexp 'https://github.com/paps-jp/passist' \
  --certificate-oidc-issuer 'https://token.actions.githubusercontent.com'
仕組みと「なぜ数学的に安全か」の初心者向け解説は 🔐 検証に基づく安全性 を参照してください。

⚙️9. 設定(環境変数)

ホスト側

変数既定説明
PASSIST_PUBLIC有効0 で UPnP 公開を無効(同一LAN内のみ)
PUBLIC_BASE_URL共有URLのベース(トンネル使用時に指定)
REMOTE_SERVER_URL外部シグナリングを使う場合の接続先(内部起動を無効化)

サーバ側

変数既定説明
PORT8443サーバの待受ポート
ACCESS_MODEapprove既定アクセスモード:approve / pin / invite / token
SESSION_TTL_MS1800000セッション有効期限(ms)。0 で無期限
TLS_CERT / TLS_KEY指定すると https/wss で待受
ICE_STUN_URLGoogle STUN配布する STUN サーバ
ICE_TURN_URL / ICE_TURN_USER / ICE_TURN_PASS静的(固定認証)TURN
SIGN_TURN_URLS / TURN_AUTH_SECRET / SIGN_TURN_USERNAME / SIGN_TURN_TTL_SEC短期認証TURN(HMAC で期限付きクレデンシャルを発行・自動失効)
RELAY_BUDGET_BPS / RELAY_MIN_BPS / RELAY_MAX_BPSTURN 中継時の送信ビットレート上限(コスト管理)

GIT_COMMIT / IMAGE_DIGEST / BUILD_TIMESTAMP / GIT_TAG はビルド時に自動で注入されるビルド情報で、/api/build から参照できます(手動設定不要)。

🐳10. セルフホスト・運用

signaling サーバは独立運用できます。deploy/docker-compose.ymlCaddy(自動TLS)+ signaling(GHCR から pull)+ coturn(TURN・同居)を立ち上げます。

# VPS 初期化(UFW・Docker 導入・.env 雛形生成)
bash deploy/bootstrap.sh

# .env に最低限を設定
#   DOMAIN=passist.example.com
#   ICE_TURN_* / TURN 認証 など

docker compose -f deploy/docker-compose.yml up -d
# 更新: docker compose pull && docker compose up -d
  • image の真正性は pull 前後に cosign verify で確認できます(→ 検証・署名)。
  • TURN だけ別ホストに分離したい場合は deploy/coturn-only/ を使用。
  • 統計:稼働サーバは /api/stats(CORS 許可)で匿名の利用統計を公開。ダッシュボードは stats.html

🔨11. ビルドと配布

ホスト(PAssist.exe)

npm install                 # host/server の依存を一括導入
npm start                   # 開発起動(Electron)
npm test                    # 信頼ロジックの単体テスト
npm --prefix host run dist  # 配布用 PAssist.exe(portable)を生成
  • 成果物:dist\PAssist.exe(単一実行ファイル・portable)。サーバ一式は resources\server に同梱、ネイティブ(nut.js)は asar から展開。
  • 入力注入が「閲覧のみ」になる場合は npm run rebuild(nut.js を Electron 用に再ビルド)。

signaling サーバ(自動・署名付き)

v* タグを push すると release-signaling.yml が起動し、Reproducible Build → GHCR へ push → SLSA provenance+SBOM 添付 → Cosign keyless 署名(Rekor 記録)まで自動実行します。

git tag vX.Y.Z && git push origin vX.Y.Z   # あとは CI が全部やる

🖼️12. iframe 埋め込み

ビューアは普通の Web ページなので iframe に埋め込めます。

<iframe
  src="https://passist.example.com/s/<token>"
  allow="fullscreen; autoplay"
  style="width:100%;height:600px;border:0"
  title="PAssist"></iframe>
  • 親ページは https 推奨。allow="fullscreen" は全画面ボタン用。
  • 埋め込み元を限定するなら CSP frame-ancestors を返す。
  • sandbox を付けるなら allow-scripts allow-same-origin allow-fullscreen が必要。

📌13. 制限と今後

現在の制限

  • ホストは Windows のみ(nut.js / Windows Graphics Capture)。
  • 厳しい NAT の相手は TURN が必要な場合あり。
  • exe は未署名(SmartScreen 警告)。
  • 操作は OS レベルの実入力(ホストの実カーソルが動く)。

今後の候補

  • exe の SHA256SUMS を cosign sign-blob で署名し release に添付。
  • コード署名(EV / Azure Trusted Signing 等)で SmartScreen 通過。
  • 送信ビットレートの自動調整・自動更新。

📄14. ライセンス

PAssist は MIT License で公開しています。OSI 認定の最もシンプルな許諾型 OSS ライセンスで、商用利用を含むほぼすべての利用が自由です。

使う・自社運用する・改変する・監査する・再現ビルドで検証する・再配布する——いずれも自由(商用・派生物への独自ライセンス付与も含む)。必須の条件は再配布時に著作権表示と MIT ライセンス全文を含めることのみです。詳細は LICENSE または 検証に基づく安全性 のライセンス節を参照。