Skip to content

openclaw.json 設定エントリ

源码版本v2026.6.11

責務

openclaw.json は OpenClaw の唯一の作成者态設定 (config) ファイルです:ゲートウェイポート、モデル provider、チャネル資格情報、スキル読み込みパス、cron、記憶ポリシー、approvals、hooks がすべてこの 1 つの JSON5 ファイルに書かれます。ランタイムのすべての設定はここから読み出され,include 展開、env 変数置換、Zod schema 検証、デフォルト値注入を経て,メモリ内の OpenClawConfig オブジェクトになります。

「一度読んで終わり」のファイルではありません:CLI の config set/patch/unset は原子書き戻しを行い,ランタイムスナップショット (runtime snapshot) はディスク内容と比較して競合を防ぎ,問題時には last-known-good バックアップからロールバックできます。だからこのエントリは読み、書き、検証、バックアップ、移行の 5 件事を担います。

設計動機

なぜディレクトリ分散ではなく単一ファイルか?初期の OpenClaw は provider、channel、memory をそれぞれ別々の JSON に詰め込んでいましたが,結果としてユーザが 1 つの provider を変更するために 3 つのファイルを変更する必要があり,チャネルプラグインのアップグレード後に移行ロジックをあちこちに撒く必要がありました。単一の openclaw.json に収斂した後,バージョンスタンプ、バックアップ、監査ログがすべて同じファイルに付きます,openclaw doctor は 1 つのパスをスキャンするだけで健康診断できます。

しかし単一ファイルは膨張するので,同時に 2 つの逃げ道をサポートします:

  • $include ディレクティブで長い provider/channel 設定をサブファイルに分割,ルートファイルは参照だけを保持;
  • ${VAR} 環境変数置換で秘密鍵をファイルから切り離し。

これら 2 つの特性により openclaw.json は git リポジトリに(マスキング後)入れられ,Docker/Tailscale のような環境変数だけを与えるデプロイでも動作します。

主要ファイル

  • paths.ts CONFIG_FILENAME:25-26openclaw.json ファイル名定数,隣に legacy clawdbot.json の兜底も残存。
  • resolveStateDir:58-87 — 状態ディレクトリ(state dir)を解析,$OPENCLAW_STATE_DIR が覆盖,さもなくば ~/.openclaw,さらにさもなくば legacy .clawdbot にフォールバック。
  • resolveCanonicalConfigPath:152-161 — canonical パス $OPENCLAW_CONFIG_PATH または $stateDir/openclaw.json
  • resolveConfigPath:191-226 — 実際の読み取り時,既に存在する候補ファイル(legacy 含む)に優先ヒット,見つからなければ canonical にフォールバック。
  • normalizeStateDirEnv:89-95 — 起動時に $OPENCLAW_STATE_DIR~ を展開して env に書き戻し,ゲートウェイ起動シーケンスの第一件事。
  • resolveIncludeRoots:117-143$OPENCLAW_INCLUDE_ROOTS ホワイトリスト,$include が設定ディレクトリを跨ぐことを許可。
  • createConfigIO:1378-1434 — IO facade ファクトリ,内部に読み、書き、スナップショット、バックアップ、検証の全フローをカプセル化。
  • loadConfigLocal:1654-1828 — 同期読みパスの核心:dotenv → ファイル読み → JSON5 parse → include 展開 → env 置換 → 検証 → ランタイム設定物化。
  • writeConfigFileLocal:2269-2366 — 書きパスの核心:スナップショット比較,merge-patch 生成,検証,原子書き込み。
  • writeConfigFile エクスポート:2826-2860 — 公共書き込み入口,ランタイムスナップショットを source 形态に投影してから書き込み。
  • applyMergePatch:83 — JSON merge patch 実装,patch モードの基礎。
  • applyConfigOverrides:93 — ランタイム注入覆盖(テスト、CLI 一時パラメータ)。
  • env-substitution.ts${VAR} 解析,変数欠落は warn だけでクラッシュしない。
  • includes.ts$include ディレクティブ解析,パス越境保護付き。
  • defaults.ts apply*:109-529applyMessageDefaults / applyAgentDefaults / applyCronDefaults / applyCompactionDefaults などのデフォルト値注入。
  • runConfigPatch:2279-2309openclaw config patch CLI エントリ。
  • mergeConfigValue:771-790 — patch 時の再帰マージ,provider models[] 配列は特殊重複排除パスを走る。

データフロー

起動時,createConfigIO がまずパス定数を pin し,次に loadConfigLocal(loadConfigLocal:1654)を走ります。読み取りパスの主要ステップ:

