08/Documentation
durable agentのセキュリティ
durable agentが実行できることとできないこと、ソケットおよびトークンの信頼ルール、Collieがdurable agentのテキストに加える処理、監査ログの記録内容
実験的。 通信プロトコル、SDK、およびこのページは、今後のマイナーリリースで変更されるか、機能自体が削除される可能性があります。機能が存続するかどうかの判断基準についてはdurable agentを参照してください。
durable agentはユーザーが実行またはデプロイするコードであり、Collieはその文言を信頼できないテキストとして扱います。このページでは、durable agentがアクセスできる範囲、他者によるアクセスを防ぐ仕組み、Collieがそのテキストに対して行う処理、および監査ログに記録される内容について説明します。セキュリティ はCollie全体を対象としています。
durable agentができることとできないこと
durable agentが問い合わせ、ペアリングされたデバイスが決定します。durable agentは、ユーザーの回答や送信内容、および通信上のリクエストを受信します。Collieがターミナルへのアクセス権をdurable agentに与えることはありません。
| durable agentができること | durable agentができないこと |
|---|---|
| 質問の提示(はい/いいえ、選択肢、1行テキスト、短いフォーム) | Collie経由で自らの質問に回答すること |
| スターターを一覧表示し、ベルト内に独自のボタンを最大4つ配置すること | ターミナルへの文字入力、または画面の表示 |
会話内へのテキスト、コード、https://リンクの配置 | 生HTML、画像、またはWebアドレスではないリンクの表示 |
| 上限の範囲内で、自身のプッシュ通知の文言を選択すること | ガードで許可された上限を超えてプッシュ通知を送信すること |
質問への回答は、人のタップ操作でのみ行えます。回答ルートは書き込みゲートの背後にあるペアリング済みデバイス向けに動作し、ブリッジ内の処理がdurable agent、フック、モデル出力のためにそれを呼び出すことはありません。スマートフォンでは、「Always allow」の選択に2回目のタップが必要です。これを提供するdurable agentは最も注意を要する権限を保持します。悪意のあるツールからの回答が1回でも通れば、それ以降のすべての呼び出しが確認なしで通過するためです。
承認されたアクションが何を実行するかを制御するのはCollieではなくdurable agentです。durable agentのボタンはそのdurable agentの権限で実行され、Collieはタップ操作を送信するだけです。
durable agentがロック画面を通知で埋め尽くすことはできません。Collieはdurable agentとツール呼び出しをキーとして各質問を1回だけ判定し、最大でも1回しかプッシュ送信しません。会話ごとに毎分3回、ホストごとに毎分10回、毎時30回までのプッシュを許可します。31回目となるプッシュは1件のサマリープッシュにまとめられ、ホストはその1時間後までプッシュを送信しなくなります。カードは常に表示されます。
注意。 同一ユーザーはソケット経由で質問に回答できます。ソケットは他のユーザーを締め出しますが、エージェントが実行するツールを含め、同一ユーザーのプロセスは除外されません。子孫プロセスチェックはdurable agent自身の防御策であり、durable agentがそれを実行しているかどうかをCollieが判別することはできません。
このチェックにより、durable agentは自身、自身が起動した任意のプロセス、およびこれらと区別できない呼び出し元からのanswer、send、abort、action、rename、close、reopen、starterを拒否します。拒否は403 caller-refusedとなり、読み取りは開いたまま維持されます。このチェックにはLinux上のBunが必要であり、SDKがソケットの呼び出し元を読み取ります。以下の表は、durable agentの実行環境ごとに強制される内容を示しています。
| durable agentの実行環境 | 子孫プロセスチェックが強制する内容 |
|---|---|
| Linux上のBun | デフォルトで実行されます。SDKが接続ごとの呼び出し元を読み取り、durable agent、その子孫プロセス、または特定できない呼び出し元からの操作を403 caller-refusedで拒否します。 |
| macOSなど、Linux以外のBun | 実行できません。durable agentがcallerCheck: "off"を渡さない限り、openCollieは起動時にエラーで停止します。offを指定した場合、対象ユーザーの任意のプロセスが回答可能になります。 |
| Node | 実行できません。durable agentは起動し、警告を1行出力します。対象ユーザーの任意のプロセスが回答可能になります。 |
エージェントのツールをソケットにアクセスできないサンドボックスに配置するか、カードをゲートではなく単なる通知として扱うか、あるいはLinux上のBunでdurable agentを実行してください。callerCheck: "off"は、チェックなしで実行するというdurable agent自身の選択です。
ソケットトランスポート
プライベートソケットは他のすべてのユーザーを締め出し、その唯一のゲートはファイルパスです。
ls -ld /run/user/1000 /run/user/1000/desk /run/user/1000/desk/app.sockソケットはトークンやAuthorizationヘッダーを保持しません。Collieはパス全体がプライベートである場合にのみ接続し、リンクをたどらないよう、各接続の前に/から下に向かってlstatを用いてこの検証を実行します。
| 構成要素 | Collieが要求する条件 | 理由の内容 |
|---|---|---|
| パスの各部分(ソケットを含む) | シンボリックリンクは不可(正しい場所を指していても不可) | socket-not-private: <path> is a symlink |
| ソケット自体のフォルダー | 自身の所有する実フォルダーであり、モードが厳密に 0700 であること | the socket's folder <path> has mode 0755, not 0700、belongs to user <a>, not to <b>、または <path> is not a directory |
| その上位のすべてのフォルダー | group および others から書き込みできない実フォルダー。/tmp など sticky ビット付きのフォルダーはここで許可されるが、ソケット自体のフォルダーとしては不可。所有者は確認されない。プライベート /tmp を持つサービスでは root のフォルダーがユーザー 65534 として表示されるため | <path> has mode 0775, so group or others can write to it |
| ソケット | 自身の所有するソケットであり、group および others のパーミッションがないこと | the path is not a socket、the socket belongs to user <a>, not to <b>、the socket has mode 0666, not 0600 |
| ユーザー ID のないプラットフォーム | フェイルクローズ: Collie は接続しない | this platform shows no file owner |
| Windows | HTTPS トランスポートのみ利用可能: collie durable attach --socket は書き込み前に拒否 | a private socket needs Unix user ids; on Windows use --url <https://...> --token-env VAR |
検証に失敗したパスは猶予なく直ちに到達不能になる。collie doctor は durable-socket 行で同じチェックを挙げて修正方法を示す。何も存在しないパスは no socket at <path> と判定されるが、これはプライバシー上の問題ではない。
SDK で構築された durable agent は、ソケットのフォルダーが存在しない場合にモード 0700 で作成する。別のモードでフォルダーが既に存在する場合は起動を拒否する。ソケットをモード 0600 に設定し、リッスン前に不要なソケットファイルを削除する。
ソケットトランスポートは Linux および macOS 向け。Windows にはソケットトランスポートがないため、token トランスポートを使用すること。
token トランスポート
URL と token を使用して別ホストの durable agent にアクセスする。token がアクセス制御のすべてを担う。
openssl rand -hex 32| ルール | 値 |
|---|---|
| トークン | 前後の空白を除いた状態で32〜4096バイト、スペースを含まない印字可能ASCII |
| URL | https://、またはこのマシン上の durable agent の場合は http://127.0.0.1:<port> か http://localhost:<port>。最大 2048 バイト。ユーザー名、パスワード、クエリ、フラグメントは不可 |
| 証明書 | 通常のクライアントと同様に検証される。Collie にスキップ用のスイッチはない |
| リダイレクト | Collie はリダイレクトに追従しないため、token が別ホストに送信されることはない |
token を保持していれば誰でもスマートフォンとして振る舞える。すべての会話の閲覧、質問への回答、テキスト送信、ターンの停止、名前変更や終了、starter の実行が可能。インターネットからの TLS 接続には発信元の確認対象がないため、ここでは descendant チェックは存在しない。この方法でアクセスされる durable agent は Allow と Deny のみを提供すべきであり、always-allow は提供してはならない。
ホストはテキストを閲覧できる。TLS はホストで終端するため、ヘッダー内の token を含め各リクエストはホストから見え、会話はホストのストレージに保存される。
Collie は token をプレーンテキストで 2 か所に保持する。sources.json にエントリとして保持され、設定フォルダーのバックアップや同期によって複製される。Collie はこのファイルをモード 0600 で保持し、より広い権限のファイルは起動時にログを出力してパーミッションを制限する。また、group または others が読み取り可能な場合は collie doctor に赤い sources 行が表示される。durable 専用の形式では .env または config.toml に保持され、これを保持するファイルはモード 0600 でなければならない。Collie が token を出力することはない。collie durable list には (token set) と表示され、collie doctor には set, <n> bytes と表示され、エラーやログ行に値が含まれることはない。attach は --token-env で指定された変数から token を読み取るため、コマンドライン上には現れない。
token に有効期限はない。ローテーションを行うには、Collie を再起動する前に新しい token を durable agent に設定する。そうしないと、durable agent に拒否された旨がスマートフォン側に表示される。durable agent はローテーション処理中に 2 つ目の token を受け入れる場合がある。保留された Cloudflare の例では COLLIE_TOKEN を受け入れ、設定されている間は COLLIE_TOKEN_NEXT(ADR 0103)も受け入れる。手順は トークン に記載されている。
これは Collie が設定された URL に対して行うアウトバウンド呼び出しであり、何も添付しないことで拒否される。各リクエストには Authorization: Bearer <token>、ワイヤーバージョン、前回の読み取りの ETag、および tap の場合は回答または送信した本文が含まれる。読み取りは本文なしの GET となる。
https 経由のポーリングはすべてホストから課金される可能性のあるリクエストであり、「変更なし」の応答もカウントされる。スマートフォンが開かれていない状態ではアイドルポーリングが 10 秒ごとに実行されるため、Collie は 1 日に 8,640 回問い合わせを行い、スマートフォンで監視中は 2 秒ごとに問い合わせる。durable agent がダウンしているか token を拒否する場合、問い合わせは最大で 10 秒に 1 回となり、これらのリクエストも課金対象となる。Collie が終日ポーリングを行う durable agent には有料プランを使用すること。
Collieがdurable agentのテキストに対して行う処理
Collieは他の処理が読み取る前に、durable agentからのすべての文字列をサニタイズし、テキストとして描画します。
durable agentは上限内の文字列を送信する必要がありますが、Collieはその遵守を前提としません。各文字列は以下の3つのステップで処理されます。
- 制御文字の削除。 CollieはC0制御文字、DEL、およびC1制御文字を削除します。複数行を保持するフィールド(アイテムのテキスト、ツール呼び出しの出力と要約、質問の理由)では、改行とタブを保持します。
- 不可視文字と方向文字の削除。 Collieは、方向マーカー(U+200E、U+200F、U+061C)、オーバーライドとアイソレート(U+202A〜U+202E、U+2066〜U+2069)、ゼロ幅文字(U+200B〜U+200D)、単語結合子(U+2060)、バイト順マーク(U+FEFF)、および不可視の文全体を伝送しうるタグ文字(U+E0000〜U+E007F)を削除します。
- 文字列の切り詰め。 Collieは文字単位の境界でバイト上限に合わせて切り詰め、末尾に
…を付加します。
サニタイズによってタイトルまたは理由が変更された質問のカードには、「Text shortened」のマークが表示されます。文字が残らなかったオプションやベルトボタンは破棄されるため、ボタンが空白になることはありません。値が空になったフィールドには、そのidが表示されます。
| テキスト | 上限 |
|---|---|
| ワークスペース、タブ、会話、または質問のタイトル | 256バイト |
| オプション、フィールド、またはスターターのラベル | 128バイト |
| ベルトボタンのラベル | 16文字 |
| 質問の理由 | 2048バイト |
| ツール呼び出しの出力 | 16384バイト |
| メッセージまたは返答のテキスト | 65536バイト |
markdownまたはcodeアイテムのテキスト | 32768バイト |
| durable agentに送信するテキスト | 1〜32768バイト、送信前に拒否 |
| durable agentからの1回の返答 | helloの場合は16 KiB、スナップショットの場合は4 MiB |
idはサニタイズや切り詰めの対象になりません。idは制御文字、空白文字、および/、?、#を含まない1〜128バイトである必要があります。満たさない場合、そのidを持つオブジェクトは破棄されます。
Collieはテキストをテキストとして描画します。markdownアイテムは、見出し、リスト、強調、コードスパン、リンクを表示し、生のHTMLは描画されません(タグは文字としてそのまま表示されます)。markdownアイテム内のリンクはWebアドレスのみを開きます。linkアイテムには、ホストを含み、ユーザー名やパスワードを含まず、空白、制御文字、不可視文字がなく、最大2048文字のhttps://アドレスが必要です。これらを満たさない場合、Collieはそのアイテムを破棄し、会話ごとに1行ログを出力します。画像とファイルはアイテムではありません。
ログに記録される内容
Collieは、スマートフォンがdurable agentに対して行う各書き込みを<state-dir>/audit.log(モード 0600)に出力します。
各行には、durable agentの名前(session)、ペアリングされたデバイス、およびイベントが含まれます:
| イベント | 表示条件 | 行に保持される内容 |
|---|---|---|
answer | タップによって質問に回答した | リクエストID、質問とそのリビジョン、オプションID、または回答の種類(optionIds、text、またはvalues)、および結果。入力された文字列は保持されません |
send | テキストを送信またはステアリングした | リクエストID、指定されたターンまたは質問、steer、結果、および120文字のプレビューとしてのテキスト |
abort | ターンを停止した | リクエストID、指定されたターンまたは質問、および結果 |
action | ベルトボタンをタップした | リクエストID、アクションID、ターン、および結果 |
starter.start | スターターをタップした | スターターID、リクエストID、および結果 |
durable.reopen | 終了した会話を再度開いた | 会話ID |
space.rename | ワークスペース名を変更した | スペースIDと新しい名前 |
会話やタブの終了および名前の変更には、通常のイベント(pane.close、pane.rename、tab.rename)が使用されます。COLLIE_AUDIT_CONTENT=noneは、リクエストIDなどの指定された一部のフィールドを除き、詳細内のすべての文字列を秘匿化するため、sendのプレビューも秘匿化されます。ログには事後に何が起きたかが記録されます。何かを中断することはありません。
Collieは、入力テキストを除き、各回答、送信、停止、ボタンタップ、スターターのレシートも保持します。保持期間はCollieがdurable agentのデータを保持する場所で確認できます。