はじめに
kanade — 奏 は多数の Windows 端末を一元管理するシステムです。operator はひとつの CLI / SPA から、数百台規模の PC に対してスクリプト実行・ソフトウェア配布・インベントリ収集・ライブのパフォーマンスデータ取得を一気にこなせます。構成要素:
| 構成要素 | 役割 |
|---|---|
| kanade-agent | 管理対象 PC 上で動く Windows サービス。NATS を購読し、コマンドを実行して結果を送り返します。 |
| kanade-backend | HTTP API + projector。状態を永続化し、SPA を配信し、operator 用エンドポイントを公開します。 |
| kanade-client | オプションの Tauri デスクトップアプリ。エンドユーザー向け UI。 |
| NATS server | コマンドのファンアウトと結果集約を担うメッセージブローカー。agent は NATS とのみ通信し、backend も NATS から読みます。 |
| kanade CLI | operator が使うコマンドライン。バイナリの publish、ジョブの起動、状態の照会を行います。 |
このサイトは 2 種類の読者を想定しています:
- 運用者 (kanade fleet の運用者) 向け — 各端末に ssh せずに各コンポーネントをアップデートする方法について解説しています(agent 経由のアップデート 参照)。
- 開発者 (agent が実行する PowerShell ジョブを作成する人) 向け — スクリプトが動作する仕組みや制限事項、最近の agent バージョンにおける変更点について解説しています(agent 向けスクリプトを書く 参照)。
詳細なプロトコル / on-wire 仕様は Spec にあります (旧 single-page ドキュメント。docs サイトが整うにつれて章別に分割予定)。
開発者クイックスタート
このガイドでは、ローカルの kanade 開発環境をセットアップして起動する手順を説明します。
1. 前提条件
開始する前に、Windows マシンに以下がインストールされていることを確認してください。
- Rust ツールチェーン (stable チャンネル)
- cargo-make (
cargo install --force cargo-makeでインストール) - bun (SPA の依存関係管理とビルド実行用)
- gsudo (ローカルのサービスデプロイテスト用)
- nats-server (PATH から実行可能であること)
2. 初回セットアップ
ワークスペースのルートで以下のコマンドを実行し、git の pre-push フックを登録し、apm.yml で定義されたエージェントスキルをインストールします。
cargo make setup
3. 開発サンドボックスの起動
単一のコマンドを使用するだけで、ローカルホスト上に完全に分離されたマルチコンポーネント開発スタックを起動できます。
cargo make dev
このタスクは、ループバックサンドボックス内で以下のサービスを並行して実行します。
- nats-dev: ポート
4223で待機する、認証なしの NATS ブローカー。 - backend-dev: 認証が無効化された、ポート
8081で待機する開発用 API サーバー。 - agent-dev: ポート
4223の開発用 NATS ブローカーと通信する、ローカルの開発用エージェント。 - web-dev:
http://localhost:5173で待機する、React SPA 用の Vite 開発サーバー。
Ctrl+C を押すことで、すべてのコンポーネントをクリーンに終了できます。
4. 複数エージェントフリートのシミュレーション
複数のマシンを管理しているときにのみ発生する動作(並行実行結果のプロジェクションや ID 衝突など)をデバッグするために、マルチエージェントサンドボックスを起動できます。
cargo make dev-fleet
これにより、NATS ブローカー、バックエンド、SPA に加えて、独立した ID(dev-pc-1、dev-pc-2、dev-pc-3)と隔離された状態データベースを持つ 3 つの個別開発用エージェントが起動します。
5. ローカルデプロイのテスト
Windows サービスとしてコンポーネントをインストールする全ライフサイクル(本番環境の模倣)をテストしたい場合は、ローカルデプロイスクリプトを使用します。
# Installs CLI, agent, backend, and NATS services locally via gsudo elevation
cargo make local-deploy
デプロイ後、実際の Windows サービスを確認して対話できます。完了したら、以下のタスクを使用してサービスを停止し、きれいに削除します。
cargo make local-undeploy
スクリーンショット
運用コンソールの実際の画面です。このページの画像はすべてデモスタック (cargo make demo) で撮影しています。モックバックエンドが 248 台の架空フリートを返すだけで、NATS もエージェントも実機もありません。氏名・ホスト名・サインインアカウントはすべて作り物で、実際の導入環境から取ったものは一枚もありません。
デスクトップは 1440×900、モバイルは 390×901 で撮影しています。
ここでは日本語表示のコンソールを載せています。英語版のページには同じ画面を英語表示で載せています。データはどちらも日本語のままです。フィクスチャが日本企業という設定で、ウィジェット名・グループ名・チェックの説明は運用者が書いたものだからです — 画面の翻訳対象ではありません。
フリートの全体像
ダッシュボードは、運用者が最初に見る数字から始まります。何台が報告しているか、何台が沈黙しているか、ブローカーのストリームが上限内か、直近24時間で何件のジョブが失敗したか。どの数字からも、その内訳のページへ辿れます。

下のピン留めウィジェットは運用者が定義したものです。分析ページで自分で作ったビューを、そのままダッシュボードに固定できます。
エージェント
死活だけを見るページです。どの端末がオンラインか、エージェントのバージョンはいくつか、最後に応答したのはいつか。詳細はインベントリの担当で、ここが答えるのは「居るかどうか」です。

列は選べます。上の画面ではこのフリートに不要な列を隠しています。表示列の選択はそのためのものです。
1台を見る
行を開くと、その端末自身の数字が出ます。エージェントが採取した CPU・メモリ・ディスク・ネットワークを、選んだ期間で表示します。エージェントは自分自身も計測しているので、「端末のせいか、こちらのせいか」にもグラフが答えられます。

端末が知り得ないことを持たせる
端末は自分について見えることしか報告できません。誰の机の上にあるのか、どの部門が費用を持っているのかは、端末には分からないままです。そこで各ホストは、運用者が編集できる自由な key/value のカードを持ちます — 利用者、メール、部署、拠点、資産管理番号、この端末が特別な理由を書いたメモ。

これらは決まったスキーマではなくただのフィールドで、API から書き込めます。ディレクトリ同期のジョブが display_name や department を最新に保てるのはそのためで、誰も手で入力しません。一度入れてしまえば、フリート全体に対して表示・検索できる列になります。
The writes go through the backend API (kanade meta set / rm / clear, or the per-key endpoint behind them), so they are authenticated, role-checked (operator or above) and audited. A directory-sync job that calls kanade meta set therefore needs a KANADE_AUTH_TOKEN even when it runs on the backend host; it touches only its own keys, so hand-entered ones survive.
画面を見る
報告を読むだけでは分からず、実際に見るしかないことがあります。その端末のページから画面を開いて、そのまま見られます。

このために端末側で何かを待ち受けたりはしません。エージェントはすでに外向きの接続を1本持っていて、セッションはその上を通ります。NAT の内側にいる PC も、拠点の回線にいる PC も、自宅の Wi-Fi にいる PC も、隣の席の1台とまったく同じ条件で見られます。端末へ内向きの経路を開くことはありません。ヘッダーには画面サイズと受信タイル数が出ます。更新が止まったセッションと、変化していない画面を映しているだけのセッションは、区別できるべきだからです。
上のデスクトップは合成画像です。デモスタックが描いたもので、実在の端末から取得したものではありません。
インベントリ
マニフェストが集めたものが、そのまま並びます。プローブは YAML ジョブに inventory: を付けた運用者製の PowerShell なので、列は「あなたが集めると決めたもの」です。固定スキーマではありません。行をクリックすると、その端末の全ファクトが開きます。

さらに開くと、その PC が報告したファクトが、プローブごとに1枚のカードで並びます。

何がいつ変わったか
各ファクトカードには履歴タブがあります。インベントリはスナップショットだけではありません。実行のたびに前回と差分を取るので、インストール済みアプリはそれ自身の時系列を持ちます — いつ現れ、どのバージョンを辿り、いつ消えたか。

ここでは何ひとつ「追跡する」と設定していません。プローブはインストール済みアプリの一覧を報告するだけで、追加・削除・変更の行は連続する実行を突き合わせた結果として出てきます。
コンプライアンス
ヘルスチェックのフリート横断ステータスを、チェックごとに件数付きでまとめます。チェックとは check: ヒントを持つジョブにすぎないので、追加するとはマニフェストを書くことです。kanade が検証できる項目のハードコード一覧、というものは存在しません。

肝は詳細列です。その端末がなぜ問題なのかを、その端末自身の言葉で書きます。
サポート期限
同じ仕組みを、設定ではなく日付に向けたものです。このチェックは各ホストの OS ビルドを読み、そのサポート終了日を引き当て、期限までの距離に応じて異常・警告・OK として報告します。今日は問題ないビルドが、誰かが気づこうと思うより何ヶ月も前に、自分から警告し始めます。

稼働状況
電源・セッション・スリープ・アクティブの区間を、Windows イベントログから再構成し、種別ごとのレーンで並べます。レーンの空白は、その種別のイベントが無い区間 — 電源オフ、またはアイドルです。

分析
ジョブが吐いたものを集計します。ここではアプリ利用時間・閲覧履歴・インベントリを出しています。ウィジェットは YAML で定義し、ダッシュボードに固定できます。

タブは決まったレポートの一覧ではありません。ひとつひとつがどれかのマニフェストが宣言した dashboard: の名前なので、その名前を名乗るウィジェットを書けば新しいタブが現れます。次の2つはフィクスチャに入っているものです。


グループ
宣言的なフリートグループです。動的グループはクエリを持ち、バックエンドが一定間隔で再評価するので、メタデータが変われば所属も追随します — エージェントに site と department を一度付ければ、異動した端末は自分で正しいグループに入ります。静的グループはメンバーを直接列挙するもので、クエリでは表せない所属のためにあります。

通知
クライアントアプリに通知を送り、誰が確認したかを追跡します。本文は Markdown(見出し・箇条書き・表・リンク)なので、全社通知を段落ではなく文書として書けます。

誰が確認し、誰が取り消したか
通知を開くと、宛先ごとに、その端末にサインインしていた本人と紐づけて追跡されます。状態は2つではなく3つです — 確認済み、まだ未確認、そして取消済(一度確認したあとに取り消した人)。取消済の行は両方の時刻を保持するので、「全員が読んだ」があとから静かに真に戻ることはありません。

