
Atlas 密钥存储机制完整解析为什么它主动放弃 KeychainToken 绝不落盘【免费下载链接】atlasSource control for agents. Use multiple coding agents, track their changes and query them in one place项目地址: https://gitcode.com/GitHub_Trending/atlas115/atlasAtlas 是一款面向多个编码智能体coding agents的桌面工作台它的密钥存储机制是本文核心Atlas 主动放弃操作系统 Keychain把会话凭证Session Token写入权限0600的私有文件而临时访问令牌Access JWT只存活于内存、绝不落盘。下文带你读懂这套设计背后的安全权衡。仓库地址git clone https://gitcode.com/GitHub_Trending/atlas115/atlas先搞懂Atlas 里有哪些密钥很多人以为密钥就是一个东西其实 Atlas 里有三类完全不同的敏感数据存储策略各不相同凭证类型用途存储位置生命周期会话 TokenSession Token长期登录凭证7 天滚动续期私有配置文件atlas-session.json跨启动持久化访问 JWTAccess Token短时调用凭证约 10 分钟 TTL仅内存用完即弃BYOK API Key各家 AI 供应商密钥Model-Chat 调用大模型私有配置文件byok-keys.json跨启动持久化记住这张表后面的所有设计决策都能对应回来能持久化的只有长期凭证因为重启后还得能离线登录短时 JWT 从不写盘——它随时可以从会话 Token 现场铸造mint写下来只会徒增泄露面。这套规则写在认证模块的开头注释里Token material never leaves this module.详见 src-tauri/src/auth/mod.rs。为什么不用系统 Keychain这是本文最值得展开的反直觉决策。主流教程会说密钥当然放进系统钥匙串macOS Keychain / Windows Credential Manager最安全。但 Atlas 的作者明确写了明知故犯的理由见 src-tauri/src/auth/store.rs未签名的二进制 Keychain 每次访问都弹窗。macOS 上一个没有 Developer ID 签名、又频繁重新构建的 App每读取一次钥匙串就会触发一次权限确认。登录、刷新、退出……用户会被弹窗轰炸到抓狂。自动更新会破坏 Keychain ACL。Atlas 的自动更新每次发布都替换二进制文件这会让钥匙串的访问控制列表ACL失效——结果是每次更新后所有用户都要重新授权。发布版反而比开发版更糟。私有配置文件已经足够隔离。app_config_dir()本身就是应用私有目录把密钥放这里不会暴露给其他应用。代码作者还留了一句诚实的注脚Revisit once a real Developer ID signing identity is configured——一旦正式配置签名身份就会重新启用 Keychain。所以这不是不懂安全而是在签名能力落地前的务实降级。顺带一提连 TLS 库都避开了 Keychain项目选用了自包含的rustls注释说明 macOS 的 Security.framework / Keychain 代码路径在沙盒化 Release 构建里会静默失败见 src-tauri/Cargo.toml。会话 Token 如何最小暴露落盘放弃 Keychain 不等于粗放存储。会话凭证的落盘做了三层收敛1. 独立文件一键清除凭证单独存放在atlas-session.json与 BYOK 密钥文件分离。好处很实际退出登录 一次文件删除unlink没有从大 JSON 里摘掉一个字段的中间态。删除操作还是幂等的——文件本来就不存在也算成功store.rs。2. 权限0600每次写入后都会把文件权限收紧为仅属主可读写Unix 下的0o600见 store.rs其他系统用户读不到。3. 损坏即视为登出而不是报错如果文件损坏或不可读Atlas 直接按未登录处理而不是崩溃或弹错误——两种情况的用户动作是一样的重新登录没必要制造额外噪音store.rs。文件里还藏了什么atlas-session.json除了 Token还存了一份身份快照用户名、邮箱、头像本地缓存路径、所属组织列表。这带来一个贴心能力——断网启动也能渲染完整的已登录状态飞机上打开笔记本标题栏照样显示你的头像和组织而不是一块空白或半截信息store.rs。内存 Token铸造一次、用完即弃短时效的访问 JWT 走的是完全不同的路线规则可以浓缩成一句话现场铸造、现场读取、现场丢弃。每次需要组织角色信息时才用会话 Token 去/token接口换一枚 JWT读完orgs声明就扔掉core.rs模块注释里专门解释A cache would buy nothing and would be one more thing this function had to remember to clear——缓存一枚 10 分钟就过期的令牌毫无收益反而多一个退出登录时必须记得清空的隐患core.rs日志侧同样收口错误信息在进日志前会先redact掉 URLURL 是 Token 唯一可能搭便车出现的地方且认证模块从不打印任何 Token 或设备码core.rs。更关键的是一条分类铁律写在 core.rs整个认证模块中只有 401服务端明确拒绝凭证才允许销毁本地凭证。这意味着断网、超时、5xx 全部归类为不确定——凭证原样保留按退避策略重试绝不会因为一次网络抖动就把你登出。这也是Token 不落盘哲学的延伸凭证的生死只能由服务器判决。BYOK 密钥连最后四位都交给前端算给 Model-Chat 配置各家 AI 供应商 API Key 时密钥写入byok-keys.json同样0600、同样弃用 Keychain理由与上文完全一致src-tauri/src/commands/byok.rs。细节上有个值得新手学习的设计列表接口byok_list只返回元数据供应商名 最后 4 位 添加时间前端 UI 永远接触不到完整密钥完整密钥只在byok_get中返回且调用方是后端的 Model-Chat 运行时用于配置 AI SDK——密钥从后端读盘、在后端消费全程不跨 IPC 边界到前端连最后 4 位都是前端算好后传进来的后端不需要切分密钥进一步缩小后端接触完整密钥的窗口。前端对应的密钥管理界面在 src/features/settings/stores/byok-store.ts 和 src/features/settings/components/providers-settings.tsx。退出登录先清本地后通知服务器最后看密钥消失的完整流程core.rs顺序被设计得非常刁钻先删头像缓存——因为身份文件里记录着头像路径先删凭证文件就会留下有脸无主的孤儿文件再删凭证文件——无条件执行不依赖任何网络调用所以 WiFi 关掉也能退出最后拿着吊销凭证单通知服务器——本地状态已空才可能拿到这张一次性凭证单RevocationTicket物理上杜绝了先吊销后清本地的错误顺序服务器通知失败诚实告知用户服务端会话可能残留但不重试——反正它会自行过期而重试机制在凭证已清空时已无保护目标。这个顺序即类型The ordering the ticket exists for is written as a type的写法值得借鉴把流程约束编码进数据结构而不是依赖开发者记得先做 A 再做 B。总结三层密钥存储策略速查层级策略对应实现长期凭证私有目录 0600文件独立存放src-tauri/src/auth/store.rs短期 JWT内存铸造、单次消费、无缓存src-tauri/src/auth/core.rs第三方 API Key私有目录 0600前端只见元数据src-tauri/src/commands/byok.rsAtlas 给出的答案很有参考价值不用 Keychain不是偷懒而是在签名能力缺失时避免把体验做坏Token 不落盘则把攻击面压到最小。对于任何要处理用户凭证的桌面应用这套能少存就少存、存了就收紧权限、生死只由服务器判决的原则都可以直接抄作业。【免费下载链接】atlasSource control for agents. Use multiple coding agents, track their changes and query them in one place项目地址: https://gitcode.com/GitHub_Trending/atlas115/atlas创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考