コンテンツにスキップ

ガードレール — すべての変更操作に対するひとつのポリシー

Control Center のエージェントは、ファイルの削除、ブランチのプッシュ、プルリクエストの公開、外部 API の呼び出しを行えます。それぞれは異なる_種類_のリスクであり、長い間、そのうちのひとつ — シェルコマンド — にしかポリシーがありませんでした。ガードレールは、エージェントが実行できるあらゆる変更操作について、どのランタイムやツールがそれを提案するかに関係なく、ひとつの問いに一律に答えるために存在します。これは許可されるのか、承認が必要なのか、それとも拒否されるのか。

ポリシーエンジンはただひとつだけです。アクションがシステムの外に出うるあらゆる箇所、つまり内蔵ハーネスのツールレジストリ、サーバー側 MCP ディスパッチャーのゲート、リポジトリ操作の変更で参照されます。そのため、一度書いたルールは内蔵ランタイムにも外部エージェント CLI にも同じように適用されます — それを迂回する、より静かな第二の経路はありません。以前からある bash 専用のコマンドポリシーも引き続き機能します。ルールはアクションクラスではなくコマンドプレフィックスにマッチでき、2 つの形式は同じストアに共存します。

ただし、このエンジンは OS サンドボックスの代わりにはならず、サンドボックスもこのエンジンの代わりにはなりません。両者は異なる問いに答えます。ガードレールは、Control Center が見ることのできる呼び出しをそもそも実行してよいかを決め、サンドボックスは、その結果として生じるプロセスがどこまで到達できるかを制限します。cc_server は、ホストがバックエンドを提供する場所ではサンドボックスを適用します — しかし Windows にはそれがなく、Linux には bwrap と socat が必要です。そうしたホストでは、ガードレールは床の上に載るポリシーではなく、もっとも外側の強制レイヤーになります。どれだけ寛容にするかを決めるときには、これを知っておく価値があります。

すべての変更ツールは、13 個の効果クラスからなる閉じた集合の中から、自分が属する ActionClass を宣言します。そのうち 5 つは初期状態で確認を求め、残りは許可されます。それを変えるのがルールです。

クラス 組み込みのデフォルト 対象
fileDelete 先に確認 ファイルの削除(削除の形をした書き込み、rm)
gitPush 先に確認 git リモートへのプッシュ
prCreate 先に確認 プルリクエストの作成
prPublish 先に確認 レビューの公開、PR のマージ
vendorSyncWrite 先に確認 外部チケットベンダーへの書き込み(Linear、Jira、GitHub 同期)
fileWriteOutsideWorktree 許可 分離されたワークツリー外へのファイル書き込み
gitCommit 許可 git コミットの作成
networkEgress 許可 ネットワーク外向き通信(Web フェッチ、任意の HTTP)
secretAccess 許可 シークレットまたは資格情報の読み取り
packageInstall 許可 パッケージのインストール(npm、pip、pub、brew)
processSpawn 許可 プロセスの起動(シェルと、シェル経由で実行されるものすべて)
workspaceMutation 許可 ワークスペース構造の変更(リポジトリ、スペース、エージェント)
enclosureControl 許可 エンクロージャー(リグ)の操作: VM の起動、入力の送信

デフォルトで許可される 8 つのクラスは、その下にサンドボックスの床が適用されるという前提で選ばれました。ホストがバックエンドを提供する場所ではそうなりますが、Windows や、bwrap と socat を欠く Linux マシンではそうなりません — そこでも fileWriteOutsideWorktree や packageInstall をゲートで制御したいなら、それは自分で書く必要のあるルールです。

この集合は意図的に閉じています。クラスを宣言しない新しい変更ツールはラチェットテストに失敗するため、分類は明示的で論証された決定によってしか増えません — 「分類の肥大化はこの機能の死である」。13 個のクラスは、人が実際に書くポリシー(確認なしでは決してプッシュしない、ワークツリーの外では決して削除しない、読み取りは常に許可する)を表現するのに十分であり、設定 UI が理解可能なままでいられる程度に少ない数です。

ルールは (scope, ActionClass | command prefix) → allow | prompt | deny を対応付けます。スコープはもっとも具体的なものからそうでないものへと入れ子になります。

スペース > エージェント > ワークスペース > モードプリセット > 組み込みのデフォルト

解決は具体性が優先です。一致するルールを持つ最初のスコープが決定し、そこで解決は止まります。もっとも制限的であることは、あくまでタイブレークです — 同じスコープ内の同じ具体性を持つ 2 つのルールの間と、単一のアクションが宣言する複数のクラスを組み合わせるときに使われます。スコープ内では、コマンドルールはもっとも長いコマンドプレフィックスにマッチします。許可が拒否に勝つことはどこでもありません。

