Provide transactional config mutation and scoped teardown commands that safely modify OpenClaw system state.
Admin Operations
Config mutation, scoped resets, and system-state commands
Admin operations are the privileged commands that modify OpenClaw’s persistent configuration, credentials, and installed services. Every mutation — from changing a model default to a full factory reset — flows through the mutateConfigFile transactional pipeline, which guarantees atomic reads, draft-based edits, and hash-checked writes so that concurrent CLI sessions never silently overwrite each other’s changes.
📚 What is this & why does it matter?
Admin operations form the control plane of OpenClaw. While regular slash commands read state and produce output, admin commands write state — they change the config file on disk, stop or start system services, and tear down workspace artifacts. Understanding this module is essential for debugging “config not saved” issues, building new administrative commands, or auditing what reset --scope full actually deletes.
💡 Real-world analogy
Think of a bank vault with a double-entry ledger. Every teller (CLI session) that wants to modify account balances must first read the current ledger page, make changes on a draft copy, and then compare a checksum before committing. If another teller changed the ledger in the meantime, the commit is rejected and the teller must re-read. mutateConfigFile is that ledger protocol — it prevents lost updates even when multiple terminals run configure simultaneously.
Contents
Overview
RuntimeEnv context, user-supplied options (ResetOptions, UninstallOptions), and the current OpenClawConfig on disk.
Which reset scope to apply? Which uninstall targets to remove? Should the operation run in dry-run mode? Is a hash conflict detected?
ConfigReplaceResult with the new config hash, service stop/start signals, and filesystem deletions for reset and uninstall scopes.
Core Types
Admin operations revolve around four key type families: the config mutation generics, the reset scope enum, the uninstall scope set, and the agent config parameters.
// src/commands/config-io.ts — config mutation signature
export async function mutateConfigFile<T = void>(params: {
base?: ConfigMutationBase;
baseHash?: string;
writeOptions?: ConfigWriteOptions;
mutate: (
draft: OpenClawConfig,
context: { snapshot: ConfigFileSnapshot; previousHash: string | null }
) => Promise<T | void> | T | void;
}): Promise<ConfigReplaceResult & { result: T | undefined }>
// src/commands/reset.ts — reset scope types
export type ResetScope = "config" | "config+creds+sessions" | "full";
export type ResetOptions = {
scope?: ResetScope;
yes?: boolean;
nonInteractive?: boolean;
dryRun?: boolean;
};
| Type | Source file | Purpose |
|---|---|---|
ConfigReplaceResult | config-io.ts | Return value after a successful config write — includes the new file hash and whether the write was a no-op |
ResetScope | reset.ts | Discriminated string union controlling how much state is wiped: config only, config + credentials + sessions, or full factory reset |
UninstallScope | uninstall.ts | Set of removal targets: "service", "state", "workspace", "app" |
UninstallOptions | uninstall.ts | CLI flags mapped to scope booleans, plus all, yes, nonInteractive, and dryRun |
ConfigMutationBase | config-io.ts | Optional base config to mutate from (defaults to reading from disk) |
ConfigFileSnapshot | config-io.ts | Immutable snapshot of the config file at read time, used for conflict detection |
Decision Points
| Decision | What controls it | Why it matters |
|---|---|---|
| Hash conflict detection | baseHash parameter vs. current file hash at write time | Prevents concurrent CLI sessions from silently overwriting each other’s config changes |
| Reset scope selection | ResetScope enum passed via --scope flag or interactive prompt | Determines whether only config, config + credentials + sessions, or everything is wiped |
| Uninstall scope set | buildScopeSelection(opts) reads boolean flags and --all | Allows granular teardown — stop service only, remove state only, or nuke everything |
| Dry-run guard | dryRun: boolean on both ResetOptions and UninstallOptions | Lets operators preview destructive actions without executing them |
| Interactive confirmation | yes flag bypasses prompt; nonInteractive auto-declines | Safety net for destructive operations in interactive terminals; CI pipelines use --yes |
| Agent config merge strategy | applyAgentConfig parameter presence checks | Only fields explicitly provided are overwritten; omitted fields preserve existing values |
How It Works
Admin operations follow three distinct patterns depending on the command: transactional config mutation for configure and agent config, scoped reset for the reset command, and multi-scope uninstall for teardown. All three patterns share the same dry-run and confirmation guardrails.
Config Mutation Lifecycle
The mutateConfigFile function implements an optimistic-concurrency protocol. It reads the current config file into a snapshot, passes a mutable draft to the caller’s mutate callback, and then performs a compare-and-swap write using the snapshot’s hash. If another process modified the file between read and write, the operation fails with a hash-conflict error rather than silently overwriting.
flowchart TD
START["mutateConfigFile() called"]
READ["Read config file from disk"]
SNAP["Create ConfigFileSnapshot + hash"]
DRAFT["Clone snapshot into mutable draft"]
MUTATE["Execute caller's mutate(draft, context)"]
COMPARE{"baseHash === currentHash?"}
WRITE["Atomically write draft to disk"]
RESULT["Return ConfigReplaceResult + result T"]
CONFLICT["Throw hash-conflict error"]
DRYRUN{"dryRun enabled?"}
PREVIEW["Return preview without writing"]
START --> READ
READ --> SNAP
SNAP --> DRAFT
DRAFT --> MUTATE
MUTATE --> DRYRUN
DRYRUN -->|Yes| PREVIEW
DRYRUN -->|No| COMPARE
COMPARE -->|Match| WRITE
COMPARE -->|Mismatch| CONFLICT
WRITE --> RESULT
The flowchart above shows the full config mutation pipeline. The critical path runs from top to bottom: read → snapshot → draft → mutate → compare → write. The dry-run branch exits early after mutation without touching disk, and the hash-conflict branch raises an error that the caller can retry.
Configure Command
The configureCommand and configureCommandWithSections functions launch an interactive wizard that collects user input, then calls mutateConfigFile internally to persist changes. The sections parameter allows callers to restrict the wizard to specific config areas (e.g., only model settings).
// src/commands/configure.commands.ts — configure entry points
export async function configureCommand(
runtime: RuntimeEnv = defaultRuntime
) {
await runConfigureWizard({ command: "configure" }, runtime);
}
export async function configureCommandWithSections(
sections: WizardSection[],
runtime: RuntimeEnv = defaultRuntime
) {
await runConfigureWizard(
{ command: "configure", sections },
runtime
);
}
Scoped Reset Flow
The resetCommand applies a progressively wider teardown depending on the chosen ResetScope. The "config" scope only removes the config file. The "config+creds+sessions" scope additionally clears stored credentials and active session tokens. The "full" scope performs a factory reset that also removes workspace artifacts, agent directories, and cached data.
sequenceDiagram
participant User
participant CLI as resetCommand()
participant Confirm as Confirmation Prompt
participant Config as Config File
participant Creds as Credentials Store
participant Sessions as Session Store
participant Workspace as Workspace Artifacts
User->>CLI: reset --scope config+creds+sessions
CLI->>Confirm: "This will delete config, credentials, and sessions. Continue?"
Confirm-->>CLI: yes
CLI->>Config: Delete config file
Config-->>CLI: OK
CLI->>Creds: Clear stored credentials
Creds-->>CLI: OK
CLI->>Sessions: Invalidate active sessions
Sessions-->>CLI: OK
CLI-->>User: Reset complete (config + creds + sessions)
The sequence diagram shows the reset flow for the "config+creds+sessions" scope. Each store is cleared in order: config first, then credentials, then sessions. If --yes is passed, the confirmation prompt is skipped. If --dry-run is set, the command prints what it would delete without actually deleting anything.
// src/commands/reset.ts — reset command signature
export type ResetScope = "config" | "config+creds+sessions" | "full";
export type ResetOptions = {
scope?: ResetScope;
yes?: boolean;
nonInteractive?: boolean;
dryRun?: boolean;
};
export async function resetCommand(
runtime: RuntimeEnv,
opts: ResetOptions
)
Multi-Scope Uninstall
The uninstall command uses a scope-set pattern rather than a single enum. The buildScopeSelection helper reads individual boolean flags (--service, --state, --workspace, --app) and the --all shorthand to produce a Set<UninstallScope>. Each scope triggers a specific teardown action, and scopes execute in dependency order: service is stopped before state is removed, and workspace is cleaned before app binaries are deleted.
// src/commands/uninstall.ts — scope selection and teardown
type UninstallScope = "service" | "state" | "workspace" | "app";
export type UninstallOptions = {
service?: boolean;
state?: boolean;
workspace?: boolean;
app?: boolean;
all?: boolean;
yes?: boolean;
nonInteractive?: boolean;
dryRun?: boolean;
};
function buildScopeSelection(
opts: UninstallOptions
): { scopes: Set<UninstallScope>; hadExplicit: boolean; }
async function stopAndUninstallService(
runtime: RuntimeEnv
): Promise<boolean>
service → state → workspace → app. Attempting to remove state while the service is still running would cause file-lock errors on Windows. The stopAndUninstallService helper handles graceful shutdown before any filesystem deletions begin.
Code Walkthrough — applyAgentConfig
This walkthrough traces how agent configuration parameters are merged into the global OpenClawConfig object. The function uses a selective-merge strategy: only fields that are explicitly provided in the params object are overwritten; omitted fields preserve their existing values.
Receive the current config and parameters
The function takes the full OpenClawConfig as its first argument and a params object with the target agentId plus optional override fields. The config is treated as immutable input — a new config object is returned.
// src/commands/agents.config.ts — entry point
export function applyAgentConfig(
cfg: OpenClawConfig,
params: {
agentId: string;
name?: string;
workspace?: string;
agentDir?: string;
model?: string;
}
): OpenClawConfig
Look up or create the agent entry
The function searches cfg.agents for an entry matching params.agentId. If no entry exists, a new agent record is initialized with default values. This ensures that applyAgentConfig can be called for both existing agents and first-time setup.
Selective field merge
Each optional parameter is checked for presence (not just truthiness — an empty string is a valid override). Only present fields are written to the agent record. This prevents accidental field clearing when only one property needs to change.
Return new config
A new OpenClawConfig object is returned with the updated agents array. The original cfg is never mutated, keeping the function pure and compatible with the mutateConfigFile draft pattern where the caller controls when the draft is committed.
Setup Command
The setupCommand orchestrates first-run initialization. It creates workspace directories, writes a default config file (via mutateConfigFile), and optionally launches the configure wizard. It is safe to call on an already-initialized workspace — existing files are not overwritten.
// src/commands/setup.ts — setup entry point
export async function setupCommand(
opts?: { workspace?: string },
runtime: RuntimeEnv = defaultRuntime
)
setupCommand checks for the existence of each artifact before creating it. Running setup on an already-configured workspace is a safe no-op — it will only create missing files and directories.
Edge Cases
| Scenario | Behavior | Rationale |
|---|---|---|
Hash conflict during mutateConfigFile | Throws a hash-conflict error; caller must re-read and retry | Prevents concurrent CLI sessions from silently overwriting each other’s changes |
| Config file missing on reset | Skips config deletion, proceeds to next scope | A missing config is already in the desired end-state; no error is warranted |
--dry-run with --scope full | Prints every file and directory that would be deleted, returns without side effects | Operators need a way to audit the blast radius of a full reset before committing |
nonInteractive: true without yes: true | Automatically declines destructive operations | CI pipelines that accidentally invoke reset without --yes should fail safely |
applyAgentConfig with unknown agentId | Creates a new agent entry with defaults, then applies overrides | Supports first-time agent setup without requiring a separate “create agent” step |
| Service still running during uninstall | stopAndUninstallService sends graceful shutdown; waits up to 10s before force-killing | Prevents file-lock errors when removing service state on Windows |
buildScopeSelection with no flags and no --all | Returns empty set with hadExplicit: false; caller prompts interactively | Prevents accidental uninstall when user forgets to specify what to remove |
| Concurrent setup calls | Second call is a no-op for already-created artifacts | Idempotent design ensures race conditions don’t produce duplicate files |
Configuration
| Setting | Location | Default | Description |
|---|---|---|---|
| Config file path | RuntimeEnv.configPath | ~/.openclaw/config.json | Location of the OpenClaw config file on disk; all mutations target this path |
| Reset scope | ResetOptions.scope | "config" | Controls which stores are wiped: config only, config + creds + sessions, or full |
| Dry-run mode | --dry-run flag | false | When enabled, destructive commands print what they would do without executing |
| Auto-confirm | --yes flag | false | Bypasses interactive confirmation prompts for destructive operations |
| Non-interactive mode | --non-interactive flag | false | When set, automatically declines any confirmation prompt (safe default for CI) |
| Workspace directory | setupCommand opts.workspace | process.cwd() | Root directory for workspace initialization |
| Agent directory | applyAgentConfig params.agentDir | Agent-specific default | Override the filesystem path for a specific agent’s working directory |
Related Topics
Authorization checks that run before any admin operation is allowed to execute.
How raw user input is parsed into structured command objects before reaching admin handlers.
Where admin commands are registered and looked up during dispatch.
Source Files
Key files involved in admin operations
src/
└── commands/
├── config-io.ts ← mutateConfigFile() transactional mutation
├── configure.commands.ts ← configureCommand(), wizard launcher
├── reset.ts ← resetCommand(), ResetScope, ResetOptions
├── uninstall.ts ← uninstallCommand(), buildScopeSelection()
├── agents.config.ts ← applyAgentConfig() agent config merge
└── setup.ts ← setupCommand() first-run initialization