Config writes
Every path that mutatesopenclaw.json goes through one writer: writeConfigFile in the core config I/O layer. The gateway RPC methods (config.set, config.patch, config.apply), the CLI, the chat /config command, and plugins all funnel through it. This page documents what that writer does, which gateway methods write config, and when a change is hot-reloaded versus requiring a restart.
Write paths
Hand-editing the file in a text editor is also supported — the gateway watches the file and applies the reload policy described in Hot reload versus restart.
Gateway RPC methods
config.patch is the safe default for targeted changes: only the paths present in the patch are touched, everything else is preserved as it exists on disk (including keys the caller never saw). config.apply replaces the whole document and is meant for intentional full replacements.
Optimistic concurrency (baseHash)
Key:baseHash (request parameter) — no default; required by every write method when a config file already exists.
The flow:
- The client calls
config.getand receives the snapshot plus its hash. - The client sends that hash as
baseHashwithconfig.set/config.patch/config.apply. - The gateway re-reads the file. If the hash no longer matches, the write is refused with
config changed since last load; re-run config.get and retry.
Validation and secret redaction
Before anything touches disk:- Schema validation. The merged document is validated against the full config schema, including schemas contributed by loaded plugins and channel plugins. An invalid write is refused before the file is modified, with the offending path and issue in the error.
- Secret redaction.
config.getreturns the config with secret values (API keys, tokens) replaced by redaction markers. On write, unchanged redacted values are restored from the file on disk — a client that read, modified one unrelated key, and wrote back cannot accidentally blank your secrets, and the agent never sees secret values in the first place. - Environment variable references. If the file uses
${VAR}references (for example"${ANTHROPIC_API_KEY}"), the writer restores the reference instead of persisting the resolved value, unless the write actually changed that value.
Top-level key removal guard
Default: refusals on; cannot be disabled globally. A write that would drop a top-level key that exists on disk (for example losing the wholechannels or agents block) is refused with error code CONFIG_WRITE_REFUSED:
allowTopLevelKeyRemoval. The CLI openclaw config unset and the chat /config unset command do this automatically for the path you name. The agent’s gateway tool never sends allowTopLevelKeyRemoval, so the assistant cannot silently delete top-level config blocks.
Advisory conditions are logged but never block: a greater-than-50% size shrink, a missing meta block, or removal of gateway.mode.
Write mechanics: atomicity, backups, audit
Every write follows the same sequence:- Serialize the validated document as pretty-printed JSON with a stamped config version.
- Write to a temporary file in the same directory with mode
0600. - Rotate backups: the previous file is copied to
openclaw.json.bak, and the existing ring shifts —openclaw.json.bak.1throughopenclaw.json.bak.4(5 backups total, permissions hardened to0600, orphan.bak.*files pruned). - Atomically rename the temp file over
openclaw.json(with a copy fallback on Windows). - Append an audit record to
~/.openclaw/logs/config-audit.jsonl.
rename, copy-fallback, failed, or refused). To answer “who changed the config and when”, read that file:
config.patch, config.apply) additionally log a control-plane line with the acting client, device id, client IP, and the changed paths.
Restart gate
Key:gateway.restartPolicy — default "drain"; values "drain" | "force" | "cancel".
config.patch and config.apply restart the gateway after writing (via SIGUSR1, with restart coalescing and an optional restartDelayMs). Before that, the restart gate checks for active runs (in-flight agent turns and embedded runs):
The gate check happens before any file write, so a confirmation round-trip never leaves a half-applied state on disk.
After the restart completes, a restart sentinel (written before the restart) delivers the writer’s
note back to the session that requested the change — this is how the assistant reports “done” after it restarts itself.
This change requires a gateway restart: config.patch and config.apply perform it automatically. config.set does not — see below.
Hot reload versus restart
Key:gateway.reload.mode — default "hybrid"; values "off" | "restart" | "hot" | "hybrid". Companion key: gateway.reload.debounceMs — default 300.
openclaw.json and reacts to external edits — including writes made via config.set, the CLI, /config, or your text editor. When the file changes, the gateway debounces, re-reads, and validates. An invalid file is never applied: the reload is skipped with a warning and the previous config keeps running.
Which strategy applies:
"hybrid"(default) — the gateway classifies the changed paths and hot-reloads what it can, restarts when required."hot"— hot-reload only. Changes that would require a restart are ignored with a warning."restart"— restart the gateway for every config change."off"— never react to config changes; nothing is applied until the next manual restart.
config.patch and config.apply bypass this classification: they always schedule a restart after writing. The reload policy governs external edits and config.set writes. If you want a patch-style change without the restart, use config.set with the full document, or accept the restart.systemctl --user restart openclaw-gateway; macOS: wednesdayai gateway restart --deep).
Troubleshooting
Related
- Configuration — the config file itself
- Tool policy —
tools.execsecurity and the exec approvals file - Exec approvals — the exec approval flow
- Config writes (developers) — the plugin surface for config writes
- Reference: config