Feature Parity: Configuration System Enhancements
Priority: P2-P3
Source: FEATURE_PARITY.md — Configuration System
IronClaw uses .env files with a type-safe Config struct. OpenClaw has richer configuration options.
Missing
Notes
- Hot-reload is the most impactful item here. The current
Config struct is built once at startup.
- Could use
notify crate for file watching + channel to signal config changes
- JSON5/YAML are lower priority since
.env works well for the current use case
Design Considerations
Current Config Architecture
Config struct (src/config.rs:16-33): 14 sub-configs covering database, LLM, embeddings, channels, agent, safety, WASM, secrets, builder, heartbeat, routines, sandbox, and Claude Code.
Two loading methods:
Config::from_env() — Env vars only, used before DB connection
Config::from_db(store, user_id, bootstrap) — DB settings override env vars
Critical: Config is NOT Arc'd. The config is cloned into individual components at startup:
let agent = Agent::new(
config.agent.clone(), // AgentConfig is Clone
deps,
channels,
Some(config.heartbeat.clone()),
Some(config.routines.clone()),
...
);
Components hold their own copies. Changes to the source Config don't propagate.
Settings Storage (Already Implemented)
The Database trait provides 5 settings methods: set_setting(), get_setting(), delete_setting(), get_all_settings(), set_all_settings(). The CLI already has ironclaw config list/get/set/reset/path. Settings use flat dot notation: "agent.name", "heartbeat.interval_secs".
Resolution order: Environment variable → Database setting → Hardcoded default.
Hot-Reload Options
Option A: Component-scoped reload (recommended, minimal refactor)
Keep config cloned to components. Add a reload channel:
pub struct Agent {
config: AgentConfig,
config_rx: watch::Receiver<AgentConfig>, // Tokio watch channel
}
// On config change:
config_tx.send(new_agent_config)?;
// In agent loop:
if self.config_rx.has_changed().unwrap_or(false) {
self.config = self.config_rx.borrow_and_update().clone();
tracing::info!("Agent config reloaded");
}
Pros: No Arc/RwLock overhead, each component reads on its own schedule, minimal code change.
Cons: Each component needs a watch channel, config push logic per sub-config.
Option B: Shared Arc<RwLock> (comprehensive, significant refactor)
Wrap Config in Arc<RwLock<Config>>, share across all components. Components call config.read() when they need values.
Pros: Single source of truth, any change immediately visible.
Cons: ~500 LOC changes across 10+ files, lock contention on hot paths, every config access becomes async.
Recommendation: Option A for initial implementation. Only the most dynamic settings need hot-reload (heartbeat interval, gateway port, routine schedules). Most config (database, LLM provider) requires restart anyway.
Config Change Sources
- CLI command:
ironclaw config set heartbeat.interval_secs 900 → writes to DB → notifies running agent
- Web UI: Gateway
/api/settings endpoint → same flow
- File watch:
notify crate watches .env file → re-parse → push changes
- Signal:
SIGHUP → reload all config from DB
Notification mechanism: Use a Unix domain socket or the existing DB settings table with a config_version counter. Running agent polls or watches for version changes.
What's Actually Hot-Reloadable
| Setting |
Hot-Reloadable |
Reason |
heartbeat.interval_secs |
Yes |
Background task reads interval each tick |
heartbeat.enabled |
Yes |
Start/stop background task |
routines.max_concurrent |
Yes |
Checked per-fire |
agent.max_parallel_jobs |
Yes |
Checked before job spawn |
safety.max_output_length |
Yes |
Checked per tool output |
llm.backend |
No |
Requires provider re-instantiation |
database.url |
No |
Requires connection pool rebuild |
channels.gateway.port |
No |
Requires server rebind |
embeddings.provider |
No |
Requires provider re-instantiation |
JSON5/YAML Support
Lower priority since .env + DB settings cover most use cases. If implemented:
- JSON5: Use
json5 crate. Supports comments and trailing commas. Good for capabilities.json files (developer-facing).
- YAML: Use
serde_yaml crate. Better for complex nested config. Could replace .env for power users.
Recommended path: Support JSON5 for capabilities files and WASM tool manifests (developer experience). Keep .env for main config (simplicity).
Success Criteria
- Hot-reload works for dynamic settings: Changing
heartbeat.interval_secs via config set takes effect within one heartbeat cycle without restart
- Non-reloadable settings warn: Attempting to hot-reload
database.url or llm.backend prints "Restart required for this change to take effect"
- Config change audit trail: All config changes logged with timestamp, old value, new value, source (CLI/API/file)
- File watch optional: Hot-reload works via DB polling even without the
notify crate (file watch is a nice-to-have)
- No performance regression: Config access on hot paths (tool output sanitization, approval checks) adds < 100ns overhead
- Backward compatible: Existing
.env + Config::from_env() continues to work unchanged for users who don't use hot-reload
Feature Parity: Configuration System Enhancements
Priority: P2-P3
Source:
FEATURE_PARITY.md— Configuration SystemIronClaw uses
.envfiles with a type-safeConfigstruct. OpenClaw has richer configuration options.Missing
Notes
Configstruct is built once at startup.notifycrate for file watching + channel to signal config changes.envworks well for the current use caseDesign Considerations
Current Config Architecture
Config struct (
src/config.rs:16-33): 14 sub-configs covering database, LLM, embeddings, channels, agent, safety, WASM, secrets, builder, heartbeat, routines, sandbox, and Claude Code.Two loading methods:
Config::from_env()— Env vars only, used before DB connectionConfig::from_db(store, user_id, bootstrap)— DB settings override env varsCritical: Config is NOT Arc'd. The config is cloned into individual components at startup:
Components hold their own copies. Changes to the source
Configdon't propagate.Settings Storage (Already Implemented)
The
Databasetrait provides 5 settings methods:set_setting(),get_setting(),delete_setting(),get_all_settings(),set_all_settings(). The CLI already hasironclaw config list/get/set/reset/path. Settings use flat dot notation:"agent.name","heartbeat.interval_secs".Resolution order: Environment variable → Database setting → Hardcoded default.
Hot-Reload Options
Option A: Component-scoped reload (recommended, minimal refactor)
Keep config cloned to components. Add a reload channel:
Pros: No Arc/RwLock overhead, each component reads on its own schedule, minimal code change.
Cons: Each component needs a watch channel, config push logic per sub-config.
Option B: Shared Arc<RwLock> (comprehensive, significant refactor)
Wrap Config in
Arc<RwLock<Config>>, share across all components. Components callconfig.read()when they need values.Pros: Single source of truth, any change immediately visible.
Cons: ~500 LOC changes across 10+ files, lock contention on hot paths, every config access becomes async.
Recommendation: Option A for initial implementation. Only the most dynamic settings need hot-reload (heartbeat interval, gateway port, routine schedules). Most config (database, LLM provider) requires restart anyway.
Config Change Sources
ironclaw config set heartbeat.interval_secs 900→ writes to DB → notifies running agent/api/settingsendpoint → same flownotifycrate watches.envfile → re-parse → push changesSIGHUP→ reload all config from DBNotification mechanism: Use a Unix domain socket or the existing DB settings table with a
config_versioncounter. Running agent polls or watches for version changes.What's Actually Hot-Reloadable
heartbeat.interval_secsheartbeat.enabledroutines.max_concurrentagent.max_parallel_jobssafety.max_output_lengthllm.backenddatabase.urlchannels.gateway.portembeddings.providerJSON5/YAML Support
Lower priority since
.env+ DB settings cover most use cases. If implemented:json5crate. Supports comments and trailing commas. Good forcapabilities.jsonfiles (developer-facing).serde_yamlcrate. Better for complex nested config. Could replace.envfor power users.Recommended path: Support JSON5 for capabilities files and WASM tool manifests (developer experience). Keep
.envfor main config (simplicity).Success Criteria
heartbeat.interval_secsviaconfig settakes effect within one heartbeat cycle without restartdatabase.urlorllm.backendprints "Restart required for this change to take effect"notifycrate (file watch is a nice-to-have).env+Config::from_env()continues to work unchanged for users who don't use hot-reload