運用ガイド
このガイドでは、信頼できる単一ホスト上での ghalet の設定・実行・保守を説明します。最初のセットアップは導入ガイド 、各サブコマンドの用途はコマンド一覧 を参照してください。
設定と認証情報
run と config validate は、--config で明示しない限り ~/.config/ghalet/config.yaml を使います。
status、apply、gc は --config を省略するとアクセス可能なデーモンを探索し、応答するデーモンが 1 件の場合だけ自動選択します。複数ある場合は ghalet list で管理 UID、ソケット、稼働中のスナップショットを確認し、--config PATH で選択してください。設定を明示した場合は、別の設定を使うデーモンには切り替わりません。
見つからない場合、管理コマンドはユーザーごとのデフォルト設定を使い、gc はロック取得後にオフラインで保守を行えます。
list --config PATH は一覧を絞り込み、ファイルの存在は要求しません。
探索では、ファイルシステムの権限に従って /tmp/ghalet-<UID>/ の非公開 Unix ソケットに問い合わせます。通常は管理ユーザー自身のデーモンのみが対象ですが、root は他ユーザーのデーモンも探索できます。
候補は 1 秒以内に応答し、返した設定パスがソケット名と一致する必要があります。残存・無応答・無関係なソケット、シンボリックリンク、非公開でないソケットは無視し、削除しません。PID やプロセス名による推測は行いません。応答サイズの上限は 4 MiB です。
操作時にも接続相手の UID を確認し、デーモンの設定が選択したパスと一致しなければ操作を拒否します。
CLI の更新時はデーモンも再起動してください。
設定ファイルはカレントディレクトリや /etc から探索しません。相対パスの基準は、シェルのカレントディレクトリではなく、選択した設定ファイルのディレクトリです。サービス定義には絶対パスを使ってください。YAML 内の ~ は展開されません。
systemd の STATE_DIRECTORY と CACHE_DIRECTORY が設定されている場合は優先し、実行ディレクトリと復旧状態は $STATE_DIRECTORY/runs、runner のアーカイブとダイジェストは $CACHE_DIRECTORY/runners に保存します。各変数には単一の絶対パスを指定し、相対パスやコロン区切りの複数パスは拒否します。これらは XDG の基準ではなくアプリ専用のディレクトリです。systemd のディレクトリ管理
を参照してください。
未指定の場合、実行ディレクトリと復旧状態は $XDG_STATE_HOME/ghalet/runs(デフォルト: ~/.local/state/ghalet/runs)、runner のアーカイブと公式 SHA-256 ダイジェストは共通の $XDG_CACHE_HOME/ghalet/runners(デフォルト: ~/.cache/ghalet/runners)に保存します。
XDG 変数が未設定・空・相対パスの場合は、これらの HOME 基準のデフォルトを使います。保存先は runner.user や設定ファイルの場所ではなく、管理プロセスの環境に従います。
同じ実行領域を使えるデーモンは 1 つだけです。複数起動する場合は state の保存先を分けてください。これらの保存先は YAML では設定できません。
ファイルを編集するだけでは、稼働中の設定も制御ソケットのアドレスも変わりません。アドレスは正規化した設定ファイルの絶対パスから決まります。デーモンに接続する管理コマンドは、設定が不正でもファイルが削除されても、稼働中のスナップショットを参照します。反映可能な設定と再起動が必要な変更は設定反映 を参照してください。
examples/config.yaml にはリポジトリ、Organization、gh、PAT、App の例があります。各 target は登録先の scope を持ちます。
credential を省略すると defaults.credential を継承し、共通設定もなければ type: gh を使います。target に明示した credential は共通設定を丸ごと置き換えます。空のマッピングは継承せず gh を選びます。認証設定内で type を省略した場合も gh です。PAT / App を使う場合は、type: pat または type: github-app の明示が必要です。認証方式固有のフィールドだけで方式が自動選択されることはありません。明示的に空の type を指定すると拒否されます。
GitHub.com では、scope にリポジトリの OWNER/REPO、または Organization の ORG を指定できます。これらは検証・認証・登録の照合前に HTTPS URL に正規化されます。完全な HTTPS URL も指定でき、GitHub.com 以外のホストでは必須です。対応する scope はリポジトリと Organization のみです。runnerGroup は Organization 専用です。target ごとに scope は 1 つだけ指定できます。
各 target には scope と、GitHub 上の登録名・workflow の runs-on ラベルとなる scaleSet が必須です。runnerGroup(省略時はデフォルトグループ)との組で target を一意に識別します。scope や runner group が異なれば同じ Scale Set 名を使えます。
ローカルの id は設定しません。正規化した登録情報から導出した SHA-256 キーを、ローカルステート・認証情報・診断の targetId に使います。status 出力には scope・runner group・Scale Set 名も含めます。
App の installationId は credential に属し、client ID と鍵のパスと一緒に継承します。異なるインストールでは、別々の credential から同じ鍵のパスを参照できます。共通のインストールを使う場合は defaults.credential にまとめられます。
セットアップで管理する情報
App セットアップは不足する target 登録を静的設定に追加し、認証情報の登録コマンドは静的設定を変更しません。どちらも認証情報は別の managed ファイルに保存します。シークレット本体は認証ファイルに保持し、どちらの YAML にも直接書きません。手書きの認証設定には managed ファイルは不要です。
標準のユーザー設定に対するデフォルト保存先は <StateDir>/managed.yaml です。/etc/ghalet/config.yaml では、サービス外でも /var/lib/ghalet/managed.yaml を使います。ただし STATE_DIRECTORY があれば、その state を優先します。その他の設定では <StateDir>/managed/<設定パスのSHA-256>.yaml を使います。識別には --config の有無によらず、正規化した設定の絶対パスを使うため、独自の設定ファイルを移動するとデフォルトの managed 保存先も変わります。
managedFile で保存先を変更できます。相対パスは静的設定のディレクトリ基準です。
managedFile: /var/lib/ghalet/managed.yaml独自の systemd 設定では、絶対パスの managedFile を指定するか、セットアップ・検証・サービスで同じ STATE_DIRECTORY を使ってください。別の config を選んでも unit は変わりません。
managed ファイルは runner の実行・キャッシュ・ログ用ディレクトリの外に配置します。生成時は所有者限定とし、既存の managed ファイルも管理ユーザー所有の通常ファイルで、他ユーザーが書き込めない必要があります。認証情報と App の再開用情報に加え、managed ファイルもバックアップしてください。
有効になるのは静的設定に記載した target だけです。managed の target は正規化した scope・runnerGroup・scaleSet の組で照合し、別の登録先へ認証を流用しません。
target に credential を明示した場合、managed の target 認証は適用しません。それ以外は managed の credential 全体を使い、共通設定より優先します。
共通設定では静的な defaults.credential が managed 側より優先し、どちらもなければ gh を使います。認証設定をフィールド単位で混ぜることはありません。
managed ファイル内の相対パスは、そのファイルのディレクトリ基準です。
既存設定で生成された共通認証を使う場合は、静的な defaults.credential を意図的に削除してください。または app setup --overwrite を使ってください。target の手書き認証も、managed 側を使うときだけ削除します。
managed ファイルがなければ空として扱い、不正な形式はエラーにします。反映は起動時または明示的な apply 時だけで、systemd の認証情報にはサービスの再起動が必要です。
コメント付きサンプル
の任意の yaml-language-server コメントで、schema.yaml をエディターの補完・検証用に指定できます。登録先の重複、最小容量、管理ディレクトリの安全性などの制約も確認するには ghalet config validate を使ってください。
未知のキーは、静的設定・managed 設定のどの階層でも許容し、無視します。既知の項目の値は検証し、YAML キーの重複は拒否します。
デフォルトの gh 認証では、ghalet の実行環境に GitHub CLI をインストールし、実際にサービスを起動するアカウントでログインしてください。ghalet は 15 秒の期限で gh auth token --hostname HOST を実行し、トークンを設定ファイルには書き込みません。
対話ユーザーのキーチェーンにログイン情報があっても、別のサービスユーザーやロック中のキーチェーンから使えるとは限りません。実際のサービス環境・ユーザーでコマンドを確認してください。GitHub CLI や認証情報がなければエラーになり、別の認証方式にはフォールバックしません。
PAT では tokenEnv と tokenFile のどちらか一方だけを指定します。App の秘密鍵を含む認証ファイルは、所有者のみアクセスできる通常ファイルにしてください(通常は chmod 600)。YAML に値を直接書くことはできません。
systemd の credential は、対応する LoadCredential= または LoadCredentialEncrypted= とともに、/run/credentials/ghalet.service/ 以下の絶対パスを tokenFile または privateKeyFile に指定して参照できます。不要に Environment= や workflow ファイルへシークレットを置かないでください。暗号化登録、共通設定、ローテーションは暗号化した認証情報
を参照してください。
scope 別の権限、PAT の作成、GitHub App のインストールは認証のセットアップと最小権限 を参照してください。
group のアクセス設定と App のインストール範囲は別です。group の作成とリポジトリへのアクセス許可は、先に GitHub 側で行ってください。ghalet は runner group の作成・アクセス設定変更・削除を行いません。
短命な runner 登録トークンを認証情報として指定することはできません。
直接実行と環境
ghalet config validate --config /path/to/config.yaml
ghalet run --config /path/to/config.yamlrunner.user で別の既存アカウントを指定しない限り、ジョブは起動ユーザーで実行します。非 root の管理プロセスは、パスワード不要の sudo でユーザーを切り替えられます。管理ユーザーは runner アカウントのプライマリグループに所属する必要があります。
パッケージの sudo 設定
は、ghalet の runner 用ヘルパーだけを ghalet-runner として実行することを許可します。独自に導入する場合は、この設定の管理ユーザー、runner のユーザー・グループ、実行ファイルのパスを合わせてください。ヘルパーは runner の起動、プロセスの識別情報を確認した停止シグナルの送信、停止後のログ保存・削除のための共有グループへのファイル公開を行います。ジョブを root で実行せず、管理用認証情報も受け取りません。
管理プロセスを root で起動して直接切り替えることもできますが、非 root の runner.user を明示する必要があります。CLI はユーザーを作成・削除せず、root ジョブは拒否します。
公式 runner は、自身のディレクトリからファイルシステムの root まで、すべての親で一覧取得と通過ができる必要があります。管理対象の実行用親ディレクトリ(workRoot、target、run)と、Linux パッケージの /var/lib/ghalet の権限は 0755 です。ディレクトリ名は見えますが、所有者は管理ユーザーのままで、他ユーザーは書き込めません。ステートファイルとロックファイルは所有者のみアクセスできます。管理対象外の親ディレクトリは変更しないため、runner ユーザーが一覧取得・通過できるようにしてください。
管理者が指定する保存先の基点(STATE_DIRECTORY、CACHE_DIRECTORY、XDG の基点または HOME 配下のフォールバック、明示した logs.root)では、macOS の /var を含むシンボリックリンクを利用できます。ghalet は既存の基点のリンクを解決してから管理対象のパスを導出し、新しいステートや認証情報のパスには正規化した保存先を使います。基点とその親は信頼できる状態に保ち、ghalet の実行中に変更しないでください。
デーモンと実行中のジョブを停止した後は、基点を移して元のパスをシンボリックリンクとして残せます。復旧時や App セットアップの再試行では、保存済みのパスを正規化した基点に合わせますが、管理対象の子のリンクは解決しません。実行中のジョブがある状態での移動はサポートしません。
systemd のドロップインでは /etc/ghalet/credentials/... の固定した参照元を維持するため、配置を移しても既存の設定が無効にならず、移動先のディレクトリ名が systemd の specifier として展開されることもありません。
管理対象の子(実行ディレクトリ、runner キャッシュ、認証情報のディレクトリ・ファイル、アーカイブ削除の起点など)をシンボリックリンクで別の場所へ向けることはできません。クリーンアップはリンクをたどって所有する実行ディレクトリやアーカイブの外を削除しません。
設定ファイルの親にも管理者の配置用リンクを利用できますが、制御ソケットの識別には選択した設定パスの字句上の表記を維持するため、保存先の正規化でアドレスは変わりません。パスの検証は、信頼するディレクトリが同時に置き換えられる競合を防ぐものではありません。
runner の起動とアクセス確認では、コントローラーの環境変数を継承しません。HOME、USER、LOGNAME は実行ユーザーのアカウントから設定します。コントローラーと runner のユーザーが異なる場合も同じです。PATH のデフォルトは /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin で、macOS では先頭に /opt/homebrew/bin:/opt/homebrew/sbin が加わります。ログインシェルやシェルの初期化ファイルは読み込みません。
ユーザーがインストールしたツール、ロケール、プロキシなどには、runner.env で環境変数の追加や PATH の上書きを明示してください。コントローラーのトークン変数、SSH エージェントのソケット、ツールの設定パスなどは自動ではコピーされません。管理用認証情報、アカウント識別情報、runner の入力変数は runner.env では設定できません。runner の入力名は大文字・小文字を区別せず検査します。
JIT シークレットは ACTIONS_RUNNER_INPUT_JITCONFIG としてのみ渡し、コマンドライン引数やステートのフィールドには入れません。管理用シークレットを意図的に別の変数へコピーしないでください。runner 自身のファイルは実行専用ディレクトリ内に置き、実行後に削除します。
これは runner の環境を最小化するものであり、認証・API 呼び出し・ダウンロードに使うコントローラーの環境は変わりません。ファイルシステムやキーチェーンへのアクセスには、信頼境界 で述べる制約が残ります。
ビルドツールと公式 runner が要求する OS の依存パッケージは、事前にホストへインストールしてください。Docker actions、ジョブコンテナ、サービスには対応していません。
Linux / macOS でのネイティブ実行には、Docker デーモン、Kubernetes、VM ランタイム、サービスマネージャーは不要です。固定したバージョンから、macOS ARM64/x64 と Linux ARM64/x64/ARM の runner パッケージを選択します。Windows には対応していません。
runner は全体共通の省略可能な設定です。バージョンのデフォルトは ghalet に組み込まれた数値バージョンで、別のリリースを固定するには runner.version を指定します。設定でバージョンを固定していなければ、ghalet の更新と再起動で新しいデフォルトが適用されます。起動時に latest を解決することはなく、runner の自己更新は無効のままです。
固定バージョンのアーカイブは actions/runner のリリースから取得し、リリースアセットの公式 SHA-256 ダイジェストで検証します。キャッシュ済みアーカイブの再利用時にも再検証します。公式ダイジェストのないリリースはエラーにして使用しません。runner キャッシュに固定ダイジェストとアーカイブを保存し、実行ごとの独立した実行ディレクトリに展開します。キャッシュの準備・検証は直列化し、実行ごとの展開は並行して行えます。
破損キャッシュを黙って置き換えることはありません。示された不正なアーカイブを削除して再起動してください。
容量・再試行・停止
scaling は省略可能です。各 target のデフォルトは minRunners: 0、maxRunners: 2 です。省略フィールドはデフォルト値を維持します。maxRunners: 0 の明示は不正です。
目標数は min(maxRunners, assignedJobs + minRunners) です。実行中ジョブ、起動中の実行、確認できない復旧対象は、同じ共有容量を使用します。受付済みジョブは、別 target の待機中の warm runner より優先されます。前のプロセスグループの停止が確認できるまで、枠は再利用しません。需要が減っただけで実行中の runner を停止することはありません。
warm pool が埋まっても、共有容量の上限内で他の target がジョブを受け付けられます。target 間の公平性や、複数コントローラー間の協調には対応していません。
準備の期限は 10 分、API 操作は 45 秒、認証コマンドは 15 秒、listener のポーリングは 60 秒です。登録・listener の失敗と準備・起動の一時的な失敗は、1/2/4/8/16/32 秒、以降は最大 1 分の指数バックオフで、回数制限なく自動復旧します。起動時の登録は最大 4 回に制限し、残る一時的な失敗はバックグラウンドで復旧するため、他の target や制御操作を妨げません。
SDK で判別可能な恒久的な不正リクエスト・認証エラーと、安全でない登録・状態・認証情報のコピーは直ちに停止し、調査後に apply または通常停止・再起動が必要です。他の target は継続します。
準備開始は target ごとに毎秒 1 回までです。drain は登録の復旧待ちを中止し、apply と通常停止は古い listener をキャンセルします。
準備に失敗した実行や異常終了した runner の cleanup が未解決の場合、cleanup の成功(または gc・再起動による解決)まで、その target の新規起動を停止し、失敗の繰り返しによる runner ファイルの蓄積を防ぎます。cleanup 自体の再試行ポリシーは変更しません。
listener と準備・起動の一時的な失敗は、対応する listener または runner の起動が復旧するまで status に表示します。workflow の実行結果に対する再試行は ghalet では行いません。
SIGINT / SIGTERM は通常停止時に全 target の drain を開始します。apply も設定切り替え前に全 target を drain します。通常停止では Scale Set を残します。新規取得と待機 runner の補充は止まりますが、割り当て済みで未起動のジョブには、target・ホスト両方の上限内で引き続き runner を用意できます。
drain の期限は各 runner の起動時ではなく、要求時から計測します。drainTimeout のデフォルトは 10 分です。期限後は準備をキャンセルし、確認済みのプロセスグループに、実行中ジョブを含めて SIGTERM を送り、残っていれば SIGKILL を送ります。コントローラーは runner のプロセスグループの停止と、ローカルの終了・出力監視の完了を待って終了します。
識別できない実行に名前や PID だけを根拠にシグナルを送ることはなく、そのグループが存在しないと確認するまで停止完了を待ち続けます。記録と診断情報は調査用に残ります。存在しないことの確認だけでは cleanup は許可しません。強制停止は成功ではなく未完了として報告します。
再起動が必要な変更では、デーモンを通常停止し、正常完了を確認してから再起動してください。フォアグラウンドでは ghalet run の終了コードを、サービス管理下では停止コマンドの結果だけでなく、デーモンの終了結果とログを確認します。強制終了や不完全な drain の後には再起動せず、診断情報を調べ、保留中の実行や cleanup の失敗を解消してください。
Linux / macOS ともに、直接起動かサービス管理下かにかかわらず、新しい runner ごとにフォアグラウンドの guard を置きます。コントローラーが終了・強制終了すると、guard が runner のプロセスグループを強制停止します。runner の通常終了後も、残ったグループメンバーを停止し、2 秒の猶予後に強制停止します。終了診断には runner の実際の実行結果を使い、ステートとログは復旧用に残します。
これは子プロセスの監視であり、ジョブの隔離ではありません。信頼したジョブが guard を停止したり、グループから逃れたりしないことが前提で、カーネル・ホスト障害では監視が中断され得ます。
設定反映
ghalet apply --config /path/to/config.yaml
# パッケージの systemd サービス:
sudo systemctl reload ghalettarget、上限、認証情報、runner バージョン、環境変数、タイムアウト、ログ保存方針は、設定を編集して apply で反映します。稼働中のデーモンが必要で、そのデーモン自身の設定パスを再読込し、ファイルは変更しません。設定が存在しないか不正な場合は失敗し、別のファイルで代用しません。
apply は反映処理の完了まで待ちます。新設定を検証してから新規受付を止め、旧設定の上限・タイムアウトで全 target を drain し、listener を閉じた後、新設定の target を登録して listener の初期化を待ちます。変更のない Scale Set は再利用します。起動失敗は apply の結果に含め、その後の listener の障害は status で確認できます。
正規化した scope・group・Scale Set の組で登録先を識別し、いずれかの変更時はその登録を置き換えます。全 target を外すには targets: [] を指定します。targets の省略や null は不正です。
削除・置換された target の Scale Set は、新設定の target が同じ scope と登録 ID(または scope・group・名前)を参照しておらず、ローカルの実行も残っていない場合に削除を試みます。drain と listener の後始末が正常完了し、GitHub の統計でも受付済みジョブや登録済み runner がないことを確認します。runner group は削除しません。管理する Scale Set を他のコントローラーと共有しないでください。
失敗は target ごとに報告し、非ゼロで終了しますが、正常な target には新設定を反映します。自動ロールバックはしません。削除に失敗した登録の旧クライアントはメモリー内に保持し、apply で再試行できます。強制 drain、不確実な実行、listener の終了失敗は調査が必要です。
保留中の削除処理は再起動をまたいで永続化しません。クラッシュ後は現在の設定を正とし、到達できない旧登録は GitHub 上で手動削除が必要になることがあります。
空き容量を作るために保留中の実行記録を破棄しません。同時の apply は拒否し、停止要求では apply を中断します。CLI が切断・タイムアウトしてもデーモン側では処理を継続するため、再試行前に status を確認してください。
実行ユーザーと管理ディレクトリの変更には通常停止・再起動が必要で、drain 前に拒否します。環境変数や systemd が復号した認証情報ファイルは稼働中サービスに属し、付属の unit でその入力を更新するにはサービスの再起動が必要です。通常の認証情報ファイルは apply で新クライアントを作る際に再読込します。
付属の systemd unit は ExecReload から同期的に ghalet apply を実行し、TimeoutStartSec で 15 分の期限を設けます。drain の期限や target 数を増やす場合は、reload の期限と TimeoutStopSec を延ばしてください。systemctl daemon-reload が読み直すのは unit 定義であり、ghalet の設定ではありません。
診断・ガベージコレクション
GitHub API の遅延、runner の準備、プロセスの停止、cleanup の間も、他のターゲットはスケジューリングを継続でき、status は処理中も直近の整合した実行スナップショットを返します。準備や cleanup の結果が反映されるまで、予約した実行は表示され続けます。drain と apply は実行ごとの処理完了を待ちます。gc も単に処理をキューに入れて返るのではなく、報告対象の cleanup 完了を待ちます。
ghalet status --config /path/to/config.yaml
ghalet status --fail-on-error --config /path/to/config.yaml
ghalet gc --config /path/to/config.yaml --dry-run
ghalet gc --config /path/to/config.yaml管理コマンドの応答は JSON です。status は常に status.problems に診断の詳細を含め、問題があればトップレベルの error に要約を出力します。問題のある状態でもスナップショットを取得できれば終了コード 0 を返します。status --fail-on-error は同じ JSON を出力し、診断上の問題が 1 つでもあれば非ゼロになります。スナップショットを取得できない場合は、オプションによらず非ゼロです。従来の doctor はこのオプションに置き換えます。gc、apply は、失敗・未完了の部分があれば非ゼロになり、個別の結果を含めます。管理操作の一部が失敗しても、デーモンは終了せず、成功済みの cleanup も巻き戻しません。
接続中の GC はデーモンが実行し、実行時ステートと直列化します。接続できない場合、直接実行する GC でも、同じ workRoot の OS ロックと設定の制御アドレスのロックを取得する必要があります。ソケットへの接続失敗だけでは、既存デーモンを迂回する根拠にはなりません。ロックファイルは PID ファイルではなく、プロセス終了後も残ることがあります。
オフライン GC でサービスの状態を参照するには、サービスと同じ保存先の環境変数を指定してください。配布用サービスでは STATE_DIRECTORY=/var/lib/ghalet と CACHE_DIRECTORY=/var/cache/ghalet です。
クラッシュ後は、記録を現在明示している登録、プロセスの UID・グループ・カーネルの開始識別情報、および生存中の実行では GitHub runner ID と照合します。Linux の識別にはブート ID と /proc の開始 tick、macOS ではマイクロ秒精度のカーネルのプロセス開始時刻を使います。プロセスの生存確認は、登録や実行ユーザーとの照合とは独立して行います。
プロセスの停止を検証できた記録は、ターゲットの削除、認証失敗、登録やユーザーの不一致により cleanup ができない場合でも容量を予約しません。記録・ファイル・診断は保持し、停止しただけでは削除を許可しません。生存している可能性がある、または検証できないプロセスは保守的に容量を予約します。破損記録も、プロセス不在を証明するための信頼できる識別情報がないため容量を予約し、cleanup を許可する根拠には使いません。
workRoot またはターゲットディレクトリ直下の通常ファイルは、警告を記録して保持し、容量は予約しません。不明なディレクトリやシンボリックリンクは保守的な診断として扱います。
生存プロセスを復旧しても、その終了コードを復元することはできません。元のプロセスグループ内で観測した子孫は復旧できますが、グループから逃れた子孫を完全に検出・隔離することはできません。
空またはゾンビのみのプロセス一覧だけでは、実行の停止を証明できません。ghalet はプロセスの識別情報を再検証し、確認できなければ不確実な状態と容量の予約を維持します。
ステートファイルは末尾の改行込みで 1 MiB が上限です。シグナル送信前に大量の子孫リストを永続化できない場合も含め、上限を超える保存は失敗し、最後に読み取り可能だった記録を置き換えません。
手動復旧では、示されたパスとプロセスを調査し、識別した実行だけを待つか明示的に停止して、gc --dry-run と gc を実行してください。設定や権限の問題を直して再起動し、再検証します。空き容量を作るためにステートだけを削除しないでください。破損記録の修復・削除前には、実際の実行を手動で調査する必要があります。
ログ
デーモンの標準出力は JSONL のみです。フィールドは time、level、msg、任意の event、scope、targetId、runId、数値の durationMs、error です。レコード内の改行はエスケープし、runner の出力は標準出力に混在させません。構造化フィールドの選別、転送、OTel への変換は外部の収集ツールで行ってください。組み込みの HTTP ヘルスエンドポイント、メトリクス exporter、OTel SDK、collector への依存はありません。
runner 開始イベントとアーカイブのメタデータの scope は登録先 URL です。
logs は runner ログの保存設定で、デーモンの標準出力・標準エラーは対象外です。logs.preserve: true では、実行領域の削除前に output.log と _diag/**/*.log を logs.root/runs/RUN_ID に退避します。
logs.root のデフォルトは STATE_DIRECTORY が設定されていれば $STATE_DIRECTORY/logs、それ以外は $XDG_STATE_HOME/ghalet/logs です。XDG_STATE_HOME も未設定・空・相対パスの場合は ~/.local/state/ghalet/logs になります。明示した logs.root はこのデフォルトを上書きします。相対値は引き続き選択した設定ファイルのディレクトリを基準に解決します。
認証情報と workspace の内容は退避しません。退避失敗時は元のログとステートを保持します。プロセス停止確認後の cleanup の失敗は実行容量を占有せず、drain 完了を無期限に妨げません。
通常稼働中は、一時的なネットワーク/API エラー(タイムアウト、レート制限、復旧可能なサーバーエラー)と一時的なファイルシステムエラーを 5 秒後から自動再試行し、待機時間を指数的に増やして最大 5 分に制限します。認証・権限エラー、identity の不一致、不正なステート、出力収集の失敗は自動再試行せず、運用者による対応が必要です。gc は待機時間に関係なく直ちに再試行し、次回起動でも保持した記録を再検証します。プロセスや登録の identity が不明な場合は cleanup を許可しません。
logs.retention は正常に保存済みのアーカイブだけを期限切れ削除し、毎時の確認も行います。ディスク容量制限、空き容量監視、外部への自動ログ転送はありません。
実行中の出力取得と診断ログの退避では、各行を改行抜きで 64 KiB に制限します。上限を超える行は全体を省略し、[ghalet: log line over 64 KiB omitted] に置き換えて、次の改行から取得を再開します。メモリー使用量を制限しつつ、行の部分出力によるシークレットの漏洩を防ぎます。
既知の管理用認証値と JIT 値はマスキングします。App の秘密鍵を含む複数行の値は、空白以外を含む各行も個別にマスクします。ジョブのシークレットを runner 自身のログでマスクする責任は、公式 runner が持ちます。信頼した workflow が意図的に変形・出力する任意のシークレットを、すべてマスクできる仕組みはありません。
サービスとしての常駐化
Linux パッケージや Docker Compose で導入する場合は、パッケージとコンテナ を参照してください。以下はサービスを手動で導入する場合の手順です。
Linux では、専用の ghalet と ghalet-runner ユーザーを、それぞれ利用できるホームディレクトリとともに事前に作成します。ghalet を runner のプライマリーグループ ghalet-runner に追加し、逆方向には追加しないでください。runner.user: ghalet-runner を設定し、packaging/sudoers
を root 所有、モード 0440 で /etc/sudoers.d/ghalet に配置します。実行ファイルのパスを /usr/local/bin/ghalet に調整し、visudo -cf /etc/sudoers.d/ghalet で検証してください。
examples/ghalet.service を /etc/systemd/system/ にコピーして、/etc/ghalet/config.yaml に設定を配置します。unit は StateDirectory=ghalet と CacheDirectory=ghalet で /var/lib/ghalet と /var/cache/ghalet をサービスユーザー用に作成し、STATE_DIRECTORY と CACHE_DIRECTORY で渡します。XDG の上書きは不要です。logs.root は、state 配下のデフォルト保存先を変更する場合のみ指定してください。そのユーザーで認証と設定検証を行い、systemctl enable --now ghalet を実行します。
KillMode=mixed は最初の停止シグナルをコントローラーだけに送り、ジョブの drain と runner グループの停止を行ってから終了できるようにします。
有限の TimeoutStopSec=15min は独立した最終期限です。コントローラーがハングしたり、残存グループを検証できなかったりした場合、systemd はコントローラーと runner を含むサービスの cgroup 内の全残存プロセスを強制停止します。コントローラーのクラッシュ・終了時にも残存プロセスを停止します。この最終手段は、復旧のために runner を生存させることより後始末を優先するものです。中断後の復旧用にステートとログは残ります。
サービスの期限は設定した drain 期限より長くし、プロセス停止、API 呼び出し、cleanup の時間も確保してください。サンプルでは 5 分を追加しています。ホスト停止や電源断によるジョブ中断は防げません。
macOS ネイティブでは examples/com.github.ghalet.plist の絶対パスを調整し、ログディレクトリを作成して、~/Library/LaunchAgents/ に配置します。launchctl bootstrap gui/$(id -u) FILE で登録してください。
サービスの停止上限 900 秒は、デフォルトの drain 10 分に 5 分を加えたものです。設定で期限を変えた場合は、この値も増やしてください。ゼロは launchd が停止を無期限に待つ意味になるため、意図的に避けています。
AbandonProcessGroup は false で、launchd がコントローラー自身のグループも cleanup します。別の runner グループには前述の guard
を使います。
LaunchAgent はログイン時に起動し、ログイン前には起動しません。スリープも防ぎません。
別の runner ユーザーを使う場合は、runner.user と、前述のグループ所属・パスワード不要の sudo を設定します。ログイン前から起動する場合は、管理者が管理する LaunchDaemon を使い、UserName にコントローラーのアカウントを指定してください。実際のログイン・サービス環境で、キーチェーンへのアクセスと停止動作を確認してください。
Apple Silicon の Lima では、examples/lima.yaml が VZ、4 vCPU、8 GiB メモリー、80 GiB ディスク、ホストマウントなし、containerd なしの Ubuntu 26.04 ARM64 VM と、インストール・有効化済みの systemd サービスを用意します。プロビジョニングはホストの認証情報をコピーしません。Go の toolchain 選択機能を使い、公開済みの ghalet モジュールをビルドします。
limactl copy で設定を明示的にコピーし、ゲストの ghalet ユーザーでログインするか、ゲスト専用の認証情報を渡してサービスを起動します。設定ファイルが存在するまで、サービス起動は条件付きでスキップされます。
examples/com.github.ghalet-lima.plist は作成済み VM をログイン時に起動します。limactl のパスとインスタンス名を調整してください。ジョブごとの VM 再起動や、Mac のスリープ・ログアウト中の動作は保証しません。limactl list とゲスト内のマウント出力で、macOS ホストの共有が公開されていないことを確認してください。VM のライフサイクル、スリープ・復帰、電源断は運用者による受け入れ確認の対象であり、テンプレート検証だけでは確認できません。
導入環境の確認
セットアップや更新後は、ghalet config validate、ghalet status --fail-on-error と、各 target の信頼できるテスト workflow を実行してください。ローカルの検証だけでは、GitHub の権限やサーバー側のジョブ割り当ては確認できません。Organization / App の権限、runner group のアクセス、複数 target のスケジューリング、サービスの停止、再起動、スリープ・復帰は、実際の運用環境で確認してください。テストが成功しても、ジョブの隔離を実証するものではありません。
信頼境界
ghalet は各 runner を Ephemeral に登録し、1 ジョブを実行させます。runner とそのプロセスグループの停止を確認した後、runner 専用ディレクトリと workspace を削除します。停止を確認できない場合や cleanup に失敗した場合は、状態とファイルを保持します。
Ephemeral な登録は隔離ではありません。 runner は OS 上のユーザープロセスであり、ジョブごとのサンドボックスではありません。信頼できるリポジトリの信頼できる workflow にだけ使用し、信頼できないコードが入り込む可能性のある fork PR は実行しないでください。
各 runner は別の実行・作業パスを持ちますが、OS、実行ユーザーの HOME、キャッシュ、ツール、ネットワーク、既存の権限を共有します。ジョブはその共有環境を変更できます。GitHub CLI やキーチェーンを含む、実行ユーザーの保存済み認証情報にアクセスできる場合もあります。
トークン変数の除去やジョブ専用の GH_TOKEN の指定では、その保存領域を隔離できません。認証情報を分けるには別の実行ユーザーを使ってください。
デフォルトの同一ユーザーモデルでは、コントローラーのファイルやプロセスもジョブからアクセス可能です。ファイル権限、マスキング、非公開ソケットは、そのユーザーに対するセキュリティ境界にはなりません。
追加の sudo 権限や Docker ソケットは提供せず、root ジョブも拒否しますが、既存の権限は剥奪しません。専用アカウント / VM と適切なホスト側の制御を使ってください。