2 つの決定には強い保証があります。

  • フェイルクローズドの確認要求。 承認者が接続されていない prompt は_拒否_に解決されます。答えのない問いが「はい」になることはありません。
  • もっとも制限的な組み合わせ。 複数のクラスにまたがるアクションは、それらをもっとも制限的な形で組み合わせます。複数のクラスが確認を求めるときは、1 回の確認がそれらすべてを列挙します。

ターン単位のラッチは意図的にありません。すべてのツール呼び出しがポリシーをゼロから再解決するため、同じアクションに対する確認要求の繰り返しは、ターンに紐づく何かではなく、オペレーター自身の「この決定を記憶する」によって制限されます。

ルールは、アクションの動詞だけでなく、その引数も制約できます。「プッシュしてよい」と「feature/* へプッシュしてよい」は別の主張であり、制御になるのは 2 つめだけです — 動詞だけを許可する能力ゲートこそ、公開されているエージェントインシデントの大半に見られる形です。

制約は次のものを指定できます。

  • paths — glob パターン(repos/**、!**/.env)
  • refs — git の参照。! による否定が使えます(['**', '!main'] は「main 以外のすべてのブランチ」と読めます)
  • hosts — ネットワークの宛先。サブドメインのワイルドカードが使えます(*.internal)
  • commands — コマンドプレフィックス。単語境界でマッチします
  • ceilings — maxCount / maxCents。「最大 50 ファイルを削除する」を表現できます

文法を閉じたまま、ループのないままに保つことから 3 つの性質が導かれ、その 3 つすべてが狙いそのものです。すべてのルールは読み返せる文として表現され、すべての拒否はマッチした制約を名指しでき、ポリシー全体は列挙できます — エージェントが何をしてよいかを実行する前に確認できるのです。

重要な 3 つのルール:

  • 制約付きのルールは、同じスコープでは制約のないルールより具体的であるため、「main へのプッシュを拒否」は、誰も順序を考えなくても「プッシュを許可」に勝ちます。
  • 制限的なルール(拒否/先に確認)は、リクエストが指定された値のいずれかに触れたときに適用されます。許可的なルール(許可)は、リクエストがその範囲内に完全に収まるときにだけ適用されます。両方を同じように読むと、エージェントは無害なパスを禁止されたパスの隣に混ぜ込むことで、禁止パスを洗浄できてしまいます。
  • リクエストが何も語っていない側面を指定したルールは、適用もスキップもされません。承認へのエスカレーションが行われます。黙ってスキップすると、ref を報告しないツールに対して保護ブランチのルールがすべて無効になります。黙って拒否すると、抽出器が記述できないプッシュをすべて拒否することになります。承認者が接続されていなければ、既存のフェイルクローズドのルールがこのエスカレーションを拒否に変えます。

コマンドルールが捕捉できることとできないこと

「コマンドルールが捕捉できることとできないこと」というセクション

commandPrefix ルールは単語境界でマッチします。git push は git push origin main をカバーし、git pushx はカバーしません。これはシェルを正規化しません。git push(スペース 2 つ)、cd sub && git push、GIT_DIR=… git push はすべて異なる文字列であり、git push に関するルールにマッチするものはありません。

これは現実の限界であり、覆い隠すのではなく明言されています。コマンドルールは、通常のエージェントの振る舞いを形作るためのオペレーター向けの便宜であり、敵対的なエージェントに対する境界ではありません。境界となるのは、効果クラス(ツールが宣言し、書き換えられないもの)、ポリシーストアの下にあるモードのコマンドネット、そしてその両方の下にある OS サンドボックスです。allow に解決されるコマンドルールが、そのどちらかを緩めることは決してありません — モードのネットは後から実行され、その deny は最終です。