typescript
function loadConfigLocal(options: { skipSuspiciousRecovery?: boolean } = {}): OpenClawConfig {
  try {
    maybeLoadDotEnvForConfig(deps.env);
    const envBeforeRead = snapshotEnv(deps.env);
    if (!deps.fs.existsSync(configPath)) {
      // ... シェル env fallback を読み込み空設定を返す
      return {};
    }
    const raw = deps.fs.readFileSync(configPath, "utf-8");
    const parsed = deps.json5.parse(raw);
    const readResolution = resolveConfigForRead(
      resolveConfigIncludesForRead(parsed, configPath, deps),
      deps.env,
      deps.lowerPrecedenceEnv,
    );
    // ... shipped plugin install records 移行、validate、materialize

3 つの点に注意:maybeLoadDotEnvForConfig は env が process.env と等しいときだけ本当に dotenv を読み込みます(テスト注入の env は触らない)。envBeforeRead は書き戻し時に「読み取り中に env が変更されたか」を検出するため。resolveConfigIncludesForRead$include 対象ファイルを読み込み,それから resolveConfigForRead に渡して ${VAR} 置換を行います。順序が重要:include が env 置換に先立つことで,include されたフィールド内でも ${VAR} を使えます。

書き込みパスは writeConfigFile(writeConfigFile:2826)を走ります。runtime snapshot が存在する場合,caller が渡した runtime 形態の設定を patch に diff してから source 形態に投影し直します:

typescript
export async function writeConfigFile(
  cfg: OpenClawConfig,
  options: ConfigWriteOptions = {},
): Promise<ConfigWriteResult> {
  options.assertConfigPathForWrite?.();
  const io = createConfigIO({ /* ... */ });
  assertConfigWriteAllowedInCurrentMode({ configPath: io.configPath });
  let nextCfg = cfg;
  const runtimeConfigSnapshot = getRuntimeConfigSnapshotState();
  const runtimeConfigSourceSnapshot = getRuntimeConfigSourceSnapshotState();
  const hadBothSnapshots = Boolean(runtimeConfigSnapshot && runtimeConfigSourceSnapshot);
  if (hadBothSnapshots) {
    const runtimePatch = createMergePatch(runtimeConfigSnapshot!, cfg);
    nextCfg = coerceConfig(applyMergePatch(runtimeConfigSourceSnapshot!, runtimePatch));
  }

このステップにより caller が書き戻すのは「ユーザ作成者態」であり,ランタイムデフォルト値に汚染された形態ではありません——さもなくば schema デフォルト値がファイルに固化され,バージョン変更時に戻せなくなります。

CLI patch モード(runConfigPatch:2279)は JSON5 patch オブジェクトを受け取り,既存設定に再帰マージします:

typescript
function mergeConfigValue(existing: unknown, patch: unknown, path: PathSegment[]): unknown {
  if (isProviderModelListPath(path) && Array.isArray(existing) && Array.isArray(patch)) {
    return mergeModelArrays(existing, patch);
  }
  if (isPlainRecord(existing) && isPlainRecord(patch)) {
    // 深マージ:オブジェクトは再帰,非オブジェクトは直接覆盖

models[] のような配列は mergeModelArrays を走り,id フィールドで重複排除してから要素をマージし,単純置換しません——provider リストに model を追加しても元の model をすべて消し去りません。

読み、書き、patch の 3 つのパスの関係全体:

境界と失敗

  • パスドリフト:ゲートウェイ起動時に必ず pinRuntimePaths(pinRuntimePaths:236-245)を呼びます,さもなくば OPENCLAW_CONFIG_PATH が module import 後に設定されると旧値を読み,「1 つのパスを読み,別のパスに書く」という撕裂が発生します。
  • legacy フォールバック:resolveConfigPath はまず clawdbot.json.clawdbot/ を走査し,なければ新パスに走ります。旧デプロイのアップグレード時,両方のディレクトリが存在すれば新パスを優先しますが,ユーザが旧ファイルを ~/.openclaw/ に誤って残すと新プロセスもそれを読みます——doctor コマンドがこの分裂を専用スキャンします。
  • $include 越境:デフォルトでは openclaw.json と同じディレクトリおよびサブディレクトリのファイルだけ参照できます。ディレクトリを跨ぐには明示的に $OPENCLAW_INCLUDE_ROOTS を設定し,各ルートは tilde 展開 + 重複排除 + 非絶対パス拒否されます(include roots filter:128-141)。
  • ${VAR} 欠落:スローせず,「feature using this value will be unavailable」と warn するだけ(src/config/io.ts:1950-1956)で,ゲートウェイは拒否ではなくグレードダウンモードに入ります。ただし provider apiKey 欠落は実際にリクエスト時にエラーになります。
  • Nix モード読み取り専用:OPENCLAW_NIX_MODE=1 のとき,assertConfigWriteAllowedInCurrentMode はすべての書き込み操作を拒否し,設定は Nix によって外部管理されます(resolveIsNixMode:16-18)。
  • 原子書き込み:replaceFileAtomic がテンポラリファイルに書いてから rename し,ディレクトリ権限 0o700,ファイル権限 0o600,Windows は copy fallback に走ります。
  • サイズ急減拦截:書き込み前に previousBytes vs nextBytes を比較し,50% 以下に減少し allowConfigSizeDrop フラグがなければ直接拒否——ある patch が provider ブロック全体を削除するのを防ぎます。
  • last-known-good:毎回の成功読み取りで lastKnownGood フィンガープリント(hash + bytes + mtime + dev/ino)を更新します。次回読み取り時に size 暴落や gateway mode フィールド抹消を検出すると,自動的に .bak から復元し,監査ログに config.observe イベントを 1 行記録します。
  • future version 警告:ファイルの meta.lastTouchedVersion が現在のバイナリより新ければ warn し,PATH が旧版 openclaw を指している可能性をユーザに提示します——「新版が書いた設定を旧版が読んで壊す」のを避けます。

まとめ

openclaw.json は単一エントリですが,背後には読み-検証-物化-書き-監査のライフサイクル全体があります。読み取りパスは include と env を層状に展開し,書き込みパスはランタイム形態を source 形態に逆投影し,patch モードは CLI に構造化増分編集を提供します。schema が各フィールドの合法形状をどう定義するかは Zod Schema 実行時検証 を参照。provider の具体フィールド(baseUrl/api/auth/apiKey/models[])は 能力層:モデル Provider で詳述。チャネル側設定は チャネル:設定 を参照。常駐プロセスが読み込んだ config をどう使うかは デプロイ:daemon を参照。

公式資料:Configuration ドキュメント · README