13/Documentation
セキュリティ: 実行前にお読みください
Collieが公開するエンドポイント、保護機能、端末のペアリング、各リクエストで使用する認証情報
Collieは設計上、マシンへのリモートシェルアクセスを提供します。 CollieのAPIを1回呼び出すだけで、実行中のターミナルペインに任意のキーストロークを直接送信できます。ペアリング済みのデバイスは、すべてのペイン(ソースコード、シークレット、環境変数、エージェントの出力)の読み取りや、実行ユーザー権限でのコマンド実行が可能です。1.18.0以降は読み取りも含めてすべてのAPIリクエストにペアリング済みデバイスのトークンが必要になったため、保護の要はペアリングそのものにあります。URLにアクセスでき、有効なペアリングコードを持つユーザーには、これらと同じアクセス権が付与されます。1.18.0より前は、URLにアクセスできるユーザー全員がすべてのペインを読み取れる状態でした。
サンドボックスやコマンドの許可リストはありません。コマンドを制限するとツールの目的を果たせなくなるためです。ペアリング済みのデバイスはrootログインと同等、ペアリングコードはパスワードと同等のものとして扱ってください。紛失したデバイスはcollie devices revoke <label>ですぐに無効化してください。ペアリングによってリスクは下がりますが、完全になくなるわけではありません。URLはパブリックなインターネット上には公開せず、tailnetなどのプライベートネットワーク内に留めてください。
デバイスのペアリング: 書き込み認証情報
1.18.0以降、これは書き込みだけでなく、読み取りを含むすべてのリクエストに必要な認証情報となります。
# on the host — prints an 8-character code and a QR code, good for 10 minutes
bin/collie pairペアリングは常時有効であり、書き込みだけでなく読み取りにもペアリングトークンが必要です。端末がペアリングされるまで、Collieはすべてのスマートフォンおよびブラウザに403 device not pairedを返します。スマートフォンでCollieを開く前に、まずホスト上でcollie pairを実行してください。
スマートフォンでCollieを開き、設定 → ペアリング済みデバイスに移動して、デバイスのラベルとともにコードを入力します。または、コマンドによって表示されたQRコードをスキャンして、コードが入力された状態でその画面を直接開きます。スマートフォンは返されたトークンを保存します。Collieはハッシュのみを保持し、トークンは一度だけ表示されます。プロセスを再起動する必要はありません。実行中のデーモンは、次のリクエスト時にペアリングと失効を適用します。
2つのデバイス制限は異なる目的を持ち、どちらか一方、両方、またはいずれも使用しない構成が可能です:
| 確認内容 | 信頼対象 | 失効方法 | |
|---|---|---|---|
COLLIE_DEVICE_HEADER | このデバイスは管理者のリストに存在するか? | サニタイズされた名前を挿入するプロキシ | COLLIE_DEVICE_ALLOWLIST を編集して再起動 |
| ペアリング | このデバイスは自身が発行した認証情報を保持しているか? | ネットワーク上の要素はなし | collie devices revoke <label> (即時反映) |
ペアリングに追加のインフラは不要です。ヘッダーを挿入するプロキシが存在しない直接の tailscale serve 構成に適しています。
ペアリングは、読み取りを含むすべての/apiルートを制限します。開放されたままのルートは2つのみです。監視ツールやロードバランサーが問い合わせる/api/healthと、デバイスがトークンを保持する前に必要とする/api/pairです。/api/healthはビルド情報、クルーモード、および過去7日間の3つのカウント(入力ボックスが空と認識された回数、読み取り不能だった回数、Collieが読み取れなかった画面を無視して手動送信された回数)を返します。カウントにはペイン名、テキスト内容、人物名は含まれません。
デバイスヘッダーは、書き込みと2つの読み取り(ワークスペースのフォルダ配下のすべてのファイルを表示できるファイルビュー、およびホーム(ADR 0086)配下のフォルダ名を一覧表示するNewページのBrowse)を制御します。
bin/collie devices list # what holds a credential, and when each was last seen
bin/collie devices revoke old-phone # effective immediately, no restart新規インストール直後はペアリングされたデバイスが存在しないため、collie pair を実行するまで Collie はどのスマートフォンにも応答しません。最後のデバイスを失効させても Collie が再びアクセス可能になるわけではなく、すべてのデバイスに device not paired が返されます。復旧するには、ホスト上で collie pair を実行してスマートフォンを再度ペアリングしてください。デバイスがペアリングされていない間は collie doctor が警告を表示します。
注。 crew リンクとスタンバイ用 door は独自の認証情報を保持します。lead は固定された相互 TLS と crew secret を介して member に読み取りを転送するため、member 自身にはペアリングされたデバイスは不要です (crew)。
コードの試行に5回失敗するとそのコードは無効になり、collie pair を再実行する必要があります。さらに、bridge は同一送信元アドレスからのペアリング試行を毎分10回までに制限し、超過時には 429 と Retry-After: 60 で拒否します。同一マシン上の front door の背後では、送信元アドレスは front door が追加した最後の X-Forwarded-For エントリになります。それ以外のピアからの同ヘッダーは無視されます。カウンターはメモリ上に保持され、bridge の再起動時にリセットされます。
CLI 自身の読み取り
bridge は起動ごとに、ステートフォルダー内の local-secret に新しいランダムシークレットを書き込みます。collie doctor と collie crew update は、同一マシン上の自身の bridge を読み取るためにこれを送信します。このファイルは所有者のみアクセス可能であるため、自身のアカウントだけが読み取ることができ、正常に停止すると削除されます。bridge はこのシークレットを読み取りにのみ受け入れ、書き込みや Files ビュー、または別マシンからのリクエストには受け入れません。シークレットはループバックからのみ受け付け、X-Forwarded-For などのプロキシヘッダーを含むリクエストでは受け付けません。COLLIE_HOST を tailnet アドレスにバインドしている crew member はループバック上に存在しないため、その場合 bridge はこのマシン自身のアドレスからも受け入れます。socat や ssh -L など、同一マシン上のプレーンな TCP リレーであればシークレットをそのまま転送できます。シークレットの安全性を保つのはファイル自体のパーミッションであるため、ステートフォルダーは必ず所有者のみアクセス可能な状態を維持してください。
デバイスに有効期限を設定する
ペアリングされたデバイスは、失効させるまでトークンを保持し続けます。期間を制限するには、ペアリング時に有効期間を指定します:
bin/collie pair --expires 30d # also 12h, 2w; at most 3650d
bin/collie devices set-expiry pixel 90d # a new lifetime, counted from now
bin/collie devices clear-expiry pixel # no expiry again有効期間は、スマートフォンがコードを取得した瞬間からカウントされます。--expires を指定しない場合、従来どおりトークンは失効せず、既存のトークンも変更されません。有効期限が切れると、bridge は device not paired ではなく device expired でトークンを拒否します。その後、スマートフォン側で ペアリングで保存されたデータを消去 が行われ、設定に 再ペアリング と表示されるため、collie pair を実行して新しいコードを発行してください。collie devices list および設定画面に各デバイスの有効期限が表示されます。期限切れのデバイスは、失効させるか set-expiry で新しい有効期限を設定するまで一覧に残ります。再ペアリングには同じ名前を使用できます。新しいペアリングによって期限切れのエントリが置き換えられて古いトークンが失効し、その失効が監査ログに記録されます。有効期限内のトークンを持つ、デバイスが現在使用中の名前は拒否されます。crew deputy のスタンバイ用 door も期限切れのトークンを拒否します。
複数のインスタンスを実行しているホストでは、コマンドの前に COLLIE_INSTANCE=<name> を付加し、スマートフォンでその特定のインスタンスURLを開いてください (単一ホスト上での複数 Collie インスタンスの実行)。
ペアリング解除時にスマートフォン上で消去されるデータ
ペアリングが終了すると、スマートフォンは Collie が保存していた関連データを削除します。これは設定からスマートフォンのペアリングを解除したときや、bridge が device not paired または device expired でトークンを拒否したときに発生します。
スマートフォンは、トークン、未送信の下書き、保存されたペインのテキストと herd、プッシュ通知の購読情報、「プロンプトなし」の確認状態、およびアプリシェルを除く Collie のキャッシュを削除します。設定は維持されます。テーマ、言語、ピン留め、その他の環境設定にはセッションテキストは保持されません。「設定」では、端末の登録を取り消す前に確認を求めます。
データが消去されるのはスマートフォン上のみです。bridge は Clear-Site-Data ヘッダーを送信しません。このヘッダーを送信すると設定やオフラインアプリまで消去され、共有ホスト名ではそのホスト上の他のアプリにも影響が及ぶためです。
書きかけのファイルなど、bridge がペアリング済みデバイスの一覧を読み取れない場合、代わりに 503 pairing unavailable を返します。その場合、スマートフォンはトークンと保存された全データを保持し、再試行します。
スマートフォンが保持するデータ
スマートフォンは直近に表示したデータの制限された小さなコピーを保持するため、bridge に接続できない状態でもコールドオープン時にそれを表示できます。以下の各項目にはサイズ制限と有効期限が設定されています。
| 項目 | 保存場所 | 保持期間 | ペアリング解除時 |
|---|---|---|---|
| ペアリングトークン | localStorage | ペアリング解除、失効、または期限切れまで | 削除 |
| 設定: テーマ、言語、ピン留め | localStorage | 変更するまで | 保持 |
| 未送信の下書き | localStorage | 48時間 | 削除 |
| プッシュエンドポイント | localStorage | 変更されるまで | 削除 |
| App shell | Cache Storage | 次のビルドまで | 保持 |
| フォントとプッシュ通知のタイトル | Cache Storage | 置換されるまで | 削除 |
| Whistle音声モデルとエンジンファイル(コピーを選択した場合のみ) | Cache Storage | 削除するまで | 削除 |
| 直近に表示されたHerdおよびペインのテキスト | IndexedDB collie-store | 24時間 | 削除 |
| 各ペインの最新のChatターン | IndexedDB collie-store | 1日、または「このスマートフォンにチャットを保持」設定 | 削除 |
末尾の2行はデバイス上のストレージです。bridgeがすでにマスク済みしたテキストのみを保持し、容量はペインあたり最大256 KiB、全体で10 MiBで、最も古いエントリから順に破棄されます。ペインのテキストとChatターンにはペインの256 KiBが半分ずつ割り当てられるため、一方が他方を追い出すことはありません。Chatターンは生のターミナル画面ではなく、Chatが表示した状態のまま保持されます。「このスマートフォンにチャットを保持」をオフに設定すると何も保持されず、保持されていたものは削除されます。ペインがパスワードを要求すると、スマートフォンはそのペインのエントリを破棄します。ストレージ内のデータからキー入力や応答を送信することはできません。ペアリング解除時の削除対象はペアリング解除時にスマートフォン上で消去されるデータ内のものです。
スマートフォンのアプリスイッチャーにもペインが表示されることがあります。Collieを離れる際、スマートフォンは最近使ったアプリの一覧用に画面のキャプチャを保持するため、その画像にペインのテキストが写る場合があります。Webアプリ側でこれを防ぐことはできません。Androidではページがバックグラウンドに移行したことを検知する前にキャプチャが撮影され(Pixelで検証、2026-10-08)、iOSにも保証はありません。これが問題になる場合は、スマートフォンを他人に渡す前に端末をロックするかCollieを閉じてください。
リスクモデル
主なセキュリティ境界とリスク:
- 実行ユーザーの権限で動作します。 Collieは、
~/.ssh、git push --force、rm -rf、sudoを含む実行ユーザーの全アクセス権限を継承します。 - ローカルの全システムユーザーがポートにアクセスできます。 標準的なターミナルマルチプレクサのソケット(
tmux、zellij、herdr)は、ファイルシステム権限を使用して他のローカルユーザーからのアクセスを制限します。CollieはローカルTCPポートをリッスンするため、すべてのローカルUIDに公開されます。読み取りにはペアリングトークンが必要なため、トークンを持たない他のローカルユーザーには/api/healthしか読み取れません。トークンがセキュリティ境界となります。ブラウザプロファイルを読み取れるローカルユーザーは、トークンも読み取ることができます(ARCHITECTURE.md §6)。 - 保持された権限はユーザーのタップを待機します。
collie hooks install claude --hold-permissionsを使用すると、Claudeの権限hookはソケットファイルを介してbridgeに問い合わせます。これを開くことができるのは自身のユーザーのみです(0700フォルダ内のモード0600)。hookは接続前にソケットの所有者、フォルダ、およびその上位のstateディレクトリを検証します。bridgeは自身がペインに配置したプロセスに対してのみ応答します。単独で応答することはありません。タップ操作にはカードに表示された内容のハッシュが含まれている必要があります。画面カードは、ダイアログが画面上に存在しないリクエストには応答しません(スマートフォン向けに保留中の権限)。 - Filesビューはディスクからファイルを読み取ります。
.gitフォルダ、Collie独自の状態・設定フォルダ、Collieの状態シークレットのような名前のファイルを除き、ワークスペースのフォルダ配下にある任意のファイルを表示します。そのため、書き込みと同様に認証済みデバイスを要求します(ADR 0083)。 - NewページのBrowseには、ホームディレクトリ配下のフォルダ名が一覧表示されます。 名前のみ。Filesビューの制限が適用されます。手入力されない限りドットフォルダは非表示、
.gitは除外、Collieの状態フォルダや設定フォルダは除外、ホーム外へのリンクは除外、最大200行。認証済みデバイスも必要です(お気に入りと最近使用したフォルダー)。 - ペアリングされたすべてのデバイスが、ワークスペースのフォルダ配下のファイルを読み取れます。 これには
.envファイルも含まれます。シェル自体の操作を信頼できるデバイスのみをペアリングしてください。 - ワークスペースのフォルダ配下にある認証情報ファイルは読み取り可能です。
~/.claude、~/.codex、~/.config/gh、~/.sshで開かれたワークスペースには、そこに存在するものが表示されます。ワークスペース配下に設定フォルダが置かれた2つ目のCollieの.envでも同様です。フォルダ外のファイルへのフォルダ内ハードリンクも検出されません。 - ペアリングされた端末はランチャーを追加できます。 デフォルトの
[phone] adds = trueでは、スマートフォンからレシピ(エージェントと、ブリッジが独自のテーブルからフラグを選択するオプションチップの組み合わせ)で構成された行を追加できます。手入力の行にはfree_text = trueが必要で、これはデフォルトで無効です。レシピでも権限プロンプトをスキップできます。該当する行には「プロンプトなし」バッジが付き、各端末で1回確認します。端末の登録を取り消すと、その端末が追加した行は削除されます。adds = falseを設定すると、スマートフォンから追加されたすべての行が無効になります(独自のランチャー、ADR 0094)。 - ペアリングされた端末は単発のコマンドを実行できます。 デフォルトの
[phone] run = trueでは、スマートフォンで作成した行をマシンの新しいシェルに入力できます。ペアリング済みのスマートフォンはすでにシェルを開いて入力できるため、権限が追加されるわけではありません。この行にはフリーライン文字ルールが適用され、ペアリングされた端末がない場合、ブリッジはこれを拒否します。起動された各コマンドは、状態フォルダー内のcommands-recent.jsonに最大12個まで保持されます(モード 0600)。シークレットを含むと思われる行(TOKEN=…のような代入、パスワードを含むURL、tokenやpasswordなどの単語)は実行されますが保持されません。この判定は推測に基づくため、リストにシークレットが混入する可能性があります。監査ログには行の最初の単語とその長さのみが記録されます。端末のペアリング解除や登録取り消しを行ってもリストはそのまま残ります。行を再実行するにはペアリング済みの端末が必要になるためです。run = falseを設定すると、このショートカットを削除できます(単発のコマンド、ADR 0095)。 - 各マシンは、Collie経由で送信されたシェル行を保持します。 Sentは、シェルペインに送信した行と各単発コマンドを、状態フォルダ内の
sent.jsonl(モード0600)に保持します。ペインを読み取れるのと同様に、ペアリングされたデバイスもこれを読み取れます。一部の機密情報は保護されたままです(Sent: 送信したシェル行)。 - 単一のインスタンスがすべてのHerdrセッションを公開します。 デフォルトでは、単一のCollieプロセスが、Herdrの設定ルート以下で検出されたすべてのHerdrセッション(サンドボックスセッションを含む)のフロントエンドとして機能します(マルチセッション)。
- 書き込みは
<state-dir>/audit.logに記録されます(COLLIE_STATE_DIRで変更しない限り~/.local/state/collie/audit.log)。サーバーはすべてのキーストローク入力、応答、ファイルアップロード、ペインおよびタブのライフサイクルイベントをログに記録します。監査ログはアクセス制御ではなく事後確認のためのものである点に注意してください。Typeモードで送信された文字はログに記録されません。COLLIE_AUDIT_CONTENT=noneが各文字を伏字化し、デフォルトのプレビューには•マークのカウントのみが記録されます。EnterやCtrl+Cなどのキー名は判読可能なまま残ります(ARCHITECTURE.md §6)。durable agentの応答、送信、停止、ボタンのタップ、起動、再オープン、名前変更も記録されます(ログに記録される内容)。 - レスポンスヘッダー。 すべてのレスポンスに
X-Content-Type-Options: nosniff、Referrer-Policy: no-referrer、Permissions-Policy: camera=(), geolocation=(), microphone=(self), payment=(), usb=()が付加されます。ハンズフリー音声設定のため、マイクの使用は許可されたままになります。App shellのContent-Security-Policyにはobject-src 'none'、form-action 'self'、frame-ancestors 'none'が含まれます。Strict-Transport-Securityは、TLSリスナーまたはプロキシからのX-Forwarded-Proto: https経由でリクエストがHTTPSで届いた場合にのみ送信されます。Collieは親ドメインを他のサービスと共有することが多いため、応答したホスト名のみを対象とし、サブドメインは対象外とします。tailscale serveの背後にあるbridge自体はプレーンHTTPで通信し、ブラウザはその環境ではヘッダーを無視します。/api/blobs/から配信される画像はセッションコンテンツであるためCache-Control: private, no-storeが付加され、スマートフォン自体のキャッシュを含め、いかなるキャッシュにも保持されません。 - デフォルトの防御制御。 Collie はループバックインターフェースにのみバインドし、トラフィックを
tailscale serveまたは同等のリバースプロキシ経由でのみルーティングし、厳格な CSP ルール、同一生成元チェック、ホストヘッダー検証を適用します。ペインの出力はinnerHTMLではなく React テキストノードとしてレンダリングされます。tailscale funnelを使用したり、生のポートを公開したりしないでください。特定のハードウェアを承認するには、ペアリング を直接使用するか、プロキシがデバイス ID を挿入する場合は後述の2つのCOLLIE_DEVICE_*変数を使用します。
| 変数 | 機能 |
|---|---|
COLLIE_ALLOW_NON_LOOPBACK_BIND=1 | ループバック限定バインドを無効化します。未設定の場合、ブリッジは 0.0.0.0 へのバインドを拒否します。 |
COLLIE_ALLOW_ANY_HOST=1 | ホストヘッダー検証を無効化します。この検証はデフォルトで有効になっており、不一致時にアクセスを遮断します。 |
COLLIE_TRUSTED_USER | Tailscale-User-Login ヘッダーが存在しないか一致しないリクエストを拒否します。 |
COLLIE_TRUSTED_USER_OPTIONAL=1 | Tailscale-User-Login ヘッダーがないリクエストを許可します(タグ付きノードはこのヘッダーを送信しません)。 |
COLLIE_ACCESS_TEAM + COLLIE_ACCESS_AUD | Cloudflare Tunnelのみ。Cf-Access-Jwt-AssertionがこのAccessアプリに対して検証されない限り、すべてのリモートリクエストを拒否します。ループバック上のローカルプロセス、/api/health、crewリンクのみがこれをスキップし、ペアリングは引き続き適用されます。tailscale serveなどの他のエントリポイントも拒否されます(Cloudflare Tunnel)。 |
COLLIE_DEVICE_HEADER | プロキシがデバイス ID を挿入する際に使用するヘッダー名。 |
COLLIE_DEVICE_ALLOWLIST | 書き込みを許可するデバイス ID のカンマ区切りリスト。それ以外のデバイスはすべて読み取り専用のままになります(docs/deployment.md)。 |
COLLIE_REDACT=off | ペインのテキスト、diff、ファイルのテキストにおけるシークレットのマスク処理を無効にします(以下)。デフォルトで有効です。 |
COLLIE_SENT=off | 送信したシェル行の記録を保持しません(以下)。デフォルトで有効です。 |
🚫 Collie ではtailscale funnelを絶対に使用しないでください。 Funnel はトラフィックをパブリックインターネットにルーティングしますが、tailscale serveはアクセスをプライベートな tailnet に制限します。Funnel 経由で Collie を実行するユースケースはサポートされていません。
Tailscale の ACL および COLLIE_TRUSTED_USER を使用してアクセスをさらに制限します。本ソフトウェアは無保証で現状有姿のまま提供されます。
durable agent
アタッチするdurable agentは自身が実行またはデプロイするコードであり、Collieはそのテキストを信頼できないものとして扱います。プライベートソケットは他のユーザーを遮断しますが、自身と同じユーザーのプロセスは遮断しません。https経由ではトークンのみが制限手段となります。トランスポートごとの信頼ルール、Collieによるdurable agentのテキストの扱い、監査ログが保持する内容についてはdurable agentのセキュリティに記載されています。
音声エンジン(Whistle)
Whistleはサードパーティのコード(Cactus Compute製のWebAssemblyエンジン)を実行します。これはスマートフォン上、またはホスト上でbridgeの実行ユーザーとして動作します。Collieはそのコードがアクセスできる範囲を制限しますが、アクセス権を完全になくすことはできません。
- ベンダリングおよび固定済み。 エンジンは
needle.jsとneedle.wasmの2つのファイルで構成され、アップストリームのライセンスと出所に関する注記(web/public/stt/needle-3-93e88ede/)とともに、アップストリームが公開した状態のままバイト単位で保持されます。テストによって両ファイルのsha256と、wasmがホストに要求する可能性のある16個の関数リストが固定(ピン留め)されます。新たなインポートが検出された場合は処理を停止します。ピンの更新は人間のみが行い、事前に必ず差分を確認します。モデルにも固定されたハッシュ値があり、Collieはそれが一致した場合にのみダウンロードしたコピーを保持します。 - スマートフォン上では、独自のポリシーを持つワーカー。 wasmは専用のワーカー内で実行されます。bridgeはこのポリシーを設定して
stt/以下のすべてのスクリプトを配信し、他のページにはこのポリシーを適用しません:Content-Security-Policy: default-src 'none'; script-src 'self' 'wasm-unsafe-eval'; base-uri 'none'ワーカーにはネットワーク、DOM、外部からコードを読み込む手段がありません。
localStorageに存在するペアリングトークンを読み取ることはできず、ワーカーにはlocalStorageもありません。他の処理を行う前に、ごく小さなwasmモジュールをコンパイルします。ワーカーに独自のポリシーを許可しないブラウザはこれを拒否し、その環境ではこのスマートフォンが利用不可として表示されます。 - ページのポリシーは変更されません。 ドキュメントポリシーに
'wasm-unsafe-eval'が付与されることはなく、connect-src 'self'が維持されます。 - ワーカーがアクセス可能な範囲。 ポリシーディレクティブでブロックされないため、このオリジンのIndexedDBとCache Storageの読み書きが可能です。
collie-storeには最近のペインやChatのテキスト(スマートフォンが保持するデータ)が保持されています。該当のテキストはすでにマスキングされていますが、読み取りは可能です。このアクセス範囲は許容されており、ADRにも明記されています。これが問題になり始めた場合、次の対策としてワーカーの周囲をサンドボックス化されたフレームで囲みます。 - ホスト上では、Collie自身のバイナリの子プロセス。 音声入力ごとに短命な子プロセスが1つ生成され、Collieが作成した固定の引数リストを用いて
_whistle-transcribeを実行します。コンパイルを実行する前に、エンジン、wasm、モデルをピンと照合して検証します。タイムアウトは60秒(SIGKILLで強制終了)、出力上限は256 KiB、同時実行数は最大2つに制限されます。bridgeのCOLLIE_*変数は一切引き継がれません。bridgeが設定するのは、自身が解決したパスである状態フォルダーとインストールフォルダーの2つのみです。リクエストから引数リストに渡されるのは録音データのみです。 - ユーザーのタップ操作による2つ目の外部通信。 1つ目は更新チェックです。2つ目はモデルのダウンロードです。固定された1つのHugging Faceアドレスから1つのファイルを取得し、ハッシュが一致した場合にのみ書き込みます。スマートフォンがHugging Faceと通信することはありません。ペアリングトークンを使用し、ホストからモデルをコピーします。各ルートには書き込み権限を持つデバイスが必要で、lead上でのみ実行され、同時ダウンロードは1件に制限され、監査ログが記録されます。
- Settingsからの設定は「セットアップはCLIで行う」という原則の例外です。 認証情報を保存したりコマンド名を指定したりするプロバイダーは、ターミナル内にとどまります。Whistleはそのいずれも行いません。流出した書き込みトークンを使えば、すでにターミナルへの入力やアップデートの開始が可能なため、設定画面でのタップ操作によって新たな権限が与えられることはありません。
- SettingsからCodexへの切り替えは、もう1つの例外です。 書き込み権限を持つデバイスは、ホストをWhistle、Codex、Offのいずれかに切り替えることのみ可能です。Codexには完全な同意(
collie stt setupが出力するものと同じ文言)と同意するのタップが必要であり、その後bridgeがCLI自体のプローブを実行します。流出した書き込みトークンによって、ホストの音声をオペレーターがサインインしているChatGPTアカウントに向けられる可能性があります。ただし、キーやコマンドを追加または置換することはできません。そのトークンがあれば、すでにターミナルにcodexを入力できます。ADR 0097ではこの点を検討しています。また、Settingsからの切り替え時には、ペアリング時に指定されたペアリング済みデバイスの名前と時刻がstt.jsonに書き込まれ、ホストの音声で最後に変更したユーザーを確認できます。トークンやアドレスは書き込まれません。collie stt setupを実行すると、その行は削除されます。 - ペアリングされたデバイスは、ホストの音声設定に指定されている項目を名前でのみ読み取ることができます。 それはコマンド名、モデルファイル名、音声サービスのホスト名であり、パス、引数、キー、URLパスが含まれることはありません。
この設計になっている理由: ADR 0096。セットアップ手順は 音声入力とWeb Push にあります。
トランシーバー
Walky talkyはエージェントセッション間でメッセージを伝送します。そのため、このセクションではメッセージが何を証明し、何を証明しないかを説明します。コマンドはトランシーバーにあります。
# in .env, then run `collie restart`
COLLIE_WALKY=offこの行を設定すると、このマシンはそのペイン宛てのすべてのメッセージを拒否します。マシン上のCLIからは引き続き送信できます。
- crewキーはマシン間でメッセージに署名します。 メンバーはメッセージにcrewキーで署名します。leadはそのメンバーに対して自身がピン留めしたキーと署名を照合し、受信側マシンのために独自の署名を追加します。単独インストールにはcrewキーがありません。署名も行わず、マシンの外に送信することもありません。
- 受信側は、leadの保証を通じて送信者を検証します。 メンバーは自身のleadのみをピン留めします。そのため、leadはメッセージとともに送信者の証明書を送信し、この証明書がそのcrewメンバーのものであるという短いステートメントに署名します。受信側マシンはそのステートメントを検証し、次に証明書と照合して送信者の署名を検証します。いずれかの検証が失敗した場合、メッセージを拒否します。クルーの構成員については、引き続きleadを信頼します。
- 結果を書き戻せるのはleadのみです。 leadはメンバー自身のメッセージの結果をそのメンバーに送信します。メンバーは、ピン留めされたleadからのみ、自身が送信してログに記録しているメッセージに対してのみ結果を受け取ります。結果とleadが受け取った記録はメモリ内に保持され、10分後に破棄されます。
- スマートフォンのカード表示にはログが必要です。 スマートフォンがメッセージカードを描画するのは、ブリッジ自身のログにそのメッセージIDが存在する場合のみです。エージェントが画面に出力しただけのヘッダーはプレーンテキストのままであり、「このマシン上にログレコードがありません」というマークが付きます。
- ペインのトークンは機密情報ではありません。
#k3f9などのアドレス内のトークンは、閉じたペインのIDを引き継いだ新しいセッションにメッセージが届かないようにします。collie walky whoはそれをすべてのエージェントに出力し、<stateDir>/walky/instances.jsonは実行ユーザーが読み取れます。これは送信者が誰であるかを証明するものではありません。 - メッセージが通過したすべてのマシンがそのメッセージを保持します。 送信側マシン、lead、受信側マシンはそれぞれ
<stateDir>/walky/にメッセージを記録します。ログは7日間、最大2,000行保持されます。メッセージに機密情報を含めないでください。 - エージェントのメッセージは、ピアとしての発言です。 ペインは送信者のヘッダーのみを含む1行を受け取り、本文は含みません。エージェントは本文を
collie walky inbox <id>の出力として読み取ります。これはハーネスによって命令ではなくデータとして扱われます。インストールされたスキルは、エージェントに対し、内容を慎重に判断し、ピアからの要求であっても破壊的なコマンドを実行しないよう指示します。
注。 エージェントは実行ユーザーと同じ権限で動作するため、crew keyの読み取りやローカルソケットのオープンが可能です。メッセージへの署名や、自身のものではないペインの専有も行えます。署名はマシン間の送信経路でメッセージを保護し、どのマシンから送信されたかを宛先に示します。同一マシン上で身元を偽るプログラムからは保護されません。
Walky talkyによる外部への発信呼び出しは一切ありません。別マシンへのメッセージは既存のcrew link上を経由し、AIモデル、クラウドサービス、サードパーティに渡ることはありません。
マシンから送信されるデータはマスクされます
Collieはペインのテキストとファイル内容に含まれる既知の形式のシークレットを、スマートフォンに届く前にマスクします。
# in your .env, only to turn the mask off; it is on by default
COLLIE_REDACT=offマスク処理はbridge上で実行され、ターミナルミラー、ChatビューおよびHistoryビュー、すべてのプッシュ通知、Changesビューのdiff、Filesビューのファイルテキストの5つの経路に適用されます。非表示化された部分は同じ文字幅の•記号に置き換わるため、列数と行数は維持されます。ベンダープレフィックスはそのまま残るため、sk-o••••によって何が隠されたかは判別できます。
確度の高いパターンのみに一致します:
| パターン | マスク対象の例 |
|---|---|
| プレフィックス付きAPIキー | sk-…, sk-ant-…, sk-or-v1-…, ghp_…, github_pat_…, xoxb-…, AKIA…, AIza…, glpat-…, npm_… |
| JWT | 3つのbase64urlパートで構成され、第1パートがeyJで始まるもの |
| PEM秘密鍵 | BEGIN … PRIVATE KEYからそのEND行までのすべての行 |
| Bearerトークン | Bearer の後に続く20文字以上のトークン |
| 名前付きの値 | password=、secret:、token=、api_key:などの後に続く8文字以上の値 |
注意。 これはリスク軽減策であり、完全な保証ではありません。単独のプレーンテキストのパスワード、単なる16進数やbase64の値、リストにないプレフィックスのキーは意図的に除外されます。これらを対象とするパターンは、あらゆる画面で通常のテキストやコードまでマスクしてしまいます。
token: somethingのような通常の文章やYAML行など、一部の通常のテキストもマスクされます。入力して送信した内容はマスクされず、監査ログには独自のルールが適用されます(COLLIE_AUDIT_CONTENT)。
この設定はブリッジにのみCOLLIE_REDACTとして存在します。「Settings → System」には「Secret masking」として読み取り専用で表示されます。スマートフォン側に設定がないのは意図的な設計です。スマートフォン側に設定があると、ペアリング済みの端末や盗難端末からマスクを解除できてしまいます。マスクされた値を確認するには、マシン上で直接確認してください。ペインに初めてマスクされたテキストが表示されると、端末ごとに1回だけ、その旨を示す短いメッセージがスマートフォンに表示されます。マスクされたテキストを返信欄に貼り付けると、入力欄の下に、ペインにはシークレットではなくドットが送信される旨の警告が表示されます。
crewでは、各メンバーが自身のテキストをマスクし、さらにリードがスマートフォンに届く前に再度マスクします。リードは自身のマスク設定と同じルールでメンバーのmirror、Chat、History、diff、ファイル内容、ペインタイトルをマスクするため、1.17.xを実行中のメンバーがスマートフォンにキーを平文で送信することはありません。すでにマスクされたテキストを再マスクしても、内容はそのまま保持されます。テキストがマスクされずにスマートフォンに届くのは、リードとメンバーの双方でCOLLIE_REDACT=offが設定されている場合のみです。注意点として、1.17.xのメンバーは自身でマスクを行わないため、返信内容の検証対象となる行にシークレットが含まれていると、スマートフォン側で「The screen changed before that could be sent」として返信が拒否されることがあります。この場合、そのメンバーが1.18.0を実行するまで返信は保留されます。
リードは、メンバーの応答を読み取れずマスクできない場合、その応答を転送しません。その場合、接続できないメンバーと同様に、スマートフォンの画面には読み取り失敗として表示されます。
プッシュ通知でも、ペイン名は指定された名前のみを使用します。ペインのラベル、Claudeの/rename名、または1ペインのタブ名です。ペイン内のプログラムが設定したタイトルは絶対に使用しません。どのプログラムでもそのタイトルを設定でき、タイトルにパスやコマンド、シークレットが含まれる可能性があるためです。該当する名前がないペインは、ハーネス名(例: claude)で表記されます。
Sent: 送信したシェル行
各マシンは、Collie経由で送信されたシェル行の記録を保持します。これにより、以前のコマンドを検索して再送信できます。
# in your .env, to keep no record on this machine
COLLIE_SENT=off| 質問 | 回答 |
|---|---|
| 保持される情報 | シェルペインに送信した行、およびNewページからの単発コマンド。行ごとにテキスト、ディレクトリ、時刻、送信元の種別が記録されます。 |
| 保持されない情報 | エージェントへのプロンプト、プログラム用のテキスト、Keysボードのキー入力、Typeモードの入力。 |
| 保存場所 | 状態フォルダ内のsent.jsonl。COLLIE_STATE_DIRで変更しない限り~/.local/state/collie/です。ファイルパーミッションは0600、配置先フォルダは0700です。 |
| 保持期間 | マシンごとに最大5000行、かつ365日以内のもののみ保持します。 |
| 閲覧権限 | ペアリングされたデバイス。送信先ペインの内容を読み取れるためです。 |
| crew内 | 各マシンは自身のファイルを個別に保持します。スマートフォン等から要求された場合、leadはmemberに問い合わせを行い、コピーは保持しません。 |
Collieは入力されたすべての行を送信した後、記録を保持するか判定します。以下の条件に該当する行は一切記録されません:
- 先頭がスペースまたはタブで始まる行
TOKEN=abc deployやexport AWS_SECRET_ACCESS_KEY=…などの変数定義を含む行https://user:pass@hostなどのパスワードを含むURLを持つ行password、secret、token、bearer、authorizationなどの単語を含む行ghp_…やsk-…のキーなど、シークレットマスクが認識する形式を含む行- 4096文字を超える行
該当する行は、行データ、カウント、ログテキストのいずれにも残りません。
特定の行を記録から除外するには、先頭にスペースを入力してください。シェルは通常どおり実行され、Collieには何も記録されません。
注意。 このチェックは推測に基づきます。mysql -pPASSのように通常引数として渡されたパスワードや、未定義の形式を持つシークレットは記録されます。そうした行は先頭にスペースを付けて実行してください。特定の行を削除するには、Sentシートの該当行でForgetを使用します。マシン全体の記録を削除するにはClear Sentを使用します。いずれもCOLLIE_SENT=offが設定されている間も動作します。監査ログには削除件数のみが記録され、コマンド行は記録されません。すべての行はマシン外に出る前にシークレットマスクで処理されます。
設計の経緯: ADR 0107
Windows でのシークレットファイル
Windows(実験的機能)では、Collieはシークレットファイルを自身のアカウント、SYSTEM、Administratorsのみに制限して保護します。「自身のアカウント」とは、Collieを実行しているWindowsアカウントを指します。
NTFSには0600モードがないため、Collieは状態フォルダと設定フォルダのアクセス制御リスト(ACL)を使用します。Collieがそこに書き込む各ファイルには同じリストが適用されます。ユーザープロファイル内のデフォルトフォルダは、すでに他の標準ユーザーからアクセスできないようになっています。このチェックが最も重要になるのは、COLLIE_STATE_DIRやその他の設定でフォルダを移動した場合です。
起動時、bridgeは両方のフォルダとそのシークレットファイルをチェックします。他のアカウントが読み取り可能な場合、bridgeは修正を行いますが、これはCollie自体のフォルダに対してのみです。修正前にacl-backups(状態フォルダ内)に元のリストを保存し、復元用のicacls /restoreコマンドを出力します。このコマンドは管理者として実行したターミナルで実行してください。他のファイルも含まれるフォルダはチェックのみ行われ、変更はされません。Collieは実行すべきicaclsコマンドを出力します。collie versionなどの他のコマンドは、チェックと警告のみを行います。
collie doctorは結果をsecrets-privateとして表示します。他のアカウントが読み取り可能なフォルダはエラーとなり、修正方法が表示されます。Collieがチェックできないフォルダは警告となります: cannot confirm。
| ケース | 発生する事象 |
|---|---|
フォルダをコピー、復元、同期した場合(zip、/SECなしのrobocopy、OneDrive、ファイル履歴、USBドライブ) | コピーによってリストが失われることがあります。Collieは次回起動時に再チェックします。 |
| ウイルス対策ソフトまたは「コントロールされたフォルダーアクセス」が変更をブロックした場合 | Collieはフォルダをプライベートにできなかった旨を表示し、修正方法を提示します。 |
| フォルダがFAT、exFAT、またはネットワーク共有上にある場合 | Collieが使用できるリストが存在しません。Collieはcannot confirmと表示します。状態フォルダをこのPC上のNTFSドライブに移動してください。 |
COLLIE_NO_ACL_REPAIR=1 | Collieはリストを変更しません。チェックと警告は継続して行われ、doctorでも報告されます。 |
注。 これは管理者に対する保護ではありません。管理者は引き続きファイルを読み取ることができます。また、自身のアカウントで実行されている他のプログラム、フォルダ内のハードリンク、~/.claude内のエージェントのバックアップ、OneDriveやドキュメント内の状態フォルダは対象外です。マシンから外部へ送信される情報
デフォルトおよびポリシーとして、何も送信されません。Collie はインストールのイベント、利用統計、クラッシュレポート、アナリティクスを一切送信しません。これらを有効にするフラグも存在しません。
自発的に行われる唯一のアウトバウンド通信は更新チェックです。GitHubの公開タグAPI (bridge/update.ts) への匿名 HTTPS GET リクエストであり、現在のバージョンを最新のタグと比較します。ユーザーやマシンに関するデータは送信されず、静的なユーザーエージェント collie-update-check のみが含まれます。
もう1つの発信呼び出しは、Whistleモデルのダウンロードです。ユーザーが ホストにダウンロード をタップするか collie stt whistle を実行したときにのみ発生し、ユーザーに関するデータは一切送信されません (音声エンジン(Whistle))。
Walky talkyによる外部への発信呼び出しはありません。別マシンへのメッセージはcrew link上のみを通り、それ以外の場所へ送られることはありません(トランシーバー)。
「Report a problem」によって外部への発信呼び出しが追加されることはありません。送信に失敗した後、スマートフォンは入力済みのGitHubの新規Issueフォームを、お使いのブラウザのリンクとして開くことができます。テキスト全体をまず確認し、自身のアカウントで手動で送信します。CollieはGitHubトークンを保持せず、スマートフォンがGitHubを呼び出すこともありません(ADR 0123)。
COLLIE_ACCESS_TEAMを設定した場合、Collieは起動時および1時間ごとに、そのCloudflare Accessチームの公開鍵も取得します。この通信はユーザーの設定によって有効化されるものであり、ユーザーに関する情報が送信されることもありません。
URLでdurable agentをアタッチした場合、CollieはポーリングごとにAuthorizationヘッダーにdurable agentのベアラートークンを含めてそのURLを呼び出します。その呼び出しの有効化も自身で行う必要があり、アタッチするまで何も送信されません(durable agentのセキュリティ)。
今後データ収集が追加される場合でも、明示的なオプトインが前提となります。デフォルトはオフであり、目に見える形で確認を求め、フラグやデフォルト設定によって有効化されることはありません。この約束を撤回することは破壊的変更に該当します (ADR 0034)。