「これを記憶する」で確認に答えると、実際のポリシールールが書き込まれます。スコープを持ち(スペース/エージェント/ワークスペース)、引数で制約され(feature/login へのプッシュを承認すると、main ではなく feature/** が許可されます)、明示的な TTL で自動失効します。次の数時間だけ「はい」と言うことは、恒久的なポリシーを静かに書き換えることとは違います。書き込まれたルールは同じストアに入り、プロベナンスは remembered、有効期限付きなので、早めに取り消せます。

各ルールは次のいずれかを持ちます。

  • advisory — 許可しますが、ルールがマッチしたことを記録します。これは導入の道筋です。厳格なルールを advisory モードで展開し、1 週間監査証跡を読んで、それが実際に何をブロックしていたかを正確に確認してから昇格させます。この段階がなければ、厳格なポリシーは決して有効にされません。
  • soft — 拒否しますが、上書き権限を持つ人が記録された正当性とともに上書きできます。上書き自体も監査対象のイベントです。
  • hard(デフォルト)— 拒否します。何もそれを上書きできません。

インストールのオペレーターは、どのワークスペース管理者も緩められないポリシーを固定できます。マネージドルールはサーバー側にあり、各ワークスペース自身のチェーンともっとも制限的な形でマージされます — チェーンの先頭に置かれるスコープではありません。チェーン先頭のマネージドな allow があると、インストールがワークスペースの拒否を上書きできてしまい、これはクランプの目的の正反対だからです。したがって、マネージドルールは決して厳しくすることしかできません。

CC_SERVER_MANAGED_POLICY を JSON ファイルに設定すると、保存されたルールを完全に上回ります。これにより、オペレーターは管理 UI ではひっくり返せない姿勢を固定できます — 「開発者が安全制御を無効化するのを止められるか?」への答えです。

自律度ダイヤルは並行システムではなくプロファイル

「自律度ダイヤルは並行システムではなくプロファイル」というセクション

スペースごとの自律度ダイヤルは、この同じポリシーストア上の名前付きプロファイルであり、2 つめのメカニズムではありません。その 3 つの設定は proposeOnly、actWithApproval、actFreely として保存・送信されます(ハイフン付きの形式は UI の文章にすぎず、autonomy.setForSpace はそれ以外をすべて拒否します)。未設定のままにすると actWithApproval として動作します。

解決済みの決定に対して、それぞれが行うことは次のとおりです。

  • proposeOnly はゲート対象のすべてのツールを真っ向から拒否します。エージェントは推論して返答することは依然としてできますが、行動することはできず、そのメッセージに理由が示されます。
  • actWithApproval(および未設定)は、前述のフェイルクローズドの承認ゲートです。
  • actFreely は、ハードな deny に解決されなかったものを事前承認します。

最後のものは、和らげるのではなく率直に述べておく価値があります。actFreely の下では、prompt の決定は承認者にエスカレーションされません — 許可され、誰にも確認されません。ダイヤルを生き残るのは明示的な deny ルールだけです。したがって actFreely は便宜的な設定ではなく、意図的な自律性の付与であり、フェイルクローズドのルールはそれが重要になるまさにそのケース(デフォルトである actWithApproval)を守ります。

エージェント間の委任は、delegate_task の関門で 4 つのガードすべてによって守られます。深さの上限(デフォルト 3)、循環の検出、自律性の上限(委任されたエージェントは、委任した側より大きい自律性で行動することは決してできません — より自由なエージェントに作業を渡すことで権限を洗浄することはできません)、そして予算のエンベロープ(委任は委任者の残り予算に計上され、それ以上を生み出すことはできません)。いずれも声高に拒否され、ガードの理由はそのままエージェントに返されます。

すべてのランタイムが、Control Center にあらゆるアクションをインターセプトさせるわけではありません。外部エージェント CLI は、いくつかのツールをネイティブに、インプロセスで実行し、そこにはゲートが置けません。そうでないふりをする代わりに、各アダプターは正直さのマトリクスを持ちます。アダプターごとに、Control Center がツールサーフェスをフィルタリングするか、ツール呼び出しをインターセプトするか、完了コントラクトを監視するか、ランナーのネイティブツールを見られるか、インプロセスのツールがサンドボックスプロファイルでカバーされるかを示します。

2 つの項目は、設定を信頼する前に読む価値があります。内蔵ハーネスは、そのインプロセスのファイルツールがサンドボックス化されていないと宣言しています — ツールサーフェスとこのポリシーエンジンが、それが持つ唯一のファイルシステム境界です。Claude Code は、--dangerously-skip-permissions で起動されるため、自身のツール呼び出しがインターセプトできないと宣言しています。Control Center が見るのは mcp__* の呼び出しと、フック経由の Bash のコマンドラインで、それ自身の読み取り、書き込み、編集の各ツールは見えないまま実行されます。

設定画面はこのマトリクスを表示します。ガードレールシステムにとって唯一許しがたい失敗は、存在しないカバレッジを主張することだからです。

ガードレールは 設定 → ワークスペース → エージェントの権限(/workspaces/<id>/settings/workspace/permissions)にあります。この画面には、ルールを書くポリシーマトリクス、ポリシーテンプレート、「もしも?」プローブ、認可監査証跡、アダプターの正直さのマトリクスがあります。

プローブは、アクションクラス(またはコマンド)とスコープを受け取り、どのルールが決定したかとその理由を表示します。解決は純粋で決定的であるため — 同じアクション、同じスコープ、同じ決定 — プローブの答えは、エージェントが得る答えそのものです。