一. 文件锁避免重复实例
let _pid_lock = match ironclaw::bootstrap::PidLock::acquire() {//尝试获取
Ok(lock) => Some(lock),
Err(ironclaw::bootstrap::PidLockError::AlreadyRunning { pid }) => {//已有运行,则取消本次运行
anyhow::bail!(
"Another IronClaw instance is already running (PID {}). \
If this is incorrect, remove the stale PID file: {}",
pid,
ironclaw::bootstrap::pid_lock_path().display()
);
}
Err(e) => {//无权限等错误到这
eprintln!("Warning: Could not acquire PID lock: {}", e);
eprintln!("Continuing without PID lock protection.");
None
}
};二. Agent startup
1. Enhanced first-run detection
避免没有必须的配置的情况,去运行agent
if !cli.no_onboard
&& let Some(reason) = ironclaw::setup::check_onboard_needed()
{
wizard.run().await?;//同直接ironclaw onboard,走一遍引导2. 最终Agent参数获取
let toml_path = cli.config.as_deref();//同Onboard Setting获取的文件
let runtime_overrides = ironclaw::config::RuntimeConfigOverrides {//这里是运行时安全配置重写
deployment: cli.deployment_mode,//包括部署模式
profile: cli.runtime_profile,//运行画像 ,这些都会影响后面agent的行为决策
yolo_disclosure_acknowledged: if cli.yolo_disclosure {
Some(true)
} else {
None
},
};
let config = match Config::from_env_with_toml(toml_path)//将用从toml读取构造的setting给config配置
.await
.and_then(|c| c.with_runtime_overrides(&runtime_overrides))
1. Settings::default()
2. Profile preset
3. TOML 文件
4. 从 OS 凭据存储注入(Keychain / Linux credentials)
5. 从加密 DB 注入的 LLM 密钥
6. 用户显式设置的环境变量(.env / IRONCLAW_* / shell)— 最高3. 日志广播初始化
生产者(产生日志方):业务代码直接调 tracing::info!() / error!() 等宏,不需要感知 web、订阅者、SSE 这些东西。tracing-subscriber 框架会把日志路由到 Layer,Layer 再推给 broadcaster。
消费者(需要日志方):SSE/WebSocket handler 调用 subscribe() 建立订阅,broadcaster 维护一个活跃连接列表,每次收到新日志就遍历推送。
简单说就是:
业务代码 (tracing!宏)
↓
tracing Layer (log_layer.rs 的 on_event)
↓
Broadcaster::send() ← 所有在线订阅者收到
↓
SSE handler / WebSocket handler (subscribe → receiver)
↓
客户端浏览器
Layer 本身充当了桥接层——一边接 tracing 框架的被动回调,一边接主动的订阅/广播机制,业务代码完全不用改。
let log_broadcaster = Arc::new(LogBroadcaster::new());三. AppBuilder(构建核心组件)
buildAll()
init_database//连接数据库,获取句柄;清理"残留 sandbox 任务"——上次进程崩溃可能留下未结束的 sandbox job 记录init_secrets(&mut self)
拿master key:环境变量、Keychain (macOS) / Credentials file (Linux)
拿到后let crypto = Arc::new(SecretsCrypto::new(master_key.clone())?);构造加密引擎
构造真正的持久化 secrets storelet store = create_secrets_store(crypto, handles);
如果本进程自动生成了新 key,但 DB 里已有历史密钥行,新 key 解不开旧行——继续往下走会静默遮盖无法恢复的数据。一旦发现,回滚 key 持久化(让下次启动重新触发检测),并 fail-
closed。
crate::config::inject_llm_keys_from_secrets(secrets.as_ref(), &self.config.owner_id)注入配置将加密llm key
除了固定读的,还有动态发现的(provider.json文件里,有配置什么模型需要什么变量)
DB 里是加密的,内存里是明文的——明文只存在于进程运行时的 INJECTED_VARS,进程退出就清掉。
若缺失补上config.llmconfig的apikey