开源桌面应用 OpenWork 的许可证分层:MIT 与 /ee 目录边界如何影响自建与商用
本文基于 openwork 仓库 commit 3b41381(2026-08-03)梳理,该项目仍在高频迭代,具体行为以仓库 https://github.com/different-ai/openwork 最新代码与文档为准。
把 OpenWork 记成一个 MIT 开源项目,是读这个仓库最容易犯的错——它的根 LICENSE 一上来就把自己拆成了三段,而你越往团队管控的方向走,碰到的越不是 MIT 那一段。 这不是文字游戏。它决定了你 fork 之后哪部分能随便改随便发,哪部分改完还得把条款一起带走;也决定了你打算基于它做点商业化的时候,是完全没有约束,还是要先回去读一遍 Competing Use 的定义。
站内已经有几篇偏方法论和清单的文章:开源项目选型的通用判断方法讲怎么给候选项目打分,开源 AI 工具的横向梳理和开源 Agent 平台盘点负责铺面。这篇不重复那些工作,只钉死 OpenWork 这一个仓库的许可证结构,把条文、目录、依赖关系对上号。
需要先说清楚:本文不提供法律意见。凡是涉及”这样用算不算违规""这样改能不能发”的判断,一律以许可证原文为准,必要时找你自己的法务。
一、先把两份许可证的原文读一遍
仓库根目录的 LICENSE 只有 29 行,版权行是 Copyright (c) 2026-present Different AI, Inc.,紧接着一句 Portions of this software are licensed as follows,然后是三条:
第一条,/ee 目录下的全部内容,按 ee/LICENSE 里定义的许可证走,原文在括号里管它叫 Fair Source License。第二条,所有被并入的第三方组件,按各自组件所有者提供的原始许可证走。第三条,除此之外的内容,按 MIT 走,版权行是 Copyright (c) 2026 Different AI。
然后你翻开 ee/LICENSE,第一行标题是 Functional Source License, Version 1.1, MIT Future License,缩写写在 Abbreviation 一节里:FSL-1.1-MIT,Notice 一节的版权行是 Copyright 2026 Different AI Inc。注意根 LICENSE 称它 Fair Source,ee/LICENSE 自称 Functional Source——两处措辞不一致,你在内部做合规登记的时候,登记的应该是 ee/LICENSE 里自报的那个名字和缩写。
FSL-1.1-MIT 的正文值得逐段看:
License Grant 一节给的权利其实很宽——use、copy、modify、create derivative works、publicly perform、publicly display、redistribute 都在里面。但它有个前置定语:只限于下面定义的 Permitted Purpose,并且要遵守 Patents、Redistribution、Trademark 三个条款。
Permitted Purpose 一节反着定义:任何不属于 Competing Use 的目的都是被许可的。Competing Use 指”把本软件放进一个商业产品或服务提供给他人”,且该产品或服务满足三种情形之一:替代本软件;替代许可方在提供本软件之日已经在提供的、基于本软件的其它产品或服务;提供与本软件相同或实质相似的功能。同一节还正面列了四类明确属于 Permitted Purpose 的用法:你的内部使用与访问;非商业教育;非商业研究;以及在你向某个按本条款使用本软件的被许可方提供专业服务时使用。
Redistribution 一节说得很直白:条款适用于本软件的所有副本、修改与衍生;你分发任何副本、修改或衍生,必须附上条款本身或条款的链接,并且不得移除随软件提供的版权声明。Trademarks 一节把商标权收得很紧——除了展示许可证信息和标明软件来源,你没有使用其商标、商号、服务标记或产品名称的权利。
最后是 Grant of Future License:许可方不可撤销地额外授予你一份 MIT 许可,在”我们提供该软件之日起满两周年”时生效。这句里的”该软件”,在 The Software 一节被定义为”我们按本条款提供的每一个版本”——所以这个两周年是逐版本计算的,不是整个项目共用一个解锁日。
二、这条边界在目录上落在哪
条文读完,下一步是把它翻译成路径。判断规则只有一条:文件在不在 ee/ 下面。全仓 3490 个受版本控制文件里,ee/ 下面占了一千出头。
| 组成部分 | 它负责什么 | 仓库位置 | 你什么时候会碰到它 |
|---|---|---|---|
| 桌面客户端与本地服务 | Electron 外壳、应用界面、本地服务端 | apps/desktop、apps/app、apps/server(apps/ 共 4 个) | 装桌面应用、改界面、跑 pnpm dev |
| 共享基础包 | 类型、UI 组件、路径解析、安装配置等 | packages/types、packages/ui、packages/paths、packages/install-config(packages/ 共 12 个) | 二次开发时被引用 |
| 远程 MCP 客户端实现 | 服务端消费远程 MCP 的 OAuth 与工具目录生命周期 | packages/enterprise-mcp-client/src(含 oauth-provider.ts、tool-catalog.ts) | 自己接外部 MCP 服务 |
| Den 控制面 API | 组织、成员、SSO、桌面策略、能力检索与执行 | ee/apps/den-api/src(含 mcp/、entitlements.ts) | 想自建团队控制面 |
| Den 网页端与推理服务 | 登录与管理台、模型代理与计量 | ee/apps/den-web、ee/apps/inference(ee/apps/ 共 10 个) | 想把控制面托管给团队用 |
| 控制面数据层与管理 MCP | 数据库 schema 与迁移、管理端 MCP | ee/packages/den-db、ee/packages/den-admin-mcp(ee/packages/ 共 3 个) | 自托管运维与升级迁移 |
| 分发方式 | 容器、K8s、Arch 打包三条路 | packaging/docker、packaging/helm/openwork-ee、packaging/aur | 上 K8s 或自己打安装包 |
| 文档与流程稿 | 接入指南、架构说明、流程用例 | packages/docs(57 份 mdx,其中 model-context-protocol/ 10 份客户端接入指南)、docs/(20 份 md)、evals/(顶层 26 份流程 md) | 排查行为、对齐部署姿势 |
有个细节容易误读:packaging/helm/openwork-ee 这个 chart 名字里带 ee,但它本身在 ee/ 目录之外。Chart.yaml 里的描述是部署 Den 控制面、web 应用和可选的推理服务——也就是说,打包脚本在 MIT 侧,被打包的东西在 FSL 侧。你分发 chart 和分发镜像,面对的不是同一份条款。
三、依赖方向决定了这个开放核心能不能切干净
很多”开放核心”项目的问题在于核心和企业版互相纠缠,你想只留开源部分,一编译就发现少了一堆符号。OpenWork 这里可以自己验一遍。
pnpm-workspace.yaml 的 packages 段列了四个 glob:apps/*、packages/*、ee/apps/*、ee/packages/*。一个 workspace 里同时装着两种许可证的包,这是前提。
再看依赖声明的方向。只数跨到 MIT 侧的那些工作区依赖:ee/apps/den-api/package.json 声明了 @openwork/connect-link、@openwork/email、@openwork/enterprise-mcp-client、@openwork/install-config、@openwork/types;ee/apps/den-web 声明了 @openwork/types 和 @openwork/ui;ee/packages/den-db 声明了 @openwork/types。这三个包同时也各自依赖同侧的 @openwork-ee/*(比如 @openwork-ee/utils),那是 FSL 内部的事。反过来查 apps/* 和 packages/* 的 package.json,没有任何一个声明 @openwork-ee/* 依赖。
这是单向的。FSL 侧依赖 MIT 侧,MIT 侧不依赖 FSL 侧。命名也跟着这条线走:MIT 侧的包统一是 @openwork/ 前缀,FSL 侧统一是 @openwork-ee/ 前缀。
对你意味着两件事,一件好一件不那么好。好的是:把 ee/ 整个删掉,MIT 侧仍然自洽,你拿到的是一个能独立成立的桌面客户端加本地运行时加共享包。不那么好的是:你拿到的就只有这些。组织、成员、角色、SSO、桌面策略下发、跨成员的能力发布与分配,这些代码都在 ee/ 下面,删了就得自己写。
桌面侧还有一处值得看。apps/desktop/electron/desktop-distribution.mjs 里定义了三种分发口味:public、cloud、enterprise,每种带 requireSignin 和 requireActivation 两个布尔字段——public 两个都是 false,cloud 要求登录,enterprise 两个都要求。对应的构建配置 apps/desktop/electron-builder.enterprise.yml 把 productName 设成 OpenWork Enterprise,并在 extraMetadata 里写 openworkDistribution: enterprise。这段门禁判断的代码本身在 MIT 侧可读可改,它要连上去的那个控制面在 FSL 侧——客户端全开,客户端连什么、被谁管才是收口的地方。
四、自建、二次开发、商用分别对上哪一条
只跑桌面应用和本地能力,你全程都在 MIT 那一段里。仓库 README 这样定位自己:桌面应用是”当你想要一个专门的工作区时”才用,并不是必需的,你可以从已有的 agent 里用它。packages/docs/model-context-protocol/ 下的 10 份接入指南分别对应 chatgpt、claude-code、claude-desktop、codex、cursor、gemini-cli、opencode、vs-code、windsurf、zed。这里要留个心眼:README 给出的接入命令里那个 URL 指向的是官方托管端点,加完之后客户端会打开浏览器让你登录并选择组织——那不是你自己的服务。
自建到需要团队控制面,就跨进 /ee 了。self-host 文档把要部署的东西拆成 OpenWork app、Den web、Den controller、可选的推理服务四块,Kubernetes 路径用的正是那个叫 openwork-ee 的 chart。FSL 的 Permitted Purpose 一节明确把”你的内部使用与访问”列了进去,公司内部自用这条路在条文里是写着的;但具体到你的部署形态算不算内部使用,以原文为准。
二次开发,两侧规则不同。MIT 侧改完怎么发都行,保留版权与许可声明即可。/ee 侧改完,Redistribution 条款把条款本身绑在了副本、修改和衍生上,你分发的时候得附条款或链接,并且不能把原版权声明摘掉。
商用,MIT 侧不设限。/ee 侧的红线就是 Competing Use 那三条——尤其第三条”提供相同或实质相似的功能”,覆盖面比多数人第一眼读到的要宽。另外别忘了 Future License 是逐版本两周年,你今天从某个版本分叉出去,解锁时点跟着那个版本走,不是跟着项目走。
仓库里还有一处对边界的直白自述。docs/enterprise-plan-gating.md 这份设计稿写的是把 SSO 和桌面策略放进企业计划的门禁方案:原则是”限制管理动作(写),不限制交付(读)和移除(删)“,失去权益的组织现有配置照常运行,只是不能再新增或编辑;实现上返回 HTTP 402,权益判断放在 ee/apps/den-api/src/entitlements.ts;总开关是环境变量 DEN_PLAN_GATING_ENABLED,默认关闭。文档里对自托管的说法是:自托管安装保持关闭,除非运维方主动打开;并且直接点明,这些代码本来就在 /ee(FSL-1.1-MIT)下,那里就是这些功能的许可证边界。这是文档里的写法,具体规则会调整,以官方最新说明为准。
五、边界与代价:这个结构放弃了什么
放弃的第一样东西是”一份许可证走天下”的确定性。 混合仓库意味着每一次代码搬运——哪怕只是抄一个工具函数——都得先问一句这个文件在不在 ee/ 下。这是持续成本,不是一次性成本。
第二,Competing Use 是目的性判断,不是技术性判断。 你能自动化检查的只有路径:文件在不在 ee/。“你做的东西算不算替代品”这件事,CI 查不出来,只能人来判断,而且不同人判断结果可能不同。这个不确定性是 FSL 这类许可证的固有代价,换来的是两年后自动转 MIT 的确定性。
第三,Future License 的逐版本计时需要你自己记账。 长期维护分叉的团队要记录自己是从哪个版本、哪个时间点拿的代码,否则两年后你也说不清哪些部分解锁了。
它明确不管的部分也要看清。 根 LICENSE 第二条把所有第三方组件推回各自的原始许可证。这个仓库有 patches/ 目录,pnpm-workspace.yaml 的 patchedDependencies 里明确列了对 @modelcontextprotocol/sdk 和两个 @better-auth 包打的补丁——补丁改的是别人的代码,跟着别人的许可证走,跟根 LICENSE 的 MIT 那一段没关系。
更要紧的是:许可证不管数据流向。 这一点在这类工具上格外值得说清。它是装在你机器上的桌面应用,代管模型凭据和第三方服务的 OAuth 授权(packages/enterprise-mcp-client/src 下 oauth-provider.ts、oauth-discovery-binding.ts、authorization-response.ts 这一整套就是干这个的),并且一旦你的桌面激活到某个组织的控制面,管控关系就建立了。
具体到”企业侧能看到什么”,文档里写得很清楚:桌面策略配置由控制面通过 GET /v1/me/desktop-config 下发,策略目录的权威定义在 packages/types/src/den/desktop-policies.ts,布尔型策略键取 false 表示该功能被限制。审计侧,packages/docs/cloud/security-and-operations.mdx 列出的组织审计事件包括 API key 的创建与删除、成员角色变更与移除、邀请的创建刷新与取消、自定义角色的增改删、SCIM token 轮换与连接删除、SSO 注册与删除——这些是特权管理操作的记录。
把两件事分开看:许可证解决的是”你能不能改、能不能卖”,暴露面解决的是”你的凭据和操作被谁看见”,两条独立的评估线,任何一条不过关都不该上。凭据集中保管的通用风险,AI 工具的数据安全风险那篇讲得更细;MCP 侧授权怎么收紧,可以看MCP 授权加固。
六、上手与避坑清单
不要用 package.json 的 license 字段判断许可证。 会踩是因为这个字段在这个仓库里基本是缺的:ee/ 下 11 个有 package.json 的包,一个 license 字段都没写;MIT 侧也只有 apps/server、packages/handsfree、packages/openwork-ui-mcp 三个包写了 MIT。你要是写个脚本扫 license 字段,会得到一份几乎全空的表,然后误判成”没声明就是随便用”。避法只有一条:认路径,不认字段——文件在 ee/ 下按 ee/LICENSE,其余按根 LICENSE。
不要把 README 里的接入命令当成自托管方案。 会踩是因为那几条命令看起来就是本地配置,实际 URL 指向的是官方托管端点,执行完还会拉起浏览器登录并让你选组织,等于把这台机器接进了别人的控制面。避法:要自托管就照 self-host 文档走,把 baseUrl 配成你自己的 Den web 源站,桌面端会从 <baseUrl>/api/den/v1/... 和 <baseUrl>/api/den/mcp/... 这两条路径派生 API 与 MCP 流量,不再另走独立 API 源站。
不要以为”只用 MIT 部分”就自动没有企业侧暴露面。 会踩是因为门禁标志位(requireSignin、requireActivation)确实在 MIT 侧的 desktop-distribution.mjs 里,读起来像是本地可控;但它连上去的控制面在 FSL 侧,激活之后桌面策略由那边下发。避法:装机前先确认你连的是哪个 Den 源站,以及组织侧启用了哪些策略键。
不要在一个 checkout 上起多个开发实例。 会踩是因为拿不到 profile 锁的第二个实例会直接退出——README 特意提到这是改过的行为,早先它会挂在那儿开着 CDP 端口却没有窗口。同一段还写了另一个坑:dev:worktree 默认把 OPENWORK_ELECTRON_USE_MOCK_KEYCHAIN 设成 1,因为全新 profile 没有存过凭据,macOS 上 Chromium 一持久化认证 cookie 就会弹出真实钥匙串对话框,而那个模态框会阻塞 Electron 主循环。避法:多 worktree 并行就用 pnpm dev:worktree,它把 OPENWORK_DEV_PROFILE 设成 auto、由路径派生稳定的 profile 名、让 Electron 自选空闲 CDP 端口、让 Vite 自选 dev server 端口;确实要用系统钥匙串再把那个变量设回 0。
不要把开发用的数据库连接串带进生产。 会踩是因为文档里给的本地 Docker MySQL 连接串是明文 root 口令的,复制粘贴太顺手。self-host 文档明确说那个串只供开发、不构成生产安全姿态,并要求 DEN_DB_ENCRYPTION_KEY 至少 32 个字符且唯一(它加密的是数据库里选定的敏感列,不替代基础设施层的静态加密)。避法:生产单独配 TLS 证书校验(sslmode=verify-ca、sslmode=verify-full 或等价的 sslaccept=strict,并提供 CA 包),以及数据库、备份、对象存储、卷、日志的静态加密。
不要默认 patches/ 下的东西跟主仓一个许可证。 会踩是因为它就在仓库里,看起来自然继承。实际根 LICENSE 第二条把第三方组件交回原始许可证,而这些补丁改的正是第三方包。避法:分发前把 patchedDependencies 里列的依赖单独过一遍合规。
收尾:四问自检
真要把 OpenWork 用起来,动手前问自己四个问题,答案都能从仓库里查到:你要用的那个功能,文件路径在不在 ee/ 下面(这决定了后三问按哪份条款回答)?你打算怎么分发——只在公司内部跑,还是包进对外的产品或服务(后者必须回去逐字读 Competing Use 那三条)?你的桌面端会激活到哪个控制面,那边启用了哪些策略键和审计项?你依赖的第三方组件和补丁过合规了吗?
接下来该读的文件按这个顺序:LICENSE(29 行,读完就知道边界在哪)、ee/LICENSE(重点是 Permitted Purpose 和 Grant of Future License 两节)、pnpm-workspace.yaml(看清哪四个 glob 被装进了同一个 workspace)、docs/enterprise-plan-gating.md(仓库里对这条边界最直白的一处自述)。四个文件加起来不到半小时,比看任何二手解读都准。
以上都是对仓库文件的梳理,不是法律意见。真要落到商业决策上,把这两份许可证原文交给你的法务,让他们给结论。
本篇属于一个把开源AI 工作流桌面应用 OpenWork逐层拆开讲的系列,整体地图见 OpenWork 是什么:把技能与 MCP 打包成能力的开源桌面应用;沿着这条线往下,还可以看 OpenWork 开源桌面应用仓库导读:改一处功能该从哪个目录进去 和 读 OpenWork 开源桌面应用源码:主进程、预加载、运行时的职责边界。