Day 19 / 共 20 天 · 第 4 周 前端/安全/生态

enterprise 企业版扩展

OpenHands 不只是单机工具,还能做成企业级 SaaS。本仓 enterprise/ 目录就是这层。今天看它如何在核心之上加多租户、鉴权、计费、集成——而不改动核心。

📍 第 4 周 前端与生态 · 你在这里
Day16 前端展示Day17 安全权限Day18 微代理SkillsDay19 企业版Day20 构建收官
L01

OSS 模式 vs 企业版

回忆 Day 10:config.py 根据部署模式选不同实现——比如生命周期钩子,OSS 用 OssAppLifespanService(跑 Alembic 迁移),SaaS 用 SaasAppLifespanService(初始化 PostHog 埋点)。enterprise/ 目录就是这套"SaaS 增强"的集中地。

维度OSS(开源单机)Enterprise(企业 SaaS)
用户单用户,无需登录多租户,需鉴权
付费免费,自带模型 key计费、预算、配额
集成基础GitHub/GitLab/Slack 深度集成
协作个人会话分享、团队 profile
同一套核心,两种形态 核心(app_server + Agent)不变,企业版是"外挂"上去的一圈增强——加登录、加计费、加集成。这靠的是 Day 06 的依赖注入:企业版提供自己的 Service 实现(如带鉴权的用户服务),通过配置注入替换掉 OSS 的简单实现。核心保持纯粹,增值功能作为可插拔层——这是开源 + 商业化双赢的经典架构。
💡 本质:同一套核心,OSS 是"单人工作室",企业版是"写字楼"就像生活中从"在家单干的工作室"升级成"多家公司合租的写字楼":干活的核心能力(写代码、做设计)没变,但写字楼多了一圈公共设施——前台门禁(鉴权)、按工位收租(计费)、多家公司各占各的楼层互不打扰(多租户隔离)、会议室快递等配套(集成/分享)enterprise/ 目录就是这圈"写字楼配套",装在同一套核心之外。
L02

enterprise 目录一览

enterprise/
├── server/
│   ├── routes/          # 22 个企业级路由:auth, billing, api_keys,
│   │                    #   github_proxy, agent_profiles, integration...
│   ├── auth/            # 鉴权(Keycloak)
│   ├── maintenance_task_processor/  # 后台维护任务
│   ├── rate_limit.py    # 企业级限流
│   ├── sharing/         # 会话分享
│   └── verified_models/ # 认证过的模型清单
├── integrations/        # GitHub/GitLab/Slack/Jira 等深度集成
├── migrations/          # 企业版自己的 DB 迁移(135 个版本文件)
├── saas_server.py       # SaaS 服务入口
├── run_budget_maintenance.py   # 预算维护任务
└── storage/             # 企业级存储实现
读法:企业版结构和 app_server 平行——也有 routes/auth/storage,但都是"企业增强版"。比如它有自己的 saas_server.py(SaaS 入口)、自己的 migrations(比 OSS 多得多,135 个版本,因为企业功能表更多)。
L03

鉴权(Keycloak)

OSS 单机无需登录,企业版必须区分用户。enterprise/server/auth/ + routes/auth.pyoauth_device.py 实现了完整的身份认证(对接 Keycloak 这类身份平台,仓库里有 allhands-realm-github-provider.json.tmpl 即 Keycloak realm 模板)。

为什么企业版鉴权这么重 回忆 Day 08/17 的"用户隔离靠 user_id 前缀"——那套隔离要成立,前提是能可靠地知道"当前请求是哪个用户"。企业鉴权就是解决这个:用户登录(可能用公司的 SSO/GitHub 账号),拿到身份,之后每个请求都带着这个身份,app_server 用它做数据隔离、计费归属。鉴权是多租户安全的入口关卡——没有可靠的"你是谁",后面的"你能看什么"都无从谈起。
👶💬 对话体:多租户到底难在哪?加个登录不就行了? 👶 小白:不就是加个登录框吗,有啥复杂的?
👨‍🏫 老师:登录只是第一步——难的是登录后,怎么保证"公司 A 的人绝对翻不到公司 B 的会话、沙箱、账单"。
👶 小白:那靠什么保证?
👨‍🏫 老师:靠把 user_id 编码进每一处资源标识(存储路径 {prefix}/{user_id}/...、沙箱归属)。鉴权先可靠地确定"你是谁"(Keycloak/SSO),之后所有访问都被这个 id 框住。就像写字楼门禁卡:刷卡确认你是哪家公司的(鉴权),电梯就只让你到自己那层(隔离)。
🤔 如果没有可靠鉴权会出什么事故?假设鉴权能被伪造或缺失——用户 A 一改 URL 里的 id 就拉到了用户 B 的会话记录、甚至操作了 B 的沙箱。对 SaaS 这是致命的数据泄漏。所以企业版把 Keycloak 这类成熟身份平台放在最前面当"门禁",正是为了堵死这种越权。
📝 举个例子用户 alice 用 GitHub 账号登录 → Keycloak 发一个带身份的 token → 请求携带该 token 访问会话 → app_server 解析出 user_id=alice → 只在路径 .../alice/conversations/ 下找数据。若 alice 手改 URL 想访问 bob 的会话,因为她的 token 里是 alice,拼不出也无权读 bob 的路径 → 被拒。
写字楼门禁:刷卡 → 只准到自己那层 用户登录 GitHub / SSO Keycloak 门禁 发带身份 token app_server 解析 user_id 只读自己 那层数据 user_id 编码进存储路径 = 电梯只让你到自己楼层
图:鉴权确定"你是谁",路径前缀隔离决定"你能看什么"
L04

