Skip to main content
このページは、コマンドを実行する側そのものを作っている人のためのものです。エージェント、タスクランナー、CI のハーネス、shell ツールを持つプラグインホストなどが該当します。そうしたプログラムに hook をインストールすることはありません。チェックは自分で呼び出します。 組み込みに必要なのは 1 つの関数だけです。cc-safety-net/api サブパスからエクスポートされる checkCommand です。型、投げられる TypeError のメッセージ、1 回の呼び出しが読むものはライブラリ API がリファレンスとして持っています。このページは、その周りの判断を扱います。

プロセス内のチェックか、インストール済みの hook か

どちらも同じ guard を通ります。違うのは、誰が呼び出しを所有するかです。 判断の目安は最後の行から導けますが、本質は所有権です。ユーザーが対応エージェント CLI のいずれかを使っているなら、インストールコマンドを案内して連携に任せてください。各エージェントの組み込み方は連携アーキテクチャにあります。自分自身がコマンドを実行するプログラムなら、関数を呼んでください。自分のランタイム向けに hook 設定のジェネレーターを作るのは、すでに存在する連携を作り直すことになります。
パッケージのルートエクスポートはエンジンではなく OpenCode のプラグインオブジェクトです。checkCommandcc-safety-net/api から import してください。

組み込み時のポリシー解決

組み込み専用の設定はありません。呼び出しが読むファイルは hook が読むものと同じで、どのプロジェクトのファイルになるかは cwd 引数が決めます。
  • ユーザースコープ~/.cc-safety-net/policy.json~/.cc-safety-net/rules/CC_SAFETY_NET_HOME が設定されていればその場所に移ります。これは運用者自身のベースラインで、すべての呼び出しに適用されます。
  • プロジェクトスコープ<cwd>/.cc-safety-net/policy.json<cwd>/.cc-safety-net/rules/。渡したパスそのものから解決され、親ディレクトリをさかのぼる探索は行いません。セッションのサブディレクトリを追跡するホストは、リポジトリのルートを cwd として渡してください。そうしないと、コミットされたプロジェクトポリシーが読み込まれません。
  • マージ:プロジェクトファイルはユーザーファイルの上にフィールド単位で重なります。両スコープの保護対象パスのリストは和集合になり、プロジェクトファイルの audit は無視され、プロジェクトが緩めたフィールドは黙って適用されるのではなく報告されます。マージの仕様はポリシーが持っています。
  • 環境変数CC_SAFETY_NET_LEVEL と各機能のスイッチは呼び出しのたびに process.env から読まれ、レベルはポリシーファイルの設定より引き上げることしかできません。環境変数を参照してください。
プロジェクトごとの差は、そのまま呼び出しごとの差になります。cwd がそれを選ぶからです。複数のチェックアウトを同時に扱うホストは、正しいディレクトリを渡すだけでそれぞれに正しいポリシーを得られます。追加の API は要りません。 呼び出し側を書く前に知っておく価値のある挙動が 2 つあります。読み取りと検索が可能なディレクトリとして stat できない cwd は、例外ではなく fail-closed の deny を返します。そのため、パスの打ち間違いは別のプロジェクトを解析するのではなくコマンドのブロックになります。もう 1 つ、cwd の欠落や文字列でない command といった不正な入力は TypeError を投げます。これはコマンドに対する判定ではなく、呼び出し側のバグです。

API が保証するもの

次の点はマイナーバージョンをまたいで保たれるので、ホストはこれを前提に実装できます。
  • 入力の形 { command, cwd }cwd は絶対パスのディレクトリ)と、2 つの結果の kind、すなわち allow と、reason 文字列および省略可能な ruleId を持つ deny
  • deny はコマンドを実行してはいけないという意味で、throw も同じ意味です。throw が allow になることはありません。
  • cwd がポリシー解決の基準になります。コマンド内の相対パスも、プロジェクトの .cc-safety-net/ 設定も、渡した cwd を基準に解決され、process.cwd() は使われません。
  • API のパスは監査レコードを書かず、ネットワークリクエストも行いません。呼び出しが読むのはローカルのポリシーファイル、ファイルシステムの状態、環境設定で、マシンの外には何も出ません。
ルールカタログは、どのマイナーバージョンでも変わり得ます。ブロック対象のコマンドはリリースごとに増え、それに伴って reason の文言も変わります。そのため、アップグレードによって、テストのフィクスチャが依存していたコマンドが allow から deny に変わることもあります。分岐は kind で行ってください。reasonruleId は突き合わせの対象ではなく、人間とログのために残すものです。挙動をテストしている他の依存関係と同じように、バージョンは lockfile で固定してください。

実装例:プラグインホスト

プラグイン機構を持つホストは、たいてい実行前の hook を公開していて、そこでツール呼び出しを拒否できます。連携点はそれだけです。プラグインはホストのイベントを { command, cwd } に対応付け、checkCommand を呼び、deny をホスト側の拒否表現に変換します。
このハンドラーで重要な判断は 4 つです。
  • チェックするのはコマンド系ツールだけです。checkCommand が解析するのはシェルコマンドの文字列です。ホストの read、write、search といったツールはコマンド系ツールではないので、shell ツールだけを渡し、他はそのまま通してください。
  • **cwd は絶対パスのプロジェクトルートです。**ホスト側の呼び名が何であれ、その値がプロジェクトポリシーを選び、コマンド内の .env のような相対パスの基準になります。
  • throw はブロックです。catch は契約の fail-closed 側です。投げられたエラーをそのまま実行に進ませるホストは、壊れたチェックを allow に変えてしまいます。
  • **不正なイベントもブロックです。**同梱のプロセス内連携はまさにこう動きます。command フィールドが欠けている、あるいは文字列でないイベントは、スキップではなくブロックされます。解析する対象が存在しないからです。
ホストの拒否表現がオブジェクトの返却ではなく例外なら、拒否メッセージを返す代わりに投げてください。形は変わりますが、判断は変わりません。

運用上の注意

  • **ログは自分のものです。**このパスでは監査レコードが書かれないので、ホストが記録しない拒否は何も残りません。logs コマンドと GUI のダッシュボードが表示するのは hook とプラグインの判定であって、組み込み実行のものではありません。
  • **運用者は通常のツールで設定します。**組み込みホストも同じポリシーファイルを読むため、statusdoctorexplain はそのままホストの挙動の説明になります。ただし、cwd として渡すのと同じディレクトリで実行した場合に限ります。なぜブロックされたのかを聞かれたら、explain トレースを案内してください。
  • **ruleId は診断用です。**表示する、記録する、ルールを引くために使う。いずれも問題ありませんが、分岐条件にはしないでください。
  • **呼び出しは同期です。**まとめて処理したいならループで回してください。非同期版もバッチ版もありませんし、追加したところで呼び出しがすでに行っているファイル読み取りを隠すだけです。
  • バージョンはパッケージから読めます。cc-safety-net/package.json はエクスポートされたサブパスなので、依存関係のバージョンを報告するホストはこれも含められます。

関連ページ

最終更新日 2026年8月31日