EverRoom 安全模型拆解:本地回环、Bearer Token 与凭据脱敏的隐私设计
【免费下载链接】EverRoomEverRoom - A workspace that remembers your projects, decisions, and sources.项目地址: https://gitcode.com/gh_mirrors/ev/EverRoom
EverRoom 是一个"记得你的项目、决策和信息来源"的本地优先(local-first)个人工作区。它把文档、知识库、上下文房间和 AI Agent 都跑在你的电脑上,因此隐私设计从第一天起就是核心:网关只监听本地回环地址、每次启动随机轮换 Bearer Token、敏感凭据加密落盘并在日志中自动脱敏。本文用 3 个关键模块带你读懂这套隐私设计。
一、本地回环 + 随机 Bearer Token:网关只对"自己人"开门
EverRoom 的桌面端通过一个本地网关进程承载所有核心能力(文档、记忆、Agent、连接器)。它的安全边界非常简单:
- 只绑定
127.0.0.1:网关不监听局域网地址,其他设备无法直接访问你的数据。 - 每次启动随机生成 Token:网关启动时生成一个随机 Bearer Token,只保存在桌面端主进程内存中,并随进程结束失效。
- 每个请求都必须带
Authorization: Bearer <token>,否则直接返回 401。
鉴权逻辑只有 20 来行,核心在 auth.ts 中:
请求进入时,Fastify 的
onRequest钩子对比请求头与Bearer ${token},不匹配即返回 401 的unauthorized响应;只有/v1/health和/docs免鉴权。
Token 的注册与注入在 create-server.ts:
await app.register(auth, { token: config.authToken })—— 鉴权插件在启动时统一挂载,所有业务路由共享同一道关卡。
这套"回环 + 随机令牌"的组合,让网关即使被本机其他程序扫描到端口,也无法在不知道 Token 的情况下读写任何数据。对于桌面应用而言,这是成本最低、效果最好的一道门。
二、凭据加密落盘:AES-256-GCM + 0600 文件权限
连接 Gmail、飞书、Notion 等账号时,网关需要保存刷新令牌等敏感凭据。EverRoom 没有把这些值以明文写进配置文件,而是用了一个"加密保险箱":secret-store.ts。
三个值得注意的细节:
- 整份凭据文件一次性加密。
credentials.enc不是"每条凭据各存一段密文",而是把所有值打包成 JSON 后整体用AES-256-GCM加密,附带version / iv / tag信封结构,任何单字节被篡改都会导致解密失败。 - 文件权限 0600。持久化时先写临时文件再原子替换(
rename,Windows 上降级为 copy+delete 兼容杀软锁定),并强制chmod 0o600,只有当前系统用户可读。 - 主密钥可覆盖。默认内置固定主密钥保证开发/无头环境零交互,生产环境可通过环境变量
NXCORE_SECRET_STORE_KEY替换成自己的密钥。
桌面端同样思路的本地加密在 local-secret-cipher.ts(enc:v1:前缀 + AES-256-GCM,nonce 与认证标签拼进密文),并由 credential-store.ts 统一管理——读到旧明文时会自动迁移为密文,兼顾了历史版本的平滑升级。
💡 历史版本曾用系统钥匙串(safeStorage)托管主密钥;新方案改为内置密钥 + 文件权限保护,彻底消除了"登录解锁钥匙串"的交互,见 gateway-secret-key.ts 中对旧密钥文件的清理逻辑。
三、凭据脱敏:日志与响应中的密钥自动变成[REDACTED]
比"存好密钥"更难的是"别让密钥泄漏"——它可能出现在 HTTP 请求头、错误堆栈、甚至流式日志里。EverRoom 用 secret-redaction.ts 做了三层防御:
| 层级 | 机制 | 说明 |
|---|---|---|
| 关键词匹配 | SENSITIVE_KEY正则 | 键名含api_key / token / secret / password / credential等字段直接替换为[REDACTED] |
| 文本模式匹配 | redactText | 抹掉Bearer <任意串>和 URL 里的token=xxx&signature=xxx等查询参数 |
| 已知值注册表 | registerSecret | 任何写进保险箱的凭据都会登记到内存集合,之后在所有文本(含 URL 编码、表单编码形态)中全局替换 |
几个工程细节体现了成熟度:
- 深层递归 + 循环引用保护:
redactSecrets递归遍历任意 JSON 结构,用WeakMap防循环引用死循环; Date特判:时间戳字段原样透传,避免响应里的日期被摊平成{};- 流式日志防泄漏:
redactDelta按 scope 缓存日志尾巴,保证密钥恰好被跨块截断时也不会半截泄漏到输出里。
这套脱敏同时挂在 Fastify 的preSerialization(所有 API 响应)和 logged-axios.ts(所有对外 HTTP 调用的日志)上,相当于给"出口"装了安检机。
四、加分项:跨设备同步也是端到端加密的
当你把 EverRoom 同步到手机等其他设备时,密钥分发同样不信任中间人。account-keyring-service.ts 实现了一套密钥包协议:
X25519-HKDF-SHA256-AES-256-GCM:每次用临时 X25519 密钥协商出共享密钥,经 HKDF 派生包装密钥,再 AES-256-GCM 加密账号主密钥(UMK);本地私钥本身也用 AES-256-GCM 加密后以 0600 权限落盘。
审批新设备时,界面上会展示一个由设备公钥派生的 12 位校验码(verificationCode),用户肉眼比对确认,防止"中间设备冒充"。密钥包打开失败或版本不匹配时,会明确提示"请在手机上重新批准此设备",而不是带着脏状态继续。
五、给用户的实操建议
- 不要给网关端口做内网映射/端口转发:它只在
127.0.0.1监听,暴露到局域网就等于跳过 Token 之外的全部边界。 - 自托管部署可设置
NXCORE_SECRET_STORE_KEY:用独立主密钥替换内置默认密钥,凭据文件即使被拷贝也无法跨环境解密。 - 看到
[REDACTED]是好事:日志、报错、Agent 输出里出现这个标记,说明脱敏层正常工作,无需担心。
总结
EverRoom 的隐私设计可以浓缩成一句话:网络层用回环 + 随机 Bearer Token 关上门,存储层用 AES-256-GCM + 0600 锁住保险箱,出口层用三层脱敏拦住泄漏。没有复杂架构,每一层都短小、可验证,正是本地优先应用该有的安全姿态。
- 网关鉴权源码:apps/gateway/src/server/auth.ts
- 凭据加密保险箱:apps/gateway/src/security/secret-store.ts
- 脱敏引擎:apps/gateway/src/security/secret-redaction.ts
- 本地加密与设备密钥环:apps/desktop/src/main/security/
【免费下载链接】EverRoomEverRoom - A workspace that remembers your projects, decisions, and sources.项目地址: https://gitcode.com/gh_mirrors/ev/EverRoom
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考