计费与预算

routes/billing.py + run_budget_maintenance.py 处理付费。回忆 Day 14 的用量/成本统计——企业版把它接到计费系统:用户消耗的 token 折算成费用、扣预算、超额限制。

成本经济学的商业化落地:Day 04/14 讲的 max_budget_per_task、token 统计,在企业版里升级成完整的"配额 + 计费 + 预算维护"体系。run_budget_maintenance.py 是定时任务,定期结算/重置预算。把"每个动作花多少钱"一路追踪到"每个用户/团队的账单"——这是 AI 产品商业化的基础设施。
L05

Git 平台深度集成

enterprise/integrations/ + routes/github_proxy.py/bitbucket_dc_proxy.py/integration/ 提供和 GitHub、GitLab、Bitbucket、Slack、Jira 等的深度集成。

深度集成能做什么?(呼应 README 的自动化愿景) Day 01 提过 OpenHands 能"把 GitHub issue 自动拆成任务、生成报告推到 Slack"。这些自动化就靠集成实现:GitHub 上开个 issue → 触发 OpenHands 起个会话去解决 → 完成后自动提 PR;或 Slack 里 @机器人下任务 → Agent 干完在 Slack 回复github_proxy.py 这类"代理"让 OpenHands 能安全地代表用户操作这些平台(用 OAuth 授权)。集成把 Agent 从"聊天框里的工具"变成"嵌入你工作流的自动化队友"。
L06

会话分享

enterprise/server/sharing/ 让用户能把一次会话(Agent 干活的完整过程)分享给同事看。

为什么能轻松实现分享?——回到事件流。还记得 Day 03 说"会话 = 一条可持久化的事件流"、Day 08 说"事件全存成文件、能导出(iter_events_for_export)"吗?正因为整个会话就是一串结构化事件,"分享会话"= 把这串事件给另一个人回放——天然可行。好的底层抽象(一切皆事件)让上层功能(分享、审计、回放)几乎白送。这再次印证第 1 周事件模型的威力。
会话分享就像写字楼里"把会议全程录像发给没到场的同事":因为会议全程被完整录了下来(事件流持久化),转发录像给同事回放是天然可行的——不用额外重开一场会。
L07

靠什么优雅扩展(不改核心)

企业版这么多功能,核心 app_server 却几乎没被改动。靠三个机制(都是前面学过的):

  • 依赖注入(Day 06):企业版提供自己的 Service 实现(带鉴权的用户服务、企业存储),通过配置注入替换。
  • 路由聚合(Day 10):企业路由作为额外的 router 挂上,不动 OSS 路由。
  • 模式选择(Day 10):config.py 按 app_mode 选 OSS/SaaS 的不同实现(如 lifespan)。
"对扩展开放,对修改封闭"(开闭原则)——想加企业功能,新增实现和路由,而非修改核心代码。这样 OSS 核心保持简单干净、社区能持续贡献,企业增值层独立演进、不互相拖累。这套架构让"同一个开源核心"既能给个人免费单机用、又能撑起商业 SaaS——这是很多成功开源项目的商业模式基础。
L08

今日小结 + 动手

🧠 今天你应该能回答

  • OSS 和企业版的区别?同一核心怎么支撑两种形态?
  • 企业版加了哪些能力?(鉴权/计费/集成/分享)
  • 企业鉴权为什么是多租户安全的入口?
  • 为什么"会话分享"能轻松实现?(回到事件流)
  • 靠哪三个机制做到"扩展不改核心"?开闭原则是什么?

✋ 动手

ls enterprise/server/routes/          # 22 个企业路由
ls enterprise/server/ enterprise/integrations/
grep -rn 'SaasAppLifespanService\|app_mode\|AppMode' openhands/app_server/config.py | head
cat enterprise/README.md | head -40
明天预告 · Day 20(结业)构建、测试与收官串讲——怎么本地跑起来开发、测试怎么组织、从零用 OpenHands 完成一个任务的清单、全 20 天知识地图、以及贯穿始终的设计哲学。
← Day 18 微代理 Day 20 · 收官 →