安全弁の点検スクリプトが環境差で嘘をつかないようにする
今回やったこと
自動公開や定期処理には、動かすための設定だけでなく止めるための安全弁がある。問題は、その安全弁がルール上は有効でも、実機では別の値になっていることだ。さらに、別の機械から対象機を点検する場合、点検コマンドが対象機ではなく実行機の状態を読んでしまうこともある。
この問題を対象に、scripts/safety_valve_audit.py と scripts/test_safety_valve_audit.py で、期待値・実効値・ロード状態を照合する点検を実装した。記事で扱う実装ステータスは実装済みであり、対象は自動処理の安全弁を機械的に監査する仕組みである。
ルールと実機は別の情報源
発端は、憲章には無人公開を有効にする環境変数が既定で有効と書かれていたのに、M1 の launchd plist にはその変数が無かった事例だった。プロセスはエラー終了しない。記事が公開されず在庫だけが増えるため、別の作業で偶然気づくまで正常に見えていた。
この種の事故を検知するには、設定ファイルを読むだけでは足りない。scripts/config/safety_valves.json に期待値を置き、実機の状態を別経路で読み、差分を通知する。点検対象は次の三つである。
| 点検対象 | 見つける問題 |
|---|---|
| 実効値 | plist に値がなく、スクリプトの既定値へ静かに置き換わる |
| ロード状態 | 止めたはず、または載せたはずのジョブが launchd と一致しない |
| plist と実行中の値 | ファイルを変更したが unload/load を忘れている |
GitHub のリポジトリ変数も、同じ期待値表で扱う。ただし、ローカルの launchd と外部のリポジトリ変数は取得経路が違うため、同じ値として一括比較せず、ソースごとに状態を保持する。
--host は実行機と対象機を分ける
点検コマンドに --host があるときの最重要ポイントは、引数を表示することではない。対象機が実行機と異なるなら、対象機の launchd を SSH 経由で読む必要がある。対象が不明な名前なら、黙ってローカルへフォールバックせず終了コード付きで拒否する。
概念的な処理は次のようになる。
対象機 = --host の指定、無ければ実行機
対象機 == 実行機 → ローカルの launchctl
対象機 != 実行機 → 台帳にある SSH 経路
未知の対象機 → エラー終了
実行結果には、実行機、対象機、SSH 経由かどうかを明示する。この表示は説明のためだけではない。監視のログを後から読んだとき、どの機械の状態を見ていたのかを復元できるからだ。
監視自身の環境差を検出する
監視スクリプトは、実際に処理を実行するスクリプトより信頼されやすい。しかし監視も同じ環境差の影響を受ける。別機の launchctl を読むつもりでローカルを読んでいたなら、出力が整っていても内容は対象外である。
そのため、点検では値だけでなく由来を扱う。期待値のソース、実効値の取得先、ジョブのロード状態、plist と実行中の差分を別々に結果へ残す。取得できない場合も「一致」とせず、未確認として通知する。安全弁の監査が安全弁をすり抜けないための最低限の境界である。
やってみてわかったこと
自動化の設定は、ファイルに書かれた状態と、プロセスが現在使っている状態が一致するとは限らない。特に launchd のようにロード状態を持つ仕組みでは、plist を編集しただけでは実行中のジョブは変わらない。
また、リモート点検では「どこから見たか」が「何を見たか」と同じくらい重要になる。実行機と対象機、ローカルと SSH、既知の対象と未知の対象を結果に出せば、監視の誤報を単に無視するのではなく、環境差そのものを異常として扱える。
更新履歴
- 2026-08-25: 初稿。安全弁監査スクリプトとテストの設計・実装をもとに整理。