『写入窄、读取宽』company-brain 的权限图设计可能是企业知识库最值得抄的安全范式【免费下载链接】company-brainOpen-sourcing our company brain - A teammate in your Slack that remembers everything your team says, and can go do the work.项目地址: https://gitcode.com/gh_mirrors/co/company-brain当 supermemory 宣布把曾经服务数千用户、营收型产品 Company Brain 整套开源时README 里那句 Used to be a paid product with thousands of users. Now its free and open source 背后是一个真实跑过生产流量的企业级方案最值得研究的不是它的 RAG 管线或 Agent 编排而是一个几乎被所有企业知识库产品回避的问题当一个 AI 能记住一切并动手干活时你怎么保证它不会越权多数团队的回答是给 AI 一个最小权限的 service account或者干脆在向量库里贴一层粗糙的 metadata 过滤。company-brain 给出了完全不同的答案它把权限本身建模成一张图——记忆写入永远落在最小的房间里而读取范围则严格等于提问者本人能看到的一切。社区中已有技术文章将其总结为三种记忆容器 四步混合检索 租约机制的架构本文直接回到源码拆解这套『写入窄、读取宽』权限图是如何落地成可运行代码的。三种记忆容器与权限图的整体结构company-brain 明确拒绝一个共享大脑 一个私人大脑的两桶模型docs/guide/permissions.md 的第一句话就是这个立场。它的记忆容器是三种员工记忆Employee memory每人一份来自你和机器人的 DM以及它慢慢学会的关于你的信息私密频道记忆Private channel memory每个私密频道一份只对该频道成员可见公开频道记忆Public channel memory整个组织共享一份任何公开频道沉淀的持久事实都落在里面。在底层这三种容器不过是 supermemory 的 container tag 字符串。映射逻辑集中在 src/brain/memory/writeback.ts 的slackMemoryContainerTag中export function slackMemoryContainerTag(scope?: SlackMemoryScope): string | undefined { if (scope?.kind dm) { return scope.userId ? privateContainerTagFor(scope.userId) : undefined } if (scope?.kind private_channel) { return privateSlackChannelContainerTag(scope.channelId) } return SHARED_TEAM_BRAIN_CONTAINER_TAG }关键约束是一条消息只写入一个容器——它发生在哪个房间就写进哪个房间的 tag绝不把公开频道的讨论复制进共享桶也绝不让私密频道的内容顺便进入员工记忆。写入路径 src/brain/memory/index.ts 的writeMemory是这条纪律的执行者。值得注意的是写入侧的窄不止体现在容器归属还体现在文档形态。MemoryDocSchema明确规定每条记忆必须是关于一个连贯持久主题的单条自包含记忆要求如果单独阅读也必须有用不能依赖兄弟记忆或线程上下文src/brain/memory/writeback.ts。这从供给侧防止了 Agent 把一整个对话糊成一条又大又脏的记录——窄写入既服务权限也服务检索质量。『写入窄、读取宽』为什么安全与可用性可以兼得权限图的另一半是读取侧。读取容器集合永远是写入容器的超集且房间越私密读取范围越宽这在 src/brain/memory/read-scope.ts 中有精确的实现export function resolveBrainReadContainerTags( agent: CompanyBrainAgent, scope: SlackMemoryScope | undefined, override?: string[], ): string[] { if (override ! undefined) return [...new Set(override)] const tags new Setstring([SHARED_TEAM_BRAIN_CONTAINER_TAG]) const scopeTag slackMemoryContainerTag(scope) if (scopeTag) tags.add(scopeTag) if (scope?.kind dm scope.slackUserId) { for (const tag of readableSlackChannelContainerTagsForUser(agent, scope.slackUserId)) { tags.add(tag) } } return [...tags] }展开来就是提问位置可读取范围公开频道组织共享脑私密频道本频道记忆 组织共享脑与机器人的 DM你的员工记忆 组织共享脑 你所在的每一个私密频道记忆DM 之所以是视野最宽的座位恰恰因为它是最私密的房间——机器人在这里的答案是把你本人能看到的全部拼接起来的。而公开频道的答案永远只来自全组织允许知道的内容。docs 里那张权限图docs/guide/permissions.md用一句话总结了这个拓扑这也是整个设计最反直觉、也最优雅的一点读取宽的三个工程细节值得单独强调第一成员关系表是纯本地的。readableSlackChannelContainerTagsForUser查询的是brain_channel_membership这张 SQLite 表slack_user_id is_private 1按最近活跃排序limit 1000 只是病态场景的护栏不是产品上限全程不调 Slack API——把权限判断从运行时问外部平台变成了读本地索引这是延迟和可用性的关键。第二读取宽度不等于检索成本爆炸。每条查询会按可达容器逐个做混合检索但 src/brain/memory/search-brain.ts 用CONTAINER_SEARCH_CONCURRENCY 6做并发限流防止一个在大量私密频道里的用户触发百路向量查询的洪峰随后按结果 id 去重、保留最高相似度、截断到 40 条。多容器扇出、去重、排序是一条完整的管线而不是简单地把 tag 拼进一个 WHERE。第三标签承担了第二道窄化。focus_tags通过array_contains过滤brain_tags元数据buildBrainFocusFilter让关于某个人/某项目的事实可以在窄容器内进一步收窄避免高相关度噪声跨主题串扰。这套模型最关键的语义保证写在 docs 的 NOTE 里如果你不在某个私密频道里它的记忆对你就不存在——即使在 DM 里也不能靠推理得到。这是拒绝默认deny by default它把权限从过滤器升级成了不可见性。工具访问的镜像设计连接跟着人走记忆的权限图只是第一层。company-brain 的 Agent 会真的去 GitHub、Linear、Notion 里干活工具访问的安全模型与记忆层是镜像对称的总结在 docs/guide/permissions.md 的一张表里读取写入行为优先用你自己的个人连接没有则用组织共享连接只走你自己的个人连接原因给你你应得的最大可见范围把动作归因到真人而不是共享服务账号组织共享连接对包括管理员在内的所有人只读。如果你要求写操作建 issue、评论 PR而团队只有组织级连接机器人会直接告诉你请先个人连接。执行层面src/brain/turn/tools.ts 用actor.personalConnectionsOnly与actor.orgSharedOnly两个开关把连接清单切成两半任何写入型工具都见不到共享连接。这是写入窄在工具域的翻版写必须实名读可以借用。租约机制工具访问如何做到有时效、可追溯当一次请求需要的工具提问者本人和组织都没有连接、只有某位同事个人连接了时company-brain 没有失败也没有偷用——它发起一次租约lease。完整实现分布在 src/brain/lease/ 目录核心常量在 src/brain/lease/types.tsexport const LEASE_TTL_MS 10 * 60 * 1000 // 授权生效 10 分钟 export const LEASE_REQUEST_EXPIRY_MS 15 * 60 * 1000 // 无人应答 15 分钟过期 export const MAX_LEASE_OWNER_FANOUT 5 // 最多同时问 5 位连接所有者整个租约生命周期是一台有状态的状态机值得逐条过一遍申请即显式授权。Agent 调用request_access_leasesrc/brain/lease/tools.ts时系统先排除提问者其实自己有连接连接待重授权等所有更优路径——租约明确是最后手段只在该工具目录中标记为可出借leaseable: true时才走这条路。Gmail 这类personalOnly的服务器被硬编码为不可出借isMcpServerLeaseable会先查NON_LEASEABLE_RESERVED_SLUGS。审批卡片只投递给真正的连接所有者。src/brain/lease/decision.ts 的ownerDeliveryForSlackUser保证只有收到过这张卡、且其连接仍 active 的所有者才能 approve/deny/revoke——Only a connection owner who received this request can approve or decline it是硬性校验不是提示文案。第一个批准即生效其余所有者的卡片被批量 invalidated全员拒绝则整体denied。一切都有 TTL 和看门狗。每个请求在落库的同时通过agent.schedule布置一条过期警报runLeaseEscalation15 分钟无人决策自动作废并删除所有卡片批准的租约 10 分钟到期且每次工具调用前都要revalidateLease——重新校验租约是否 active、提问者是否还是组织成员、出借者的连接是否仍存在且属于本人。连接被删除时revokeLeasesForConnection会批量吊销该连接名下所有活跃租约。一切可追溯。brain_lease表src/brain/lease/store.ts 的ensureLeaseTables完整落盘了 request_id、lease_id、org/team/channel/thread、lessee、lessor、server、mode、reason、status、decided_at、decided_by、revoked_at以及整个回合的控制快照每次决策还通过captureLeaseRequest把 outcome 和时间到决策的毫秒数送入埋点。谁借的、借了什么、谁批的、批了多久、何时失效全部有据可查。模式是能力边界。LeaseMode只有read_only | read_write两档工具 schema 要求needsWrite仅在任务确实要 create/update/delete/send 时才为 true——每个连接所有者看到并批准的正是这一档能力不存在借了写权限干只读的活或反之的模糊地带。租约还绑定回合控制turn control线程里出现了更新的指令旧租约请求会被标记cancelled并回收卡片防止过期请求在错误上下文里继续生效。对企业知识库团队的可迁移经验把记忆图、工具连接和租约三层拼在一起看company-brain 真正值得抄的是五个可独立迁移的决策而不是某个具体类用现有的协作图当权限源别另造 ACL。私密频道成员关系直接来自 Slack员工的可见范围就是频道成员关系的并集。知识库产品最常见的错误是把权限复制成一套与协作平台脱节的映射最终必然漂移。company-brain 的读取权限是实时从成员关系表推导的新增/离开频道自动收敛。写入窄、读取宽不是偷懒是安全定理。让 AI 只把事实写进最小的房间同时允许它按提问者身份读取超集等价于输出范围 ⊆ 提问者可见范围。只要写入路径守纪律、读取路径严格绑定提问者身份幻觉和越权泄漏在结构上就被排除了而不是靠 prompt 提醒别乱说。共享账号只读、写操作永远实名。组织级连接是团队可用性的兜底但代价是写入必须降级到个人连接。这个取舍值得所有 AI 助手团队抄走共享凭据可以帮你读但绝不能替你写——写操作需要可归因。租约是最小临时权限的生产级实现。10 分钟 TTL、15 分钟请求过期、真人审批、回合绑定、连接吊销联动、全量审计字段六个机制缺一不可。许多系统只做了审批就上线结果留下的是永久生效的隐性授权。租约的精髓是权限必须自己死掉TTL 看门狗 重校验而不是依赖谁记得去回收。把拒绝写进类型系统。leaseable: false的硬编码、personalOnly的目录字段、read_only/read_write的模式枚举这些都不是运行时策略而是编译期就存在的边界。团队里最危险的权限漏洞往往来自这个场景没人想过——而显式的类型标注逼着每个接入点回答这个工具能不能被借走。回到开头的问题企业知识库真正缺的从来不是更大的模型或更炫的 RAG而是一套能让记得住一切和不越权同时成立的权限基础设施。company-brain 用 200 多行的读取范围解析、一张brain_lease表和几个常量给出了一个可以被任何团队复制的范式——它的价值不在于这些代码有多复杂而在于它证明了AI 的安全边界应该是图的结构而不是人的自觉。【免费下载链接】company-brainOpen-sourcing our company brain - A teammate in your Slack that remembers everything your team says, and can go do the work.项目地址: https://gitcode.com/gh_mirrors/co/company-brain创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
阅读完成 · 觉得有帮助?