ジョブ
マニフェストそのものを、タグ別にまとめた一覧です。kanade が端末で実行するものは、すべてこのどれかです。

マニフェストそのもの
開くと、運用者が書いた YAML がそのまま出ます。そこから生成したフォームではありません。コメントもブロックスカラーのインデントも往復して残ります。ソースを解析したオブジェクトから再直列化するのではなく、そのまま保存しているからです — 保存したファイルが、そのまま返ってきます。エディタはスキーマを理解しているので、フィールド名にカーソルを合わせると説明が出ます。

Git で管理されているマニフェストは、代わりに読み取り専用で開き、リポジトリ内のパスを示します。SPA 側の編集が正本から乖離することを許さない、ということです。
1回の実行
配信のたびに、その実行自身の結果が残ります。終了コード、ジョブとリクエストへ辿るための各種 ID、そしてホストが出力したそのままの内容です。失敗は赤いバッジ以上の価値があります — この例では、原因になった PowerShell のエラーがそのまま入っています。

監査ログ
すべての配信と、すべての運用者操作を、その裏にあるペイロードごと記録します。実行主体・アクション・対象で絞り込め、ペイロード本文の全文検索もできます。

アカウント
運用者アカウントと、そのページアクセスです。混同しやすい3状態を同時に出しています — 無制限、アカウント個別の許可リスト、そして共有の権限グループ。グループが設定されている場合はグループが優先し、アカウント個別のリストは(あっても)効きません。

自分のアカウント
二要素認証とパスワードは、ひとつのセルフサービスページにまとまっています。管理者を介さずに運用者自身が設定できます。登録はごく普通の TOTP で、任意の認証アプリで QR を読むか、セットアップキーを手入力します。候補の秘密鍵は実際のコードで確認できて初めて保存されるので、途中でやめた登録はアカウントを元のまま残します。

JetStream
ブローカー自身の健康状態です。バックエンドがブートストラップするストリーム・KVバケット・オブジェクトストアを、上限に対する使用量とともに一覧します。

ダークテーマ
既定では OS の設定に追随し、ブラウザごとにライト/ダークへ固定もできます。
![]() | ![]() |
![]() | ![]() |
スマートフォンでは
1024px を下回ると、データテーブルは表であることをやめます。各行がカードになり、値には列名が付きます。横スクロールも、セルを読むためのピンチズームもありません。
![]() | ![]() | ![]() |
クライアントアプリ
利用者の手元に届く側です。端末に常駐する小さなトレイアプリで、本人が見るヘルスタブ、問い合わせずに自分で実行できるジョブ、確認を求められる通知が入っています。同じ端末のエージェントと名前付きパイプで話し、バックエンドへは直接つながりません。
アプリ自身のウィンドウサイズである 880×560 で、日本語のみ掲載しています。クライアントアプリには i18n の層が無く、文字列がソースに直接書かれているため、英語版の画面というものが存在しません。
利用者に見てほしい2つを、開いた直後に出します。

端末ヘルス
運用者がコンプライアンスページで見ているのと同じチェックを、反対側から見たものです。警告には運用者自身が書いた説明と対処が付くので、最初の一手が「問い合わせる」にはなりません。

通知
通知ページから送られ、誰が確認したかまで追跡されます。開くと、運用者が書いた Markdown がそのまま描画されます — 見出し、番号付きの手順、表、引用、リンク。


セルフサービス
運用者が利用者に公開したジョブを、カテゴリ別に並べます。中身はどれも普通のマニフェストで、ここに出るかどうかは運用者が決めるだけです。
![]() | ![]() |
ジョブは実行前に確認を挟めます(マニフェストの confirm: に、運用者自身の文面で)。実行中は出力が流れていくので、沈黙のあと完了だけが現れる、ということになりません。
![]() | ![]() |
カタログは同じ仕組みを、インストールに向けたものです。

自分で動かす
cargo make demo # 運用コンソール
cargo make demo-client # クライアントアプリ
あとは http://localhost:5173 を開き、任意のユーザー名とパスワードでサインインするだけです。他に何も起動しておく必要はありません。モックが何をカバーしているか、どう足すかは crates/kanade-backend/web/demo/README.md にあります。
システムアーキテクチャ
kanade は、数百台の Windows エンドポイントを並行して、安全に、そして非同期で管理するように設計されています。
コンポーネントトポロジー
システムは 5 つの主要コンポーネントで構成されており、イベント駆動型の pub/sub 構造を介して調整されます。
graph TD
subgraph Operator Session
CLI[kanade CLI]
SPA[React SPA]
end
subgraph Server Infrastructure
Backend[kanade-backend]
NATS[NATS Broker / JetStream]
end
subgraph Windows Endpoints
Agent1[kanade-agent PC-1]
Agent2[kanade-agent PC-2]
Client[kanade-client Tauri App]
end
CLI -->|Command / Query API| Backend
SPA -->|REST / WebSockets| Backend
Backend <-->|State/PubSub| NATS
Agent1 <-->|NATS-only Connection| NATS
Agent2 <-->|NATS-only Connection| NATS
Client <-->|Tauri IPC| Agent1
1. kanade-agent
各管理対象ホスト上で動作する高性能な Windows サービスです。
- 役割: コア実行エンジン。
- 通信: アウトバウンド専用の NATS 接続を確立します。インバウンドポートを開かないため、ファイアウォールに優しい設計です。
- 機能: 安全に隔離された PowerShell サブプロセスを起動し、ハードウェア/ソフトウェアのスペック情報の収集、ライブパフォーマンスデータ(CPU、RSSメモリ、ディスクI/O)のストリーミング、およびローカルパッケージの管理を行います。
2. kanade-backend
中央の HTTP API およびプロジェクションサーバーです。
- 役割: コマンドの調整、受信テレメトリの処理、およびオペレーター用 Web インターフェースの提供。
- 状態管理: イベント、アクティビティログ、およびステータスレコードをローカルの SQLite データベースに永続化します。
- プロジェクターパターン: NATS コマンドレスポンスストリームを購読し、受信したペイロードを解析して、リアルタイムで状態テーブルに反映(投影)します。
3. NATS ブローカー (with JetStream)
フリート全体のメッセージ転送レイヤーです。
- 役割: 軽量かつ高スループットなメッセージブローカー。
- JetStream: コマンドストリーム、ジョブ登録、およびファイルストレージ(パッケージやエージェントスクリプトを配信するための NATS オブジェクトストアバケットを使用)を保持します。
- 疎結合: バックエンドとエージェントを切り離します。バックエンドがオフラインまたは再起動中であっても、エージェントは実行を継続して送信トレイにレコードをキャッシュし、接続が再開されたらそれらをプッシュします。
4. kanade-client
エンドポイント上のログインユーザーのデスクトップセッションで動作する、オプションの Tauri デスクトップアプリケーションです。
- 役割: エンドユーザーとの対話(プロンプトダイアログ、通知、ユーザー向けダッシュボードなど)を提供します。
- 通信: セキュリティで保護されたローカル IPC メカニズムを介して、ローカルの
kanade-agentと状態を共有します。
5. kanade CLI
オペレーター向けの主要なコマンドラインツールです。
- 役割: ソフトウェアアップデートのパッケージングと公開、ジョブマニフェストの送信と実行、およびコマンドラインからのライブフリートインベントリの照会。
セキュリティと信頼性の設計
アウトバウンド専用接続
エージェントは、アウトバウンド TCP 接続を開始することによってのみ NATS ブローカーと通信します。エンドポイント側でファイアウォールポートを開く必要がないため、横移動や外部からのポートスキャンのリスクを排除できます。
エージェントジョブのサンドボックス化
スクリプトを実行する際、エージェントは %ProgramData%\Kanade\agent-scripts 内にコマンドを配置(ステージング)し、カスタマイズされたランチャテンプレートを使用して実行します。管理者はジョブマニフェストを介して実行アイデンティティの設定を強制でき、run_as: system(昇格したシステム管理用)または run_as: user(制限されたディレクトリ ACL を使用して、アクティブなユーザーの資格情報の下で安全に実行する用)を指定できます。
運用概要
kanade の day-2 運用は 2 つのフローに分かれます:
-
直接インストール — まっさらなホストにバイナリと config を置いて Windows サービスを登録します。最初の agent、最初の backend、NATS サーバーをブートストラップするときに使います。スクリプト:
scripts/deploy/agent.ps1、scripts/deploy/backend.ps1、scripts/deploy/nats.ps1。対象ホスト上で手動で実行します。 -
agent 経由のアップデート — agent がいったん動き出せば、その agent 自身が ssh / RDP なしで同じホスト上の他コンポーネントをインストール・更新できます。operator はバイナリとスクリプト本体をブローカーに publish し、ジョブを起動。agent が fetch・検証・swap・サービス再起動を行います。day-2 運用の大半はこれ。
agent 経由フローの形は、何をアップデートする場合でも同じです:
operator host ─► kanade CLI ─► NATS broker ─► agent (on target host)
│ │
├── publish binary ────────────► fetches from
│ to OBJECT_APP_PACKAGES OBJECT_APP_PACKAGES
├── publish script ────────────► fetches from
│ to OBJECT_SCRIPTS OBJECT_SCRIPTS
├── register / update job ─────► reads job manifest
│ from `jobs` KV
└── exec job ──────────────────► PowerShell child
runs the script
コンポーネント別ガイド:
- kanade-backend — HTTP / projector バイナリ
- kanade-client — Tauri 製のエンドユーザーアプリ
- NATS サーバー — ブローカー本体 (はい、ブローカー越しにブローカーをアップデートできます)
- kanade-agent 自身 — agent の self-update 経路 (他 3 つと違い、汎用の OBJECT_APP_PACKAGES + script ペアではなく専用の rollout バケットを使います)
インストールとデプロイ
このセクションでは、本番環境またはステージング環境で kanade コンポーネントをネイティブの Windows サービスとしてブートストラップする方法を詳しく説明します。
デプロイモデル
本番ホストと管理対象エンドポイントは、kanade コンポーネントをバックグラウンドの Windows サービスとして実行します。これにより、高可用性と自動起動が保証されます。
| サービス名 | トリプル / バイナリ | 設定ソース | 典型的なターゲット |
|---|---|---|---|
| KanadeNats | nats-server.exe | 保護されたレジストリ / レジストリ保存の CLI フラグ | 中央サーバー |
| KanadeBackend | kanade-backend.exe | 保護されたレジストリ / 設定ファイル | 中央サーバー |
| KanadeAgent | kanade-agent.exe | 保護されたレジストリ / ローカル状態 DB | 管理対象エンドポイント |
1. 前提条件
- ホスト OS: Windows 10/11 または Windows Server 2016+。
- gsudo: 標準のユーザシェルから管理者権限でインストールを実行する(または管理者権限の PowerShell プロンプトからコマンドを実行する)ために必要です。
- ネットワークルーティング: 管理対象エンドポイントが TCP 経由で NATS サーバーポート(デフォルト
4222)に到達できる必要があります。
2. NATS サーバー (ブローカー) のセットアップ
NATS サーバーはメッセージングのコアとして機能します。
scripts/build-release.ps1 -Roles natsを使用して、デプロイメントバンドルをステージングします。- 管理者権限でサービスをデプロイします:
# 管理者権限の PowerShell プロンプト & "dist\nats\deploy-nats.ps1" -NatsToken "your-secure-nats-token" -Recreate
これにより、KanadeNats サービスがインストールされ、ローカルシステムアカウントで実行されるよう構成され、JetStream データディレクトリがセットアップされ、安全な認証トークンが Windows レジストリ内にロックダウンされます。
3. バックエンド API & SPA のデプロイ
バックエンドは、オペレーター接続を管理し、イベントログを処理します。
scripts/build-release.ps1 -Roles backendを使用して、バックエンドバイナリと React SPA バンドルをステージングします。- サービスをデプロイします:
# 管理者権限の PowerShell プロンプト & "dist\backend\deploy-backend.ps1" ` -NatsToken "your-secure-nats-token" ` -StaticToken "your-operator-spa-bearer-token" ` -ForceConfig -Recreate
-NatsToken: バックエンドをローカルの NATS サーバーに安全に接続します。-StaticToken: オペレーターの CLI/SPA ログインに必要な API ベアラートークンを定義します。
デプロイスクリプトは、KanadeBackend Windows サービスを登録し、適切な ACL を設定して、エンドポイントを検証します。
4. 対象エンドポイントへのエージェントのインストール
管理対象のすべてのエンドポイント PC にエージェントをインストールします。
scripts/build-release.ps1 -Roles agentを使用して、エージェントバンドルをステージングします。dist/agentフォルダの内容を対象 PC にコピーします。- 対象 PC 上で、インストーラーを実行します:
# 管理者権限の PowerShell プロンプト & ".\deploy-agent.ps1" -NatsToken "your-secure-nats-token" -ForceConfig -Recreate
スクリプトの動作:
kanade-agent.exeをインストール先ディレクトリに配置します。- Windows レジストリパス (
HKLM:\SOFTWARE\Kanade\agent) 内の設定と NATS トークンを保護します。 - KanadeAgent サービスを登録して起動します。
サービスが起動すると、エージェントはアウトバウンド NATS 接続を確立し、コマンドストリームを購読し、オンラインのハートビートをフリートバックエンドに報告します。
agent 経由のアップデート
agent は万能インストーラーです。対象ホストで動き出してしまえば、他のどのコンポーネントをアップデートするときも operator がホストに直接触る必要はありません — agent が話している backend も、メッセージを運ぶブローカーも、agent 自身も対象です。
この章はコンポーネントごとに 1 ページずつあります:
全コンポーネントで共通の仕組み:
| バケット / ストリーム | 用途 |
|---|---|
OBJECT_APP_PACKAGES | 汎用バイナリストレージ (backend、client、NATS サーバーなど)。キーは <name>/<version>。 |
OBJECT_SCRIPTS | マニフェストが script_object で参照する PowerShell スクリプト本体。キーは <name>/<version>。 |
OBJECT_AGENT_RELEASES | agent バイナリ専用。agent の rollout 専用の watcher / target_version フローを持つので APP_PACKAGES とは別バケットになっています。 |
agent_config (KV) | レイヤード config — global / グループ別 / PC 別。target_version はここに置かれます。 |
jobs (KV) | ジョブカタログ。各エントリは operator が exec できるマニフェストです。 |
CLI コマンド一覧:
kanade app, kanade script and kanade agent (publish / rollout / current / logs) talk to the backend HTTP API, not to NATS: they need KANADE_AUTH_TOKEN (see kanade login) for an account with the operator role, and no broker token. Publishes and deletes are audited against that account by the backend. Operators who previously relied on the NATS token alone must now export KANADE_AUTH_TOKEN; without it the backend answers 401 / 403. kanade agent publish is capped by the backend at 64 MB for the whole upload (a normal agent binary is well under that). app publish additionally downloads the package back from the backend and checks its digest before reporting success.
| コマンド | 動作 |
|---|---|
kanade app publish <name> <file> [--version <version>] | Upload to OBJECT_APP_PACKAGES through the backend API. |
kanade script publish <name> <version> <file> | Upload to OBJECT_SCRIPTS through the backend API. |
kanade job create <yaml> | jobs KV にジョブマニフェストを upsert。 |
kanade exec <job-id> --pcs <pc> [--pcs <pc> …] | 登録済みジョブを PC 群に対して起動。 |
kanade agent publish <file> [--version <version>] | Upload an agent binary through the backend API (version extracted from PE VERSIONINFO; --version for a Linux / macOS binary). |
kanade agent rollout <version> --pc \| --group \| --global | Flip target_version on the chosen scope; agents pick it up via their self-update watcher. Goes through the backend API. |
kanade agent current | Print the global target_version (group / pc overlays are not shown; use kanade config get --group/--pc). |
kanade agent logs <pc_id> [--tail <n>] | Tail an online agent's log via the backend API. |
kanade-backend のアップデート
backend は管理対象ホスト 1 台 (またはそれ以上) で Windows サービスとして動いています。agent 経由のアップデートとは、そのホスト上で稼働する agent がサービスを停止し、バイナリを差し替えて再起動することを指します。これにより、operator は対象ホストに一度もログインする必要がありません。
end-to-end フロー
┌── operator host ──────────────────────────────────────────┐
│ 1. build kanade-backend.exe │
│ 2. kanade app publish kanade-backend <v> <exe> │
│ 3. edit deploy-backend.ps1 (set $AgentSource* knobs) │
│ 4. kanade script publish deploy-backend <v> <edited.ps1> │
│ 5. kanade job create install-kanade-backend.yaml │
│ 6. kanade exec install-kanade-backend --pcs <host> │
└────────────────────────────────────────────────────────────┘
│
▼
┌── target host (running kanade-agent as LocalSystem) ──────┐
│ • agent receives the Command on commands.pc.<host> │
│ • fetches deploy-backend.ps1 from OBJECT_SCRIPTS │
│ sha-verifies it (`script_object` machinery, #214) │
│ • stages it under │
│ C:\ProgramData\Kanade\agent-scripts\<UUID>\ │
│ kanade-<UUID>.ps1 │
│ • runs `powershell -File <launcher>` (PR #230 fix) │
│ • launcher invokes the user script via `& '...'` │
│ so [CmdletBinding()] / param() headers parse │
│ • script downloads kanade-backend.exe from │
│ OBJECT_APP_PACKAGES (via /api/app-packages/…) │
│ sha-verifies it (separate hash, on the exe itself) │
│ • Stop-Service KanadeBackend │
│ • copy exe over C:\Program Files\Kanade\… │
│ • Start-Service KanadeBackend │
│ • exit 0 — result published to NATS │
└────────────────────────────────────────────────────────────┘
sha チェックが 2 段ある理由は意図的: スクリプト本体のハッシュは agent が実行前に検証 (スクリプトの完全性)、バイナリのハッシュはスクリプトが swap 前に検証 (バイナリの完全性、operator が $AgentSourceSha256 で指定)。
ステップごとの手順
1. kanade-backend をビルド
cargo build --release -p kanade-backend
出力: target/release/kanade-backend.exe。
2. バイナリを publish
kanade app publish kanade-backend 0.43.0 target/release/kanade-backend.exe
これでバイナリが OBJECT_APP_PACKAGES/kanade-backend/0.43.0 にアップロードされ、sha-256 digest が表示されます。digest を控えておきましょう — 後でスクリプトに lowercase-hex 形式で書き込みます。
3. scripts/deploy/backend.ps1 を編集
ローカルコピーを作って、先頭の 4 つの $Agent* ノブをセットします:
$AgentSourceUrl = 'http://kanade-backend.example.com:8080'
$AgentSourceVersion = '0.43.0'
$AgentSourceSha256 = '<kanade-backend.exe の lowercase hex>'
$AgentSourceAuthToken = '<backend HTTP API 用 bearer>'
スクリプトの他の部分は触らないでください。このノブによって「agent モード」(backend からダウンロード) と「手動インストールモード」(ローカルフォルダからの読み込み) が切り替わります。
$AgentSourceSha256はGet-FileHash kanade-backend.exe -Algorithm SHA256の hex 形式の値です。
kanade app publishの出力にある base64url 形式しか手元にない場合は decode します。CLI から出る base64 は URL-safe で padding なしのことがあるので、FromBase64Stringに通す前に padding を補う必要があります:$b64 = '<paste the SHA-256= value here, without the SHA-256= prefix>' $b64 = $b64.Replace('-', '+').Replace('_', '/') if ($b64.Length % 4) { $b64 += '=' * (4 - $b64.Length % 4) } [BitConverter]::ToString([Convert]::FromBase64String($b64)).Replace('-', '').ToLowerInvariant()Python の場合:
python -c "import base64; print(base64.urlsafe_b64decode('<b64>' + '=' * (-len('<b64>') % 4)).hex())"
$AgentSourceAuthTokenは 2026-05-26 の live test 以降必須です。backend の/api/app-packages/<name>/<ver>エンドポイントは token なしだと HTTP 401 を返します。auth なしの lab 環境のときだけ空のままで問題ありません。
4. 編集したスクリプトを publish
kanade script publish deploy-backend 0.43.0 .\deploy-backend.edited.ps1
アップロード先は OBJECT_SCRIPTS/deploy-backend/0.43.0。
5. ジョブを登録 / 更新
リポジトリの configs/jobs/installers/install-kanade-backend.yaml がテンプレートです。version: と script_object: をいま publish したバージョンに合わせて編集してから upsert:
id: install-kanade-backend
version: 0.43.0
execute:
shell: powershell
script_object: deploy-backend/0.43.0
timeout: 300s
run_as: system
require_approval: true
kanade job create jobs\install-kanade-backend.yaml
run_as: systemは必須です:Stop-Service/Start-Service/sc.exeはいずれも admin が必要です。本番環境では agent は LocalSystem で動いているので問題ありません。
6. 起動
kanade exec install-kanade-backend --pcs <backend-host>
CLI はすぐに exec_id を返します。実際のインストールは対象ホスト上で非同期に走ります。
7. 確認
backend の results エンドポイントを叩く (もしくは SPA の Activity ビューを見る):
curl -H "Authorization: Bearer <token>" `
"http://<backend>/api/results?limit=5"
自分の exec_id が exit_code: 0 で、stdout が kanade-backend <new-version> で終わっていれば成功です。
ありがちなトラブル
| 症状 | 原因 | 対処 |
|---|---|---|
stderr に [CmdletBinding()] / param() の parse error | agent が 0.42.2 より古い (-Command モードで動いている) | まず kanade agent rollout で agent をアップグレード (agent self-update 参照)。 |
Start-BitsTransfer : HTTP status 401 | $AgentSourceAuthToken が空だが backend が auth を要求している | 値をセットする。 |
Start-BitsTransfer : The transfer encountered an error / ジョブ状態が TransientError | BITS サービスが停止しているか、対象マシンの WinHTTP から $AgentSourceUrl に到達できない | Get-Service BITS で確認。WinHTTP のプロキシは netsh winhttp show proxy で確認します (BITS が使うのは WinHTTP であって IE/WinINet ではありません)。 |
sha256 mismatch — expected=<x> actual=<y> | スクリプトの hash が publish されたバイナリと一致しない | 再 publish するか hash を計算し直す。スクリプトは swap の 前 に abort するので、既存のインストールは無事です。 |
| ジョブは実行されたが kanade-backend が起動してこない | 対象ホストでのサービス起動失敗 / config の不整合 | 対象ホストの C:\ProgramData\Kanade\log\backend.*.log を読む。agent 経由で kanade logs <pc> で取れる (実装後) か、直接ファイルを引き上げる。 |
kanade-client のアップデート
Tauri デスクトップクライアントは backend と同じやり方で端末に配布します: バイナリは OBJECT_APP_PACKAGES、スクリプトは OBJECT_SCRIPTS、ジョブは jobs KV。形は backend のアップデート と同じで、スクリプト内容とパッケージ名だけが違います。
backend のアップデートとの違い
| 項目 | kanade-backend | kanade-client |
|---|---|---|
| (再) 起動するサービス | KanadeBackend (Windows サービス) | なし — クライアントはユーザーが起動する |
| インストール先 | %ProgramFiles%\Kanade\kanade-backend.exe | %ProgramFiles%\Kanade\kanade-client.exe |
| リポジトリ内のスクリプト | scripts/deploy/backend.ps1 | configs/jobs/installers/scripts/install-kanade-client.ps1 (マニフェストの script_file パス) |
| マニフェストの参照方法 | script_object: deploy-backend/<v> | script_file: scripts/install-kanade-client.ps1 (マニフェスト YAML からの相対パス; kanade job create 時に inline 展開) |
| atomic swap のやり方 | サービス停止 → コピー → サービス起動 | <exe>.new にステージ → Move-Item → <exe>.old を削除 |
| インベントリへの射影 | なし (backend は自分自身でバージョンを報告) | inventory: ブロックが PC ごとのクライアントバージョンを SPA Inventory ページに出す |
両方の形が使えます —
script_object(OBJECT_SCRIPTS から hash で参照、agent が必要時に fetch) とscript_file(kanade job create時にスクリプト本体をマニフェストに inline 展開)。client マニフェストは歴史的経緯で どちらの形式もサポートされています —script_object(OBJECT_SCRIPTS からハッシュで参照、agent が必要に応じて取得) とscript_file(kanade job create時にスクリプト本体をマニフェストにインライン展開)。クライアントマニフェストは歴史的経緯でscript_fileを使用し、backend マニフェストは Object Store パスのテストのためにscript_objectへ書き換えられました。
ステップごとの手順
1. kanade-client をビルド
cargo build --release -p kanade-client
出力: target/release/kanade-client.exe。
2. バイナリを publish
kanade app publish kanade-client 0.42.0 target/release/kanade-client.exe
3. configs/jobs/installers/scripts/install-kanade-client.ps1 を編集
先頭の 3 つのノブをセット:
$BackendBase = 'http://kanade-backend.example.com:8080'
$Version = '0.42.0'
$ExpectedSha256 = '<kanade-client.exe の lowercase hex>'
backend auth が有効なら
$ClientSourceAuthTokenを backend の bearer にセットしてください(agent が/api/*に使うのと同じトークンです)。/api/app-packages/kanade-client/<v>ルートが認証なしの開発環境やスモークテスト環境では空のままで問題ありません。scripts/deploy/backend.ps1 の$AgentSourceAuthTokenノブと同じ構成です。
4. ジョブを登録 / 更新
configs/jobs/installers/install-kanade-client.yaml:
id: install-kanade-client
version: 0.42.0
execute:
shell: powershell
script_file: scripts/install-kanade-client.ps1 # `job create` 時に inline 展開 (マニフェスト YAML からの相対パス)
timeout: 180s
run_as: system
require_approval: true
inventory:
display:
- { field: version, label: Version }
- { field: path, label: Install path }
summary:
- { field: version, label: Client version }
kanade job create jobs\install-kanade-client.yaml
inventory:ブロックは projector に「スクリプトの stdout は単一の JSON blob で、version/pathフィールドが SPA の Inventory ページに反映される」と伝えます。operator は fleet 全体のテーブルから取り残し端末を見つけられ、ssh は不要です。
5. 起動
kanade exec install-kanade-client --pcs <host> [--pcs <host> …]
もしくはグループ単位で:
kanade exec install-kanade-client --groups office
6. SPA で確認
SPA の Inventory ページを開く (もしくは /api/inventory?app=kanade-client を叩く) と、対象ホストが新しいバージョンを報告しているはずです。
NATS サーバーのアップデート
管理対象ホスト上のブローカーをアップデートするのは最も特徴的なケースです。なぜなら agent は ブローカー越しにブローカーと話す ためです。NATS を停止するとジョブの途中で agent との接続が切断されます。これに対しては、2 つの仕組みが組み合わさって対処しています:
- 再接続。 agent の NATS クライアントはブローカー再起動時に自動再接続します。人の介入は不要。
- outbox。 ブローカーが落ちている間に発生したジョブ結果は
%ProgramData%\Kanade\outbox\に貯められ、接続が戻り次第再送されます。新しい NATS サーバーが立ち上がるとすぐに result row が backend に届きます。
したがってフローは backend のアップデート と同じです。スクリプトはサービスを停止してバイナリを差し替え再起動し、agent はブローカーの切断期間を透過的に許容します。
NATS アップデート固有の注意点
| 懸念 | 実際 |
|---|---|
| result row はロストする? | いいえ — outbox がブローカー停止中を跨いで保持し、再接続時に drain します。 |
| SPA からアップデートできる? | はい、他のジョブと同じです — kanade exec install-kanade-nats --pcs <broker-host>。 |
| NATS が再起動してこなかったら? | result は outbox にずっと残ります。operator はブローカーホストの outbox/ を復帰失敗の早期サインとして監視することが推奨されます。 |
| 新しい NATS バージョンに互換性がなかったら (JetStream のアップグレードなど)? | まず canary 1 台に rollout (--pcs <one-broker>)、outbox と backend の健全性を見てから fleet 全体に展開。SPA クエリには 5 分の cache TTL があるので canary の状態は数分以内に反映されます。 |
手動インストール (ブートストラップ)
初回インストール (ブローカーホストにまだ agent がない状態) では直接ワークフローを使います:
.\scripts\build-release.ps1 -Roles nats # fetches nats-server.exe
# from github.com/nats-io/nats-server/releases
.\scripts\deploy\nats.ps1 -NatsToken '<token>'
これで nats-server.exe を %ProgramFiles%\Kanade\ に、nats-server.conf を %ProgramData%\Kanade\config\ にインストールし (bearer token を平文で保存するため ACL は SYSTEM + Administrators に絞ります)、KanadeNats Windows サービスを登録し、TCP 4222 (broker) を開放し、サービスを起動します。
monitoring (8222) は意図的に開放しません。古いデプロイが作った規則があれば削除します。このエンドポイントには独自の認証が無く、リポジトリ内の利用側はすべてループバック経由で読むためです。同梱の nats-server.conf もそれに合わせて 127.0.0.1 にバインドしています。
agent 経由のアップデート (定常運用)
scripts/deploy/nats.ps1 には $AgentSource* のノブがあります (#234)。ブローカー自身もフリート経由で更新できるので、ブローカーのホストに RDP する必要はありません。
1. nats-server.exe をビルド or 取得
どちらか:
.\scripts\build-release.ps1 -Roles nats # fetches the binary
…もしくは github.com/nats-io/nats-server/releases から直接ダウンロード。
2. バイナリを publish
kanade app publish nats-server 2.10.20 .\nats-server.exe
3. deploy-nats.ps1 を編集
パターンは deploy/backend.ps1 と同じです:
$AgentSourceUrl = 'http://kanade-backend.example.com:8080'
$AgentSourceVersion = '2.10.20'
$AgentSourceSha256 = '<nats-server.exe の lowercase hex>'
$AgentSourceAuthToken = '<backend HTTP API 用 bearer>'
4. publish + 登録 + exec
kanade script publish deploy-nats 2.10.20 .\deploy-nats.edited.ps1
kanade job create jobs\install-kanade-nats.yaml
kanade exec install-kanade-nats --pcs <broker-host>
ジョブマニフェストは configs/jobs/installers/install-kanade-nats.yaml にあります:
id: install-kanade-nats
version: 2.10.20
execute:
shell: powershell
script_object: deploy-nats/2.10.20
timeout: 300s
run_as: system
require_approval: true
5. 確認
ブローカーが復帰すると outbox が drain され、/api/results に result row が出てきます。新しい NATS バージョンはブローカーの monitoring エンドポイントで確認しますが、monitoring はループバックにバインドされ firewall でも開放していないので、ブローカーのホスト上で実行してください:
curl http://127.0.0.1:8222/varz | python -m json.tool | rg version
それ以外の場所からは、ポートではなくフリートに尋ねます — いま更新を実行したのと同じ agent-exec の経路です:
kanade exec collect-broker-health --pcs <broker-host>
なぜ独立した「broker update」機構が要らないのか
初期の設計では「ブローカー越しにブローカーを更新する鶏卵問題」を避けるために専用のブートストラップチャネル (agent が broker update 専用に使う並列 NATS 接続) を検討していました。outbox + 再接続のペアによってこれは不要に: 結果は「失われる」のではなく「遅延するだけ」。トランスポートはひとつ、メンタルモデルもひとつで済みます。
kanade-agent 自身のアップデート
agent の self-update は唯一 OBJECT_APP_PACKAGES + script_object ジョブを 使わない コンポーネントです。agent は ssh なしで現在実行中の自分自身のバイナリを差し替える必要があり、汎用 install ジョブよりタイトなループのため専用の仕組みを持っています。
仕組み
| バケット / キー | 用途 |
|---|---|
OBJECT_AGENT_RELEASES | agent バイナリ。キーは <version>。rollout watcher が agent アップデートだけに反応するように、OBJECT_APP_PACKAGES とは別バケット。 |
agent_config.<scope>.target_version | 各スコープ (global / グループ / pc) があるべきバージョン。agent の self_update ループがこれを watch しています。 |
フロー:
1. agent.self_update watches agent_config for target_version
2. If target_version != my agent_version:
a. Pull `OBJECT_AGENT_RELEASES/<target_version>` to <exe>.new
b. Sha-verify against the bucket's recorded digest
c. Atomic swap: <exe> ← <exe>.new (via SCM stop/start)
d. New binary boots, watcher arms again, loop closes
rollout watcher は cold broker (ホスト再起動後に agent と broker が同時に立ち上がる、など) に耐えなければなりません。#226 以前は最初の get_object_store 呼び出しで Err(_) => return; してしまうと watcher が恒久的に死んでいて、その起動セッション中は agent が二度と self-update しませんでした。#226 以降は watcher が backoff で再試行し、broker に到達できるまであきらめません。
ステップごとの手順
1. agent をビルド
cargo build --release -p kanade-agent
出力: target/release/kanade-agent.exe。
2. publish
kanade agent publish target/release/kanade-agent.exe
The CLI extracts the version from the PE VERSIONINFO resource — no --version flag, no chance of a label / binary mismatch. It then streams the file to the backend API, so KANADE_AUTH_TOKEN (see kanade login) for an account with the operator role is required; no NATS broker token is involved, and the backend records the publish against that account. The backend caps the whole upload at 64 MB. A Linux ELF or macOS Mach-O carries no VERSIONINFO, so pass --version for those.
3. roll out
スコープを選びます。canary 1 台から:
kanade agent rollout 0.42.2 --pcs canary-01
ping で確認:
kanade ping canary-01 # agent_version should flip to 0.42.2
# within a few seconds
問題なければ範囲を広げる:
kanade agent rollout 0.42.2 --groups office --jitter 5m
# or fleet-wide
kanade agent rollout 0.42.2 --global --jitter 30m
--jitterは実際の swap タイミングをある幅で分散させ、大きな fan-out 時に全ホストの OS サービスマネージャを同時に叩かないようにします。100 台以上の fleet では推奨。
4. 確認
kanade agent current
# → global.target_version = 0.42.2
あとは SPA の Agents ページ (もしくは /api/agents) で fleet 全体をスポットチェック: agent_version 列は jitter + 約 30 秒の heartbeat 間隔以内に新バージョンに収束するはず。
ありがちなトラブル
| 症状 | 原因 | 対処 |
|---|---|---|
kanade agent rollout が "version not in OBJECT_AGENT_RELEASES" と言う | typo もしくはスコープ違い | kanade agent current と kanade jetstream object list agent_releases で再確認。 |
数分経っても kanade ping <host> が古いバージョンを返す | agent が self-update しなかった — watcher が死んでいる (#226 以前の agent) か、ホストが broker に到達できない | 対象ホストの %ProgramData%\Kanade\log\agent.*.log をチェック。self_update が無言なら (「checking target_version」ログがない)、agent が古すぎる; deploy-agent.ps1 で手動ブートストラップ。 |
agent が flap する: 起動するも exit_code: 1 ですぐ落ちる | このホストでは新バイナリが起動できない (config の不整合、依存欠落など)。SCM の failure-actions により再起動が試みられるが再びクラッシュする — Event Viewer に Service Control Manager のエラーが連続して記録される | ロールバック: kanade agent rollout <prev-version> --pcs <host>。次の watcher tick でホストは元のバージョンに戻ります。 |
なぜ別のバケット / スコープか?
OBJECT_APP_PACKAGES は <name>/<version> をキーにした汎用 blob ストアです。agent rollout パターンに必要なのは:
- agent の変更だけに反応する watcher (たくさんの名前が入ったバケットを poll するのではなく、特定の 1 つの KV キーを安価に watch)。
- 「既知の全バージョン」ではなくスコープごとの「現在の target」セマンティクス —
agent_config.<scope>.target_versionこそが「自分は何を動かすべきか」の答えで、agent 側で列挙する必要がない。 - operator 向け UX (
kanade agent publish/rollout) がkanade app publishとは別サブコマンドツリーに分けるに足るくらい違う。
というわけで agent は OBJECT_AGENT_RELEASES + レイヤード config KV を、他のコンポーネントは OBJECT_APP_PACKAGES + アプリ別ジョブを共有します。
kanade をホストから取り除く (undeploy)
本番でのロールバック経路。rollout で何かが壊れた、ホストを廃止する、再インストール用にまっさらな状態に戻したい — そんなときホストから kanade を剥がすために、deploy と対になる undeploy スクリプトをコンポーネントごとに 1 本ずつ用意しています。
| 構成要素 | Deploy | Undeploy |
|---|---|---|
| Agent | scripts/deploy/agent.ps1 | scripts/undeploy/agent.ps1 |
| Backend | scripts/deploy/backend.ps1 | scripts/undeploy/backend.ps1 |
| NATS server | scripts/deploy/nats.ps1 | scripts/undeploy/nats.ps1 |
| Client (Tauri) | configs/jobs/installers/scripts/install-kanade-client.ps1 (エージェント経由) | scripts/undeploy/client.ps1 |
4本とも管理者専用かつ冪等です。アンインストールが途中で終わったあとに再実行しても安全ですし、対象がすでに無い状態で実行しても安全です (各ステップが「not present, skipping」と記録して次に進みます)。
デフォルトの姿勢: 安全側
フラグなしで実行するとスクリプトは:
- Windows サービスを停止します。
- SCM からサービスを unregister します (エントリが実際に消えるまで待つので、後続の再 deploy が pending な削除と race しません)。
%ProgramFiles%\Kanade\からインストール済みバイナリを削除します。中途半端な<exe>.new/<exe>.oldの swap 残骸もまとめて削除。- deploy スクリプトが作成した inbound のファイアウォールルールを削除します (
-KeepFirewallで skip 可 — 外部の WAF / グループポリシーがルールを管理している場合に有用)。 %ProgramData%\Kanade\配下 (config、log、JetStream データ、SQLite DB、…) は 残します。フォレンジック / rollback / 再 deploy が state を失わずに進められるように。HKLM:\SOFTWARE\kanade\<role>\*のレジストリ secret も 残します。
よくあるケース —「この端末の kanade がおかしいので、状態を壊さずに外したい」— にはこれで十分です。
-Purge: 破壊的クリーンアップ
追加で:
- そのコンポーネント固有の
%ProgramData%\Kanade\配下エントリを削除します。重要なのは そのコンポーネント自身のファイルだけ — agent / backend / NATS は同じ root を共有しているので、各スクリプトは他のコンポーネントのファイルには触れません。 - 対応する
HKLM:\SOFTWARE\kanade\<role>\*キーを削除します (-KeepSecretsを併せて渡すとスキップ — 複数コンポーネントで同じ bearer を共有しているときに有用)。
| 構成要素 | -Purge が削除するもの |
|---|---|
| Agent | config\agent.toml、logs\agent.*.log、outbox\、HKLM:\SOFTWARE\kanade\agent\ |
| Backend | config\backend.toml、data\*.db* (SQLite — 過去の results / inventory が消える)、logs\backend.*.log、HKLM:\SOFTWARE\kanade\backend\ |
| NATS | config\nats-server.conf、nats\ (JetStream — KV / Object Store / streams すべて消える)、logs\nats*.log |
| Client | 追加なし (per-user な state はまだ存在しない) |
⚠️ 危険なのは
undeploy-nats.ps1 -Purgeとundeploy-backend.ps1 -Purge。前者は fleet 全体の JetStream state (agent_releases、app_packages、scripts、jobs、agent_config、results stream) を消し、後者は projector の過去の SQLite を消します。どちらも out-of-band バックアップ無しではリカバリ不能。スクリプトは実行前に目立つバナーを出します。
ロールバックの定石
canary 1 台で rollout が壊れたとき
# On the canary, as Admin:
.\scripts\undeploy\agent.ps1 # safe default
# kanade is now off the host. Re-deploy when ready:
.\scripts\deploy\agent.ps1 -SourceDir C:\path\to\prev-version
ホストを恒久的に廃止する
.\scripts\undeploy\agent.ps1 -Purge
dev box を再インストール用にまっさらにする
.\scripts\undeploy\agent.ps1 -Purge
.\scripts\undeploy\backend.ps1 -Purge # ⚠️ SQLite gone
.\scripts\undeploy\nats.ps1 -Purge # ⚠️ JetStream gone
.\scripts\undeploy\client.ps1
# Now nothing about kanade exists on the box.
state は触らず壊れたサービスだけ作り直す
.\scripts\undeploy\backend.ps1 # safe default: SQLite intact
.\scripts\deploy\backend.ps1 -Recreate # fresh service registration, same data
undeploy がやらないこと
- この端末が居なくなったことをフリートの他の部分に通知はしません。バックエンドはハートビートが時効になるまで (
/api/agentsの staleness 閾値) 「エージェント」に載せ続けます。SPA から即座に消したい場合は、undeploy のあとバックエンド API で行を削除してください。 - デプロイ済みバイナリを以前のバージョンへ戻すことはしません。このスクリプトの語彙では「ロールバック」は「完全に削除する」という意味です。古いバージョンへ入れ替えたい場合は、そのバイナリが入ったフォルダに対して対応する
deploy-*.ps1を実行し直してください。 - It doesn't touch NATS-side state when you remove the agent — the agent's
target_versionentry underagent_config.pcs.<pc>stays in the KV. Clean those up server-side withnats kv del agent_config pcs.<pc>.target_version(using an administrative broker credential) if needed.
ブローカーのサイジングとスケーリング
kanade は単一の NATS + JetStream ブローカーの上で動きます。フリートが大きくなる (数百台 → 数千台) と、スケーリングのボトルネックになるのはエージェントではなくブローカーです。このページでは、エージェント1台あたりのフットプリント、#512 の作業で何が変わったか、単一ノードで注意すべき限界、そしてスケールアップ中に実測値を採取して起動スプレイを入れるかブローカーを増強/クラスタ化するかを判断する方法を扱います。
エージェント1台あたりの consumer フットプリント
稼働中のエージェントはそれぞれ JetStream の consumer をいくつか保持します。KV watch ごとに ordered push consumer が1つ、それに加えてコマンド再生用の durable consumer が1つです:
| Consumer | 由来 |
|---|---|
agent_config watch (キーで絞り込み) | config_supervisor |
agent_groups watch (所属 → 実効 config) | config_supervisor |
agent_groups watch (所属 → 購読) | groups.rs |
schedules watch | local_scheduler |
jobs watch | local_scheduler |
fleet_config の freeze watch (単一キー) | local_scheduler |
EXEC durable replay | command_replay |
つまりエージェント1台あたり約7 consumerです。この数はエージェントごとにほぼ固定で、キーで絞り込んでも減りません。したがってブローカー側の総数はフリートに比例して増えます:
| フリート規模 | ブローカー上の consumer 数 (概算) |
|---|---|
| 15 | 約 105 |
| 500 | 約 3,500 |
| 3,000 | 約 21,000 |
v0.43.96 で変わったこと (と変わらなかったこと)
#512 (v0.43.96 で出荷) が叩いたのは超線形のコストであって、consumer の数ではありません:
#832—agent_configのキー絞り込み watch。 エージェントはバケット全体 (watch_all) ではなく、global、pcs.<self>、自分のgroups.<g>だけを watch します。これで2つの爆発が消えます:- PC 単位の書き込みファンアウト:
pcs.<id>1件への書き込みが全 N エージェントに届かなくなりました (以前は届いていて、N−1 台が分類しては捨てていました)。 - 再接続時の再同期ストーム: 再同期はバケット全体に対する
keys()+ キーごとの走査ではなく、エージェントあたり 3〜5 回の直接getになりました。総計で O(N²) → O(N) です。
- PC 単位の書き込みファンアウト:
#839— 単一キーの freeze watch。fleet_configはKEY_FREEZEしか持たないので、freeze の watcher はwatch_all+ クライアント側フィルタではなくwatch(KEY_FREEZE)を使います。
変わっていないのは、エージェント1台あたり約7 consumer というフットプリントと、schedules / jobs の watch_all です (これらは全エージェントがターゲティング判定のために評価する共有カタログで、無くすにはサーバー側ターゲティングという別の変更が要ります)。したがって 3,000 台では依然として約 21,000 consumer と、同期した再接続による接続/consumer 作成のバーストを見込んでおく必要があります。
単一ノードの限界と再接続の群れ
単一ノードの JetStream でサイジングすべきものは2つです:
-
定常状態のフットプリント — 3,000 台での約 21,000 consumer は JetStream のメタ (Raft) 層に載り、メモリとファイルハンドルを消費します。目標 N に合わせてブローカーホストの RAM と JetStream の
max_file/max_memoryを設定し、メタ層の健全性を監視してください。 -
再接続の群れ (herd) — 多数のエージェントが_同一の瞬間に_再接続すると (ブローカー再起動、朝の電源投入の波、多数の PC を巻き込むネットワーク事象)、接続を張り直して consumer をバースト的に作り直します。キー絞り込みによって各エージェントの再同期はすでに数回の安価な
getまで落ちているので、危険な O(N²) の読み込みストームは消えています。ただし接続 + consumer 作成のバーストは依然として O(N) で、しかも同期しています。なお、ランダムで_同期していない_再接続 (ノート PC 1台の wifi 瞬断) は群れではありません。群れになるのはフリート全体が同期した事象だけです。
ストレージ: 50 GB のファイルストア、リソース単位の上限、保持期間
RAM と consumer はサイジングの話の半分にすぎず、もう半分はディスクです。JetStream のデータはすべて1つのディレクトリ (store_dir: C:/ProgramData/Kanade/nats/jetstream) の下に置かれ、ブローカー全体として max_file_store: 50GB (configs/nats-server.conf) で制限されます。これは全ストリームと全オブジェクトストアで共有されるソフト上限であって、ストリーム単位のものではありません。埋まっていくと何が起きるか:
- 自前の保持設定を持つリソースは古いものから退避し (
DiscardPolicy::Old)、健全なままです。 - 上限を持たないリソースは退避できません。そのためファイルストアが一杯になると、フリート全体で_あらゆる_ JetStream publish が「insufficient storage resources available」(エラー 10077) で失敗し始めます — エージェントの結果アップロード、collect バンドルのアップロード、publish、KV put。読み取りは動き続け、死ぬのは書き込み側です。上限の無いリソースは、上限のあるリソースの取り分も押しのけます。
したがって全リソースが上限を持たねばならず、実際に持っています:
-
ストリーム (
RESULTS/INVENTORY/AUDIT/OBS_EVENTS/NOTIFICATIONS/EXEC/EVENTS) はそれぞれmax_ageとmax_bytesを持ちます (bootstrap.rs、合計で約 5.3 GiB を確保)。これらは転送 + 再生のバッファであり、永続的な記録はバックエンドの SQLite です。運用者が見たいと思う履歴より上限を短く取れるのはそのためです。 -
オブジェクトストアはバケットごとに
max_bytesで制限され、SPA から再起動なしで変更できます (設定 → server → オブジェクトストアのディスク上限、#1247)。空欄にすると以下の組み込み既定値に戻ります:バケット 既定の上限 格納するもの result_output1,024 MiB 大きすぎる stdout/stderr の blob (数秒で SQLite に投影されます) agent_releases2,048 MiB エージェントの実行ファイル (約20バージョン) app_packages5,120 MiB 運用者が用意したインストーラ scripts256 MiB マニフェストのスクリプト本体 collections5,120 MiB collect ジョブのバンドル ( max_ageもあり、収集バンドルの保持期間 で変更できます)バックエンドは設定値を裏側の
OBJ_*ストリームへ起動のたびと保存のたびに反映します。上限が導入される前に作られたバケットにも上限が行き渡るのはこの仕組みによります。既定の合計確保量は約 13.5 GiB で、ストリームと合わせて 50 GB に十分収まるよう設計されています。 -
Recovery. If a stream or bucket has drifted from its expected config or is corrupted, repair it with the
natsCLI and an administrative credential, not withkanade. The backend recreates anything missing the next time it starts. This works even when the backend cannot start (a drifted stream config makes its startup bootstrap fail), because the scripts talk to the broker directly. Stopkanade-backendfirst (its projectors hold durable consumers) and start it again afterwards:# delete one stream / KV bucket / object store (asks you to type the name) ./scripts/ops/jetstream-delete.ps1 -Kind stream -Name RESULTS -Server nats://127.0.0.1:4222 -Creds ./admin.creds # wipe everything kanade uses: dry run first, then add -Yes ./scripts/ops/jetstream-reset.ps1 -Server nats://127.0.0.1:4222 -Creds ./admin.creds ./scripts/ops/jetstream-reset.ps1 -Server nats://127.0.0.1:4222 -Creds ./admin.creds -YesBoth need the
natsCLI onPATHand also readNATS_URL,NATS_CREDS,NATS_USERandNATS_PASSWORD. Deleting a resource deletes its data. -
SQLite (投影) も_無制限ではありません_。バックエンドのクリーンアップタスクが5分ごとに、区切られたバッチで刈り取ります —
execution_results/executions/obs_events/inventory_historyが 90 日、audit_logが 365 日、host_perf_samplesが 30 日、process_perf_samplesが 7 日です。どの DB の窓も、対応するストリームの窓より意図的に長く取ってあります。ストリームが再生できるものは必ず SQLite に既にある、ということです。残りのテーブルは upsert/replace 型 (現在の状態ぶんの大きさ) で、死んだエージェントはagent_prune_days(これも ServerSettings のノブ) で刈られます。
リソースごとの実使用量 (使用バイト数と上限) は SPA の JetStream ページにあります。ブローカーの現在の設定を読むので、上限を変えると次回の読み込みで反映されます。
打ち手 (優先順)
- まずブローカーのサイジング。 目標 N での定常 consumer 数を賄えるだけの RAM とファイル上限を、この単一ノードに与えてください。これが主たる打ち手で、他はすべて副次的です。
- 起動スプレイ — 実測したときだけ。 再接続の再同期の前に PC ごとの決定的な遅延 (
hash(pc_id)) を入れれば、consumer 作成と接続のバーストを一定の窓に均せます。これは設計判断としてまだ製品に入っていません。PR1 ですでに二次の項は消えており、async-nats の再接続バックオフとnats_retryの ±25% ジッタがある程度はバーストを散らしているからです。またスプレイは、群れではない単発の瞬断も含めてすべての再接続に遅延を足すので、実際に群れがブローカーを苦しめていないかぎり差し引きで損になります。データで判断してください (次節)。同期した事象で consumer 作成の遅延、接続の滞留、JetStream API のエラーが見えたなら、スプレイを入れます (Disconnected → Connected後の最初の同期だけに限定し、数秒で頭打ちにします)。 - エージェントあたりの consumer を減らす。 可能なら watch をまとめ (freeze watch はすでに単一キー、2本の
agent_groupswatch は統合の候補)、KV の履歴は浅く保ちます (agent_config/agent_groupsはhistory: 1)。 - JetStream をクラスタ化する。 1ノードで抱えられる量を超えたら、JetStream クラスタへ移ります。これが最後の手段であり、最も大きな変更です。
規模での実測 (事後分析)
本番のブローカーを立ち上がりの最中にライブで眺めるのは、たいてい無理です。代わりに同梱の collect: ジョブで数値を採取し、あとからバンドルを見てください。
バックエンド / NATS のホスト上で、できれば同期した再接続事象の最中か直後に実行します (スプレイの是非を決めるのは群れの瞬間です):
kanade exec collect-broker-health --pcs <backend-host-id>
このジョブ (configs/jobs/collect-broker-health.yaml) はブローカーを約3分にわたってサンプリングし、バンドルを OBJECT_COLLECTIONS にアップロードします。SPA の 収集 ページからダウンロードしてください (zip をそのままレビュー担当に渡しても構いません)。読み取り専用で、事前準備は一切不要です。NATS の認証なしの HTTP monitoring ポート (既定 8222 の /jsz エンドポイント) を読むだけなので、SYSTEM の PATH に nats CLI を置く必要も、トークンも要りません。必要なら対象側で KANADE_BH_SAMPLES / KANADE_BH_INTERVAL_SEC (ブローカーの http_port が違う場合は KANADE_BH_MON_PORT も) で窓を調整できます。
バンドルの内容:
- 時系列 (
connz-*.json、jsz-*.json) — サンプルごとの接続数 (/connz) と JetStream の consumer 数 (/jsz)。群れの瞬間にここが跳ねていれば、それがスプレイの signal です。 - consumer フットプリント (
jsz-full.json) —/jsz?consumers=true&streams=true。consumer の全リストで、合計が約 7N であることと、どのストリームが抱えているかを確認できます。 - サーバーの健全性/リソース (
varz.json、healthz.json) —/varz(メモリ、CPU、接続数、slow consumer) と/healthzの状態。 - ログの末尾 (秘匿情報は伏せ済み) — backend と nats-server。
何を見るか
- サンプルを通して consumer/接続数がなめらかで、
/healthzが健全、/varzのメモリ/CPU に余裕がある → ブローカーはその事象を吸収できています。スプレイは不要で、N に先回りしてサイジングを続けるだけです。 - 事象の時点で接続の滞留や consumer 作成の遅延がとがっている、JetStream API がエラーを返す、メモリ/CPU が張り付く → 群れは実在します → 起動スプレイ (打ち手2) を入れる、あるいはブローカーを増強します。
#828のダウングレード・フラッピングの回帰は、このジョブではなくOBS_EVENTSのagent_updateタイムラインから (バックエンド API 経由で) 別途確認します。そのデータはすでにフリート全体に対して問い合わせ可能です。
agent 向けスクリプトを書く
agent が実行する PowerShell スクリプトは ほぼ 通常の .ps1 ファイルです。このページは、スクリプトのソースを見るだけでは分からない罠をまとめます。
agent はスクリプトをディスクに staging してから -File で実行する
PR #230 (agent version 0.42.0+) 以降、agent は次の動作をします:
- スクリプト本体を temp の
.ps1に書き出します。Windows なら%ProgramData%\Kanade\agent-scripts\<UUID>\kanade-<UUID>.ps1、それ以外 (dev のみ) なら$TMPDIR/kanade-agent-<UUID>/kanade-<UUID>.ps1。 - Writes a launcher
.ps1next to it that sets UTF-8 console encoding then& '<your-script>' @args, and re-raises your script'sexit N/ terminating error as the job's exit code (if (-not $?) { exit $LASTEXITCODE }). powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File <launcher>を spawn します。
結果として、あなたのスクリプトでは:
- 先頭に
[CmdletBinding()]やparam(...)を 書けます。launcher の call-operator 境界が独自スコープを生み、その中ではヘッダーが有効です。 $PSCommandPathが operator のソースパスと一致することを 期待しないでください — staged 後の temp ファイルパスになります。$PSScriptRootに 書き込まないでください (次節参照)。
0.42.0 より前のエージェントは powershell -Command "<body>" を使っており、これは本文をコマンドライン式として解析するため [CmdletBinding()] を構文エラーとして弾きます。stderr に「Unexpected token '[CmdletBinding()]'」が出ていたら、その端末のエージェントが古すぎます。更新してください (エージェントの自己更新 を参照)。
run_as: user のとき $PSScriptRoot は読み取り専用
run_as: user (または system_gui) のとき、子プロセスはログオン中のユーザーとして動きます — staged ファイルを書いた LocalSystem の agent ではありません。staging ディレクトリは %ProgramData% から ACL を継承していて、Users には Read & Execute は許可されるが Modify は許可されません。
つまり:
# OK from any run_as
Get-ChildItem $PSScriptRoot # list contents
Get-Content $PSScriptRoot\anything # read
# NG from run_as: user (access denied)
New-Item -Path $PSScriptRoot\out.txt
Set-Content -Path $PSScriptRoot\log.log
代わりに $env:TEMP、$env:LOCALAPPDATA、ユーザープロファイル配下の絶対パスのいずれかに書いてください。run_as: system (SYSTEM が自分の staged dir に書ける) であっても、スクリプト終了時にディレクトリは掃除されるので、隣接ファイル書き込みはいずれにせよ壊れやすいです。
実行 identity 一覧
run_as: (マニフェスト) | 子プロセスの identity | $PSScriptRoot を読める | $PSScriptRoot に書ける | admin 権限あり |
|---|---|---|---|---|
system (デフォルト) | LocalSystem | ✓ | ✓ (ただし無意味、GC される) | はい |
user | ログオン中のユーザー | ✓ | ✗ access denied | いいえ |
system_gui | LocalSystem (ユーザーセッション内) | ✓ | ✓ (ただし無意味、GC される) | はい |
system_gui は「PsExec の -i -s」パターンです。管理者権限を持ちつつ、ユーザーのデスクトップセッションから見える形で動きます (昇格と対話ウィンドウの両方を必要とする GUI ツールに有用です)。
stdout vs Write-Host
The backend's result projector reads stdout as the script's output. For jobs with hint blocks (inventory:, check:, collect:, etc.), the projector extracts JSON payloads delimited by #KANADE-<KIND>-BEGIN and #KANADE-<KIND>-END fences (e.g. #KANADE-INVENTORY-BEGIN / #KANADE-INVENTORY-END).
For single-hint jobs without fences, backward compatibility allows parsing the entire stdout as a single JSON object. However, for multi-hint jobs (e.g. combining inventory: and check:), fenced blocks are required so each hint handler can extract its respective payload.
Do NOT use Write-Host for progress chatter. Write-Host output bleeds INTO the captured stdout stream, which can mess up unfenced JSON parsing or clutter log output.
Send progress chatter to stderr via [Console]::Error.WriteLine(...). stderr is captured into the result's separate stderr field, which the projector ignores.
[Console]::Error.WriteLine("Downloading...") # → stderr (logged, ignored by projector)
Write-Output "#KANADE-INVENTORY-BEGIN"
Write-Output ($obj | ConvertTo-Json -Compress)
Write-Output "#KANADE-INVENTORY-END"
Fencing stdout payloads ensures clean extraction even if other output is present, and allows a single job script to provide inventory facts, health check state, and file collection metadata in a single run (#821). See configs/jobs/installers/scripts/install-kanade-client.ps1 for a worked example.
デフォルトで UTF-8
launcher はスクリプト起動前に [Console]::OutputEncoding = UTF-8 と $OutputEncoding = UTF-8 を設定するので、ホストのシステムコードページに関係なくあなたの stdout / stderr は UTF-8 になります。日本語 / DE / KR / CN を含む operator スクリプトも、ホスト個別の workaround なしに SPA Activity ビューで正しく表示されます。
明示的に OEM / CP932 / Shift-JIS 出力が必要なら ($OutputEncoding を無視する legacy CLI を呼ぶケースなど)、launcher の prelude が走ったあとにスクリプト内で自分で設定してください — あなたの代入が優先されます。
ネイティブコマンドの exit code
スクリプトが成功した native コマンドで終わると全体の exit は 0 — これは PowerShell のデフォルトです。native コマンドが失敗 ($LASTEXITCODE -ne 0) してハンドリングしないと、PowerShell は依然として 0 で終了します — $ErrorActionPreference = 'Stop' は 救ってくれません。
Windows PowerShell 5.1 (Windows エンドポイントのデフォルト、agent の
powershell.exeが解決する先) は$ErrorActionPreferenceに関わらず native コマンドの非ゼロ exit を non-terminating として扱います。PowerShell 7.3+ で$PSNativeCommandUseErrorActionPreference = $trueが追加されてこれを terminating にできますが、デプロイターゲットでは使えません。必ず$LASTEXITCODEを明示的にチェックしてください。
The agent does NOT auto-propagate a leftover $LASTEXITCODE — that would exit nonzero even when your script handled the native error gracefully. The launcher only re-raises an explicit exit N or a terminating error, so the job's exit code matches running your script with powershell -File. If you want the script's exit code to reflect a specific native call, propagate it yourself:
& git pull
if ($LASTEXITCODE -ne 0) { throw "git pull failed with exit code $LASTEXITCODE" }
# ネイティブの終了コードをそのまま伝播させたい場合は:
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
通常は throw のほうが望ましい — クリーンな PowerShell エラーレコードを生成し (trap { … break } クリーンアップパターンが捕まえられる) かつ非ゼロで終わるからです。exit $LASTEXITCODE は呼び出し側が正確な exit code を気にする場合に向きます。
タイムアウト
マニフェストの timeout: は agent によって適用されます。発火すると agent は PowerShell プロセスに child.kill() を呼びます — graceful な shutdown も trap も finally もありません。それを前提に設計してください:
- スクリプトが
timeout * 0.6で終わるように実行時間を見積もり、余裕を残す。 - 明示的解放が要るリソース (staging dir、lock ファイル) のクリーンアップには
trap { ... ; break }を使う —trapは terminating エラーで発火し、agent の kill では発火しません。timeout ケースには頼らないこと。 - 協調的なキャンセルが必要なら、番兵ファイルやレジストリ値をポーリングして早めに抜けてください。エージェントからスクリプトへ、穏当に「切り上げろ」と伝える手段はありません。
実行中ジョブの kill
kanade kill <exec_id> は agent が購読する kill メッセージを publish します。受信すると agent は child.kill() を呼びます — timeout 経路と同じ hard-kill。operator にはただちに Killed マークの result row が届き、終了前に agent がキャプチャできた stdout / stderr が付きます。
開発者ワークフローとコントリビューション
このドキュメントでは、kanade コードベースで作業するコントリビューター向けの標準的な開発ワークフロー、lint/テストの要件、および VCS ブランチのガイドラインについて説明します。
1. クオリティゲート (Pre-Push & CI)
プッシュや PR の送信を行う前に、ローカルのテストスイートがすべてグリーンである必要があります。これは GitHub Actions で実行される自動チェックと同じものです。
# フォーマットチェック、clippy チェック、ターゲットテスト、および cargo ロックチェックを実行します
cargo make check
- FMT & Clippy: 私たちは厳格なゼロワーニングポリシーを維持しています。強力な設計上の正当な理由がない限り、
#[allow(clippy::...)]を散布しないでください。 - TDD (テスト駆動開発): Kent Beck の TDD 手法に従ってください。最初に失敗するテストを書いて「何を」行うかを定義し、次にそれを満たすコードを実装します。
2. renri によるワークツリー管理
隔離された機能開発のために、私たちは renri を使用して軽量なリポジトリワークツリーを管理します。これにより、ステージの汚染を防ぎ、メインチェックアウトをクリーンに保ち、瞬時にタスクを切り替えることができます。
なぜ renri なのか?
Git と Jujutsu (jj) がコロケーションされた環境では、ワークツリーを手動で管理するのは複雑になります。renri は、VCS 固有のワークツリー作成(設定されている場合は jj を優先)とクリーンアップを自動的にラッピングすることで、これを簡素化します。
よく使うコマンド
# 隔離されたワークツリーを作成します (存在するならデフォルトで Jujutsu を使用)
renri add feat/your-awesome-feature
# Git ネイティブのワークツリーを強制的に作成します (jj をバイパス)
renri --vcs git add feat/your-awesome-feature
# マージ後にワークツリーをクリーンアップして削除します
renri remove feat/your-awesome-feature
# 古くなった、または破損したワークツリーをガベージコレクションしてクリーンアップします
renri prune
注意: ワークツリー作成時に自動的に cargo-make の on-add フックが呼び出され、リモートの参照を取得して APM 設定を直ちにセットアップします。
3. コロケーションされた Jujutsu (jj) & Git ワークフロー
開発環境は、Git と Jujutsu がコロケーションされるよう設定されています。ローカルのバージョン管理には、安全で競合のないコミットモデルを持つ jj を好んで使用します。
ガイドライン
mainへの直接プッシュ禁止: すべての変更は Pull Request 経由でマージされる必要があります。- ブランチ/ブックマークの命名規則:
feat/...は新機能用。fix/...はバグ修正用。chore/...はインフラ、依存関係の更新、またはリリース用。
- コミットメッセージ: コミットメッセージ、PR タイトル、本文は英語で記述してください。
- バージョン更新: リリースのバージョン更新は、
mainへの PR と自動タグ付けパイプラインを介してのみ管理されます。手動でgit tagを実行しないでください。
4. ドキュメントポリシー
ドキュメントは、コードの変更と常に完全に同期している必要があります。機能を追加または変更した場合は常に、以下を実行してください:
- コード内のコメントや docstring を更新し、「なぜ」そのようにしたのかを説明します(「どのように」やっているかを単に再記述するコメントは避けてください)。
- 関連する book ページ(
book/src/配下に英語で記述)を更新します。 - 翻訳テンプレートジェネレーターを実行して、ローカライズカタログ(ポファイル)を同期します。
仕様 (旧 single-page)
フルのプロトコル / on-wire 仕様はまだ book に取り込まれていません。オーソリティはリポジトリの single-file 版です:
このセクション配下に章分割するのは、operator / 開発者ガイドが落ち着いてからの follow-up とします。
設定リファレンス
kanade サービスは、TOML ファイル、環境変数、またはレジストリパスから読み込まれる構造化された設定に依存しています。
1. エージェント設定
エージェントは KANADE_AGENT_CONFIG 環境変数を介して設定を検索し、存在しない場合はネイティブパスにフォールバックします。
開発用設定 (configs/agent.dev.toml)
# 開発用設定スキーマ
[agent]
id = "dev-pc"
nats_url = "nats://localhost:4223"
data_dir = "target/dev-data/agent"
[log]
level = "debug"
file = "target/dev-data/agent/logs/agent.log"
設定パラメータ
| フィールド | 型 | 説明 | 環境変数による上書き |
|---|---|---|---|
agent.id | String | ユニークなハードウェア識別子 (pc_id)。 | KANADE_DEV_AGENT_ID (テンプレート化) |
agent.nats_url | String | NATS ブローカー of ネットワークアドレス。 | KANADE_NATS_URL |
agent.data_dir | Path | 送信トレイのスクリプト、状態データベース、およびローカルの補完データをキャッシュするルートパス。 | KANADE_AGENT_DATA_DIR |
log.level | String | ログ出力レベルの冗長度 (error, warn, info, debug, trace)。 | RUST_LOG |
log.file | Path | ローリングログの書き出し先ファイルパス。 | - |
Per-PC job concurrency
max_local_concurrent lives in the layered agent_config store, not the agent TOML file. It applies to backend schedules, agent-local schedules, and operator runs from the CLI or administration SPA. One agent shares a single budget across these paths. User-triggered kanade-client actions are exempt: they start without waiting for or consuming a slot. They do not interrupt jobs already running.
When no scope sets the limit, the agent uses its locally available logical CPU count (falling back to 1 if detection fails). The backend leaves this automatic value as null; it never substitutes the backend host's CPU count. An explicit limit must be an integer of at least 1. Scopes apply in order: built-in automatic default → global → groups → PC.
kanade config set max_local_concurrent=4
kanade config set --group low-power max_local_concurrent=2
kanade config set --pc EXACT-HOSTNAME max_local_concurrent=1
kanade config unset --pc EXACT-HOSTNAME max_local_concurrent
kanade config (get, set, unset, clear, effective) talks to the backend HTTP API, not to NATS: it needs KANADE_AUTH_TOKEN (an operator-role token to change anything, like the other HTTP subcommands) and no broker token. Automation that calls kanade config must therefore authenticate with an auth token. The backend does each set / unset as a compare-and-swap on that one field, so a concurrent rollout writing target_version on the same scope is not overwritten, and a request that would change nothing writes nothing. Each change is recorded in the audit log under the caller's account; the audit entry is published after the write and is best-effort, not atomic with it. effective prints the backend's resolved view; its warnings are the backend's rendered text.
unset restores inheritance; when all applicable scopes omit the field, CPU-based sizing resumes. Updates apply without restarting the agent. Reducing the limit lets existing jobs finish and holds new jobs until enough slots are free. A job keeps its slot through retries, collection and finalize. Jitter happens before admission. Queued jobs can be killed and are skipped if their starting deadline expires. Waiting does not consume the script timeout or emit a running lifecycle event. Execution gates (version pin, revocation, staleness and deadline) are checked before jitter and again after admission.
Compatibility note: runs_on: agent schedules previously omitted their starting deadline from local commands. They now enforce starting_deadline from the local fire time, including jitter and slot waiting. Existing schedules whose jitter exceeds that deadline can therefore report a deadline skip even with a free slot. Keep the deadline longer than the maximum jitter plus the acceptable queue wait, or omit it when late execution is acceptable. Kill delivery is best effort: if the broker subscription fails, the agent logs a warning and continues waiting under the same capacity and deadline rules.
A critical job can opt out for every execution of that manifest:
execute:
shell: powershell
script: 'Write-Output "critical action"'
timeout: 30s
bypass_local_limit: true
Exempt jobs consume no slots, so total host concurrency can exceed the limit. The budget is agent-process-wide; it assumes one agent service per PC. constraints.max_concurrent remains a separate backend, fleet-wide per-job limit. This setting does not fix its jitter accounting issue (#1373).
2. バックエンド設定
バックエンドの調整レイヤーは KANADE_BACKEND_CONFIG で指定されたファイルから設定を取得し、指定がない場合はデフォルトの構造体を登録します。
開発用設定 (configs/backend.dev.toml)
[backend]
listen_addr = "127.0.0.1:8081"
nats_url = "nats://localhost:4223"
database_url = "sqlite://target/dev-data/backend/state.db"
[auth]
# 認証設定
設定パラメータ
| フィールド | 型 | 説明 | 環境変数による上書き |
|---|---|---|---|
backend.listen_addr | String | HTTP/WebSocket トラフィック用のネットワークバインド文字列。 | KANADE_BIND_ADDR |
backend.nats_url | String | 対象とする NATS ブローカーの URL。 | KANADE_NATS_URL |
backend.database_url | String | SQLite データベースへの接続文字列。 | DATABASE_URL |
auth.disable | Boolean | オペレーターのトークン検証を無効にするには true を設定します (開発環境でのみ有効)。 | KANADE_AUTH_DISABLE |
3. Windows レジストリ統合
本番環境では、セキュリティに敏感なトークン(NATS クライアントトークンや管理用 API ベアラートークンなど)は、プレーンテキストファイルではなく、保護された Windows レジストリに保存されます。
キーパス
- エージェント設定:
HKLM:\SOFTWARE\Kanade\agent - バックエンド設定:
HKLM:\SOFTWARE\Kanade\backend
これらのレジストリパスはローカルの ACL 設定で保護されており、SYSTEM および指定されたオペレーターにのみ読み取り権限が厳密に制限されます。










