引言
当一个项目从「个人玩具」成长为「被公司产品依赖的基础设施」时,维护者迟早会面对一个绕不开的问题:我凭什么有权分发这些人提交的代码? 许可证只约束了「出站」(outbound,项目分发给用户),它没有解决「入站」(inbound,贡献者把代码给你)的授权链条。CLA 与 DCO 就是补上这一环的两套机制。
工程上真正的难点不在法律文本本身,而在于三者之间的张力:法务要的是可追溯、可执行的权利链条(最好还能支持未来的双许可与商业版),维护者要的是低摩擦、不吓跑贡献者(每多一个步骤就流失一批人),贡献者要的是不被「白嫖版权」(尤其是拿公司邮箱提交时,个人根本没有处分权)。这三方诉求很难同时满足,任何选择都要付出代价。
现实中失败的姿势通常有两种:一是小项目照抄大厂流程,上来就要求签版权转让的 CLA,结果 PR 数断崖下跌;二是商业公司做 Open Core,为了省事只用 DCO,等真要发商业版时才发现根本没有 relicense 权,只能回头一个个找贡献者补签——而那些人的邮箱早已失效。这两种错都源于同一个误解:把 CLA 和 DCO 当成「同一件事的严格版和宽松版」。
本文按一条完整链路展开:先讲两者的法律本质差异,再横向对比主流 CLA 模板条款,接着拆解 DCO 的 Signed-off-by 机制与公司贡献者授权,深入版权与专利授予条款,然后给出四套自动化校验配置,讨论社区抵触的沟通策略与迁移遗留处理,最后落到一张选型矩阵。如果只想看结论,直接跳到第 9 节;如果还在选许可证阶段,建议先读 许可证合规与 SPDX 实践 。
目录
- CLA 与 DCO 的本质区别
- 主流 CLA 条款对比
- DCO 的 Signed-off-by 机制
- 公司贡献者与雇主授权
- 版权与专利授予条款
- 自动化校验配置
- 社区抵触与沟通
- 迁移与历史遗留处理
- 选型矩阵
1. CLA 与 DCO 的本质区别
CLA(Contributor License Agreement,贡献者许可协议)是一份合同:贡献者以自然人(ICLA)或公司(CCLA)身份主动签署,向项目方或基金会授予版权许可,通常还附带专利许可。它需要一次性的签署动作、需要保存签署记录、需要能证明「某年某月某人签了」。法律强度高,但流程有门槛。
DCO(Developer Certificate of Origin,开发者原创声明)不是合同,而是一份逐次声明:贡献者在每个 commit 的 trailer 里加一行 Signed-off-by,声明「这段代码是我写的,我有权提交,我同意项目许可证的条款」。DCO 1.1 文本由 Linux 基金会维护,全文只有五段、约 400 词,被 Linux 内核、Docker、GitLab、Kubernetes 的很多子项目采用。
两者最关键的能力差异只有一条:DCO 无法支持 relicensing(换证)。DCO 只是把贡献按项目当前的许可证授权出去,如果项目要改成双许可(Open Core 商业版)或换一个不兼容的许可证,就必须逐个征得所有版权持有者同意。而 CLA 若包含足够宽的版权许可(或版权转让),项目方就能单方面完成换证。这就是为什么商业公司几乎必然要求 CLA,而社区型项目几乎必然反感 CLA。
另一个常被忽略的点是专利。DCO 本身不含任何专利授予条款——如果项目许可证是 MIT/BSD,那么通过 DCO 进入的贡献连隐含的专利许可都没有;Apache-2.0 则因第 3 条自带专利授予,DCO + Apache-2.0 的组合才具备基本的专利防御。
| 维度 | CLA | DCO |
|---|---|---|
| 法律性质 | 合同,双方签署 | 单方声明,逐 commit |
| 签署粒度 | 一次性(个人 / 公司) | 每个 commit 一行 trailer |
| 是否需保存记录 | 需要,且需可举证 | 记录在 git 历史里,天然可查 |
| 专利授予 | 通常显式包含 + 报复条款 | 无(除非许可证自带) |
| 支持换证 / 双许可 | 是(若含足够宽的许可) | 否 |
| 贡献者摩擦 | 高(首次需注册签署) | 低(一行命令) |
| 自动化难度 | 中(需机器人 + 名单) | 低(校验 trailer 即可) |
为什么许可证本身不够
新手最常见的反驳是:「我都用 MIT 了,还要什么协议?」问题在于 MIT 只约束出站方向——它告诉用户拿到代码后能做什么,却对入站方向只字未提。当项目方把代码发布到 npm 或 Maven Central 时,它必须能证明自己对每一行代码都有分发权;如果某次 PR 的作者其实是拿了雇主的代码,或者抄了别人的 GPL 片段,项目方的分发行为就构成了侵权。CLA 与 DCO 都是在这个「入站缺口」上做文章,区别只在补的强度。
另一个误区是把 CLA 理解成「把版权送给项目」。现代 CLA 绝大多数是许可而非转让:贡献者仍然拥有自己的版权,可以继续把同一份代码用在自己的项目里、写进简历、甚至另发一份——他授予的只是「项目方可以用、且不可撤回」这个非独占权利。真正会让贡献者失去版权的是 CAA(版权转让),而 CAA 在实践中已相当少见,主要保留在 FSF 与少数基金会。
三种组合的实际效果
把入站协议与出站许可证放在一起看,能更清楚地看到缺口在哪里:
| 组合 | 入站授权 | 专利防御 | 可否换证 |
|---|---|---|---|
| 无协议 + MIT | 弱(仅隐含许可) | 无 | 需全员同意 |
| DCO + MIT | 有举证,无权利扩张 | 无 | 需全员同意 |
| DCO + Apache-2.0 | 举证 + 隐式入站许可 | 有(第 3 条) | 需全员同意 |
| CLA(含 sublicense)+ MIT | 强 | 有(若含专利条款) | 可 |
结论很清晰:DCO 加 Apache-2.0 已经是社区项目能达到的最强组合,除换证能力外几乎没有短板;而一旦项目需要换证,无论许可证选得多好都补不上,只能靠 CLA。
2. 主流 CLA 条款对比
Apache ICLA / CCLA 是事实上的行业基线。ICLA 授予的是版权许可(非转让),措辞为「perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable」,同时含专利授予与专利报复终止条款。CCLA 由公司签署,覆盖其在册员工,并可指定「CLA Manager」维护授权名单。Apache 的条款对项目方相当友好:许可不可撤销,因此 Apache 项目可以在不改动贡献者协议的前提下调整分发方式。
Harmony Agreements(HA-CLA-I / HA-CLA-E / HA-CAA-I / HA-CAA-E)是一套可配置模板而非单一文本,由 Project Harmony 于 2010 年前后整理。它最大的价值是显式区分了「版权许可版(CLA)」和「版权转让版(CAA)」两种取向,项目可以按需挑选条款组合,而不必自己请律师起草。很多中小基金会和商业公司的 CLA 都是从 Harmony 派生的。
Google CLA 与 Oracle OCA 代表另一类取向:条款明确允许公司再许可(sublicense)与换证,因此可以直接服务于闭源商业版。Google 的版本通过 google-cla 机器人自动校验,签署后对该作者的所有 Google 组织下仓库生效——这也是「公司级 CLA」的典型体验:签一次,全公司仓库通行。
CNCF CLA 基于 Apache ICLA 修改,同样只做版权许可不做转让,落地在 Linux Foundation 的 EasyCLA 系统上,企业可批量签署 CCLA 并自助管理员工名单。Eclipse ECA 则是 Eclipse 基金会的一站式协议,覆盖该基金会下全部项目。
选模板时只需回答四个问题:版权是许可还是转让?专利条款是否含报复?是否显式授予换证 / 再许可权?是否支持公司批量签署?下表是常见模板的对照。
| 模板 | 版权处理 | 专利条款 | 换证 / 再许可 | 签署系统 |
|---|---|---|---|---|
| Apache ICLA / CCLA | 永久非独占许可 | 授予 + 报复终止 | 未显式授予 | 自建 web form |
| Harmony HA-CLA | 许可(可配转让) | 可配置 | 可配置 | 自托管 |
| Google CLA | 许可 + 再许可 | 授予 + 报复终止 | 显式授予 | google-cla bot |
| CNCF CLA | 永久非独占许可 | 授予 + 报复终止 | 未显式授予 | EasyCLA |
| Eclipse ECA | 许可 | 授予 + 报复终止 | 未显式授予 | Eclipse 门户 |
| FSF CAA | 版权转让 | 授予 | 项目方可自主 | 纸质 / 邮件 |
注意最后一行:FSF 的 Copyright Assignment Agreement 要求版权转让,这是为了给 GPL 的执行提供诉讼主体资格(FSF 持有版权才能起诉侵权者)。这是「转让」派最正当的理由,也说明转让并非天然邪恶——关键看由谁持有、为了什么目的。
条款里最值得逐字读的三处
绝大多数 CLA 文本可以快速略过,但有三处必须逐字确认,它们直接决定协议的实际强度:
1) 版权许可范围:是否含 "sublicense" 与 "irrevocable"
—— 缺 sublicense 则无法进闭源商业版;缺 irrevocable 则贡献者可随时撤回。
2) 专利授予范围:是 "patent claims necessarily infringed by the Contribution"
还是更宽的 "any patent claims owned or controlled by You"
—— 前者是行业惯例、公司法务可接受;后者通常会被企业直接拒绝签署。
3) 专利报复终止:是否含 "if You institute patent litigation against the Project"
—— 没有报复条款的 CLA,专利授予只是单向让利。
以 Apache ICLA 为例,其专利授予明确限定在「贡献本身必然侵权的必要权利要求」,并附完整报复终止条款,这正是它能被大量企业接受的原因。相反,一些公司自拟的 CLA 把专利授予写成「贡献者全部专利组合」,结果是签署率极低——工程师愿意签,公司法务不会批。
Harmony 模板的价值也在这里:它把这些措辞做成了可勾选的条款菜单,项目可以直接引用标准条款号而不必自行起草,从而避免「自拟条款写得太宽导致没人签」或「写得太窄导致没有保护」两种极端。
3. DCO 的 Signed-off-by 机制
DCO 1.1 的核心是四点声明((a) 至 (d)):贡献由本人创建且有权按项目许可证提交;基于他人作品时遵循其许可证;本人有权提交该作品;理解贡献是公开的且会被记录。它由贡献者逐次确认,落在 commit 的 trailer 里。
标准做法是提交时加 -s:
git commit -s -m "fix: 修正 parse 边界条件"
git log -1 --format=%B
得到的 commit message 形如:
fix: 修正 parse 边界条件
Signed-off-by: Leeting Yan <leeting@example.com>
-s 会读取 user.name 与 user.email 自动生成 trailer,位置固定在正文与 Co-authored-by 之后的 trailer 区。校验器要求 trailer 的邮箱与 commit author 的邮箱一致——如果你用公司邮箱当 author,却在 user.email 里配了个人邮箱,DCO 校验会直接失败。这是新手最高频的翻车点。
手工补签已有 commit 时,用 rebase 带 signoff:
git rebase --signoff HEAD~3 # 为最近 3 个 commit 补签
git commit --amend -s --no-edit # 只补签最后一个
git rebase -i --signoff HEAD~10 # 交互式补签 10 个
合并提交(merge commit)也要签——Probot 的 DCO 应用默认检查分支上的每一个 commit,包括 merge。用 squash 合并的项目要注意:squash 后生成的新 commit 会丢失原始 trailer,必须在合并时重新签。
要防止团队反复忘签,可以在本地加一个 prepare-commit-msg 钩子自动追加,并在 CI 里兜底校验。注意钩子只解决「忘记」,不解决「无权提交」——后者是法律问题,不是工具问题。
钩子文件放在 .git/hooks/prepare-commit-msg(用 git config core.hooksPath 指向版本化目录可让全团队共享):
name=$(git config user.name)
email=$(git config user.email)
grep -q "^Signed-off-by: $name <$email>" "$1" || \
printf '\nSigned-off-by: %s <%s>\n' "$name" "$email" >> "$1"
对走邮件列表的项目(Linux 内核、部分 GNU 项目),流程是 git format-patch 出 .patch 文件、邮件发送、维护者 git am 应用;trailer 会随补丁正文一起传递,因此 DCO 校验在邮件链上同样成立。用 Git 工作流与分支策略
中的约定统一提交信息格式,能让 DCO 与 CHANGELOG 生成共用同一套 trailer 解析。
trailer 的格式规则
校验器比大多数人想象的严格。以下规则来自 Probot DCO 应用与 Linux 内核的 checkpatch.pl 实践:
| 规则 | 说明 | 违反后果 |
|---|---|---|
| 必须位于 trailer 区 | 在所有正文段落之后,不能夹在正文中间 | 校验器识别不到 |
格式为 Signed-off-by: Name <email> | 冒号后一个空格,姓名与邮箱之间一个空格 | 正则匹配失败 |
| 邮箱须与 author 一致 | 按 commit author 的 email 比对 | 校验失败 |
| 每个 commit 都要有 | 含 merge commit | 分支检查不通过 |
| 大小写不敏感 | signed-off-by 也可通过 | 一般无影响 |
| 不能手改 author | 改 author 而不改 signoff 会失配 | 校验失败 |
在提交信息里塞多个 Signed-off-by 是合法且常见的:Co-authored-by 的协作者也应各加一行,这样多人协作的提交才能覆盖到每个人的声明。
常见补签场景速查
git commit -s --amend --no-edit # 补签最新一个提交
git rebase --signoff main # 为分支上所有提交补签
git rebase --signoff -i HEAD~5 # 交互式挑选范围补签
git log --format='%h %s' --no-merges \
| xargs -I{} sh -c 'git log -1 --format=%B {} | grep -q Signed-off-by || echo {}'
最后一条会列出所有缺失 signoff 的提交哈希,用于排查历史分支。注意在已推送的分支上做 rebase 补签会重写历史,公共分支上必须先协调,否则会打乱其他人的本地副本。
4. 公司贡献者与雇主授权
大多数国家的职务作品(work for hire)默认版权归雇主。这意味着一个用公司电脑、在公司时间写出来的补丁,员工个人没有处分权——他签 ICLA 在法律上是无效的,公司随时可以主张权利。这是企业级 CLA 存在的根本原因,也是「为什么大公司必须签」的答案。
正规做法是:公司签署 CCLA,覆盖其在册员工,并指定 CLA Manager 维护名单;员工提交时用公司邮箱,系统按邮箱域或名单匹配到已签署的 CCLA,自动放行。EasyCLA 的 CCLA 流程就是这样:企业签一次,之后新员工加入只需在名单里加一行。
| 场景 | 个人身份签署是否有效 | 应走的流程 |
|---|---|---|
| 纯个人时间、个人设备、与公司业务无关 | 有效 | 用个人邮箱签 ICLA |
| 公司时间或设备,但与公司产品无关 | 有争议 | 取得雇主授权信 + 个人签署 |
| 公司产品相关或使用公司 IP | 无效 | 公司签 CCLA,员工在公司名单内 |
| 公司明确鼓励且已签 CCLA | 不适用 | 公司邮箱提交,自动匹配 CCLA |
比 CCLA 更轻的一层是雇主授权信(employer consent letter):由直属主管或法务出具一页纸,说明「本公司同意该员工就某某项目做出贡献,且不主张该贡献的权利」。很多项目在收到个人 CLA 时会顺带要求这封信,尤其是贡献者邮箱域属于某家公司时。
公司侧则应有明确的员工开源政策,至少要回答:允许贡献哪些项目(白名单 / 需审批)、走公司邮箱还是个人邮箱、是否需要法务复核、能否接受 CLA、能否在贡献中引用公司内部信息。缺失这层政策时,最常见的后果是工程师「好心」用个人邮箱提交公司相关代码,几年后公司在做尽调时才发现权属瑕疵。
一个可操作的内部审批流:工程师提交内部 issue 说明项目、目标、预计投入 → 主管确认不冲突 → 法务确认项目许可证与公司政策兼容、必要时启动 CCLA 签署 → 在内部登记表记录。全程留痕,一年一审。
企业侧落地清单
把上述流程固化成可执行的动作,大致是这六项:
- 项目白名单:列出公司允许无审批贡献的项目(通常是已依赖的上游),白名单外一律需法务复核。
- 邮箱规范:明确「公司业务相关贡献必须用公司邮箱」,从源头让 CCLA 匹配生效。
- CCLA 签署:对社区项目主动发起 CCLA,签一次覆盖全员,避免员工逐个签 ICLA 带来的权属风险。
- 授权名单维护:指定 CLA Manager,员工入职 / 离职时同步更新,离职员工从名单移除。
- 贡献登记:内部记录「谁在什么时候向哪个项目贡献了什么」,为将来的合规审计留痕。
- 年度复核:每年重审白名单与已签 CCLA 清单,清理已停更项目。
贡献者侧的自保动作
如果你是个人贡献者、恰好在一家公司上班,提交前先做三个判断:这段代码是否与公司业务相关?是否使用了公司设备、时间或内部资料?公司是否已有该项目对应的 CCLA?三个问题里只要有一个是「是 / 不确定」,正确做法就是先走内部审批,而不是用个人邮箱悄悄提交。用个人邮箱提交公司相关代码,风险由你和你的雇主共同承担,而不是项目方。
需要补充的是,多数公司的开源政策并不禁止贡献,只是要求「走流程」。把它当成一次正常的合规审批即可,不必视作障碍;反过来,公司也应该让流程足够轻,否则工程师会用「反正很麻烦」为由绕过它——而绕过带来的权属瑕疵,往往几年后才会以尽调问题的形式浮出水面。
5. 版权与专利授予条款
读 CLA 时,前两节通常讲定义,真正要盯的是版权条款和专利条款这两块。
版权上有两条路线。转让(assignment):贡献者把版权所有权整体交给项目方,此后项目方可任意换证、闭源、再许可,贡献者不再是权利人。许可(license grant):贡献者保留版权,仅授予项目方永久、全球、非独占、免版税的许可。绝大多数现代 CLA 走许可路线,措辞模板基本固定:
You hereby grant to the Project a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable copyright license to reproduce,
prepare derivative works of, publicly display, publicly perform,
sublicense, and distribute Your Contributions and such derivative works.
请特别注意 sublicense(再许可)这个词——它是否存在,决定了项目方能否把贡献打包进闭源商业版。只做 Open Core 但计划发商业版的项目,必须确认条款里包含它;否则商业版里不能含社区贡献的代码。
专利条款通常包含三层:授予(贡献者就其贡献授予项目方及其下游用户专利许可)、报复终止(若贡献者或其关联方对项目发起专利诉讼,该专利许可自动终止)、范围界定(仅覆盖其贡献必然侵权的必要权利要求,不覆盖其全部专利组合)。第三层最容易被忽略:宽泛的专利授予会被公司法务直接拒绝,而恰好覆盖贡献的窄授予才是行业惯例。
Apache-2.0 的第 3 条与第 5 条值得单独说。第 3 条是显式的专利授予 + 报复终止条款,因此采用 Apache-2.0 的项目天然具备专利防御。第 5 条更关键:它规定除非贡献者明确另行声明,其提交即视为按 Apache-2.0 授权——这就是「隐式入站许可」。换句话说,Apache-2.0 项目在纯法律层面可以不要求任何 CLA 或 DCO,因为许可证本身已经完成了入站授权闭环。很多项目仍要求 DCO,是为了留一份「贡献者确认自己有权提交」的证据,属于举证层面的加固,而非权利层面的必需。
反过来,MIT 与 BSD 系许可证既无专利条款也无入站条款,因此这类项目若在意专利风险,唯一的补救手段就是引入 CLA 或改证。这也是为什么一些原本 MIT 的项目在商业化前会选择「改证 + 补签 CLA」——代价是必须联系全部历史贡献者。
专利报复条款的作用机制
专利报复条款(patent retaliation / defensive termination)的典型措辞是:
If You institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
Contribution incorporated within the Work constitutes direct or
contributory patent infringement, then any patent licenses granted to
You under this License for that Work shall terminate as of the date
such litigation is filed.
它的实际效果是:谁起诉项目专利侵权,谁就立刻失去项目授予他的专利许可。这不是「惩罚」,而是一种威慑平衡——它让持有大量专利的公司不敢轻易用专利攻击采用该协议的项目,因为一旦开战,自己反倒在项目代码上失去专利豁免。Apache-2.0 第 3 条即包含这一条款,这也是它比 MIT 更适合基础设施项目的原因之一。
值得注意的是,报复条款只覆盖项目自身授予的许可,不影响该贡献者与第三方之间的专利关系。因此它无法防止「专利流氓」——那些既不贡献也不使用项目代码的实体,不受任何许可约束。这类风险只能靠防御性专利池(如 OIN,Open Invention Network)来解决,属于另一个层面的话题。
版权许可的「不可撤回」为何关键
CLA 条款里 irrevocable(不可撤回)这个词常被忽略,但它决定了协议是否真的有效。如果贡献者保留了撤回权,那么他在某天不满项目决策时可以要求项目停止使用其代码——对一个已经发布多年的项目而言,这意味着必须剔除相关代码、重写模块,甚至下架版本。所有主流 CLA 模板都写明不可撤回,正是为了消除这个风险;而 DCO 由于不存在单独的许可文本,其不可撤回性依赖于项目许可证本身(Apache-2.0 第 2 条明确授予不可撤回的版权许可,MIT 则未使用该措辞)。
6. 自动化校验配置
四种主流方案,按治理复杂度递增排列。
DCO GitHub App(probot/dco) 最轻量。装上后在仓库设置里勾选所需状态检查,它会检查 PR 中每个 commit 是否含与 author 匹配的 Signed-off-by,缺失时给出如何补签的评论。适合社区型项目。
CLA Assistant(cla-assistant.io,可自托管)走「评论即签署」流程:PR 首次提交时机器人评论并给出签署链接,贡献者点一次授权即完成,签署记录存入指定 gist 或仓库。适合个人 CLA 场景。
EasyCLA(Linux Foundation)是唯一原生支持 ICLA + CCLA + 企业名单的托管方案,企业可自助签署 CCLA、维护授权员工列表,系统按邮箱自动匹配。基金会项目基本都用它。
自建 GitHub Actions 适合有特殊校验逻辑的团队(例如同时校验 signoff 与 DCO 文本版本、或对 Co-authored-by 也做校验):
name: dco
on: [pull_request]
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # 必须全量,否则取不到完整提交历史
- name: Verify Signed-off-by
run: |
base=${{ github.event.pull_request.base.sha }}
head=${{ github.event.pull_request.head.sha }}
fail=0
for c in $(git rev-list "$base..$head"); do
author=$(git log -1 --format='%an <%ae>' "$c")
if ! git log -1 --format='%B' "$c" | grep -qiF "Signed-off-by: $author"; then
echo "缺失 signoff: $c"
fail=1
fi
done
exit $fail
几个必须注意的配置细节:fetch-depth: 0 不能省,浅克隆会导致 rev-list 报错;校验应比对 author 邮箱而非提交者;把该 job 设为分支保护的 required check,否则没人会理它;对 dependabot[bot] 这类机器人账号要加白名单例外,否则自动升级 PR 永远红着。
最后是校验与合并策略的耦合:如果项目用 squash merge,GitHub 会用 PR 标题与描述生成新 commit,原 commit 的 trailer 全部丢失。此时应改为要求 PR 描述里含 signoff,或改用 rebase merge 保留原始 commit。
四种方案的配置成本对照
| 方案 | 部署方式 | 支持 CCLA | 签署记录存放 | 适合规模 |
|---|---|---|---|---|
| DCO GitHub App | 应用市场一键安装 | 不适用 | git 历史 | 任意规模 |
| CLA Assistant | 托管或自托管(Node + GitHub App) | 否 | gist / 仓库文件 | 中小项目 |
| EasyCLA | LF 托管,需项目加入基金会 | 是,含企业名单 | LF 系统 | 基金会项目 |
| 自建 Actions | 仓库内 workflow | 需自行实现 | 自定 | 有特殊校验需求 |
选择时按「是否有企业贡献者」一刀切即可:有企业参与就选 EasyCLA 或自建 + CCLA 流程,纯社区项目用前两种。自建 Actions 虽然灵活,但要自己实现签署状态存储与撤销,长期维护成本不低,除非确有特殊需求否则不建议。
一个容易被忽略的运维细节:CLA Assistant 的签署记录默认存在 gist 里,如果签署人删除账号或 gist 被误删,举证链就断了。稳妥做法是定期导出签署记录并归档到仓库的 .cla/ 目录下,纳入版本管理。
7. 社区抵触与沟通
反对 CLA 的理由通常有四条,且都站得住脚:门槛(新贡献者要先注册、填表、等审批,很多人就此放弃)、隐私(签署要提供真实姓名与邮箱,部分人介意)、权力不对称(贡献者交出权利,项目方却可能闭源商业化)、不可逆(签署通常不可撤销,几年后想反悔无门)。
更激烈的情形是「CLA 导致分叉」:项目宣布引入 CLA 后,核心贡献者认为这是为商业公司铺路,集体出走另起炉灶。这类事件说明引入 CLA 是治理决策,不是法务决策——它必须经过公开讨论、给出充分理由,最好在项目早期就定下来。
降低抵触的沟通策略有几点是验证有效的:
- 讲清动机:明确说明 CLA 是为了专利防御、基金会合规或双许可模式,而不是「想把你的代码据为己有」。含糊的理由会被默认为最坏的猜测。
- 选尽可能弱的条款:能用 DCO 就别用 CLA,能用「许可」就别用「转让」,能不加 relicense 权就不加。
- 公开条款全文并放进仓库(
CLA.md或CONTRIBUTING.md链接),不要只丢一个第三方签署链接。 - 写 FAQ:直接回答「我要放弃版权吗」「公司代码怎么办」「签了还能用在自己项目里吗」这三个问题,能消掉大部分疑虑。
- 保留替代路径:允许公司走 CCLA 批量覆盖,避免每个员工重复签署。
若项目确实不需要换证权,那么从 CLA 迁到 DCO 往往是净收益:贡献门槛骤降,同时用 DCO 保留「贡献者确认有权提交」的举证记录。开源社区的整体趋势也偏向这一侧——近几年多个知名项目主动移除了 CLA。
一份可复用的迁移公告骨架
无论方向如何,公告都应包含这五段,缺任何一段都会放大猜疑:
1. 结论与生效日期:从 X 年 X 月 X 日起,新贡献采用 <DCO/CLA>。
2. 为什么改:一句话说明动机(降低门槛 / 满足基金会合规 / 支持双许可)。
3. 对已有贡献者的影响:是否需要补签、旧提交如何处理、是否影响已合并的代码。
4. 对未来的影响:贡献者需要做什么(加一行 signoff / 点一次签署链接)。
5. 过渡与例外:双轨期多长、不签的 PR 如何处理、有疑问找谁。
第 3 段最容易写错。常见错误是含糊其辞,比如「历史贡献不受影响」——如果新协议要求补签,这句话就是误导;如果确实不需要补签,就应明确写「已合并的贡献无需任何动作」。
8. 迁移与历史遗留处理
迁移方向有两种,处理方式完全不同。
CLA → DCO(放松):技术上只需启用 DCO 校验并更新 CONTRIBUTING.md,但法务上要先确认一件事——已有 CLA 覆盖的代码,其权利是否已足够宽,足以支撑项目继续按现有方式分发?若原 CLA 是版权许可且不可撤销,答案通常是「是」,可以放心停用 CLA 对新贡献的要求;若项目仍保留换证计划,则必须保留 CLA,形成双轨:历史贡献按 CLA,新贡献按 DCO,未来要换证时仍得回头找老贡献者补签。
DCO → CLA(收紧):这是最容易引发社区反弹的操作。除公开讨论外,还必须处理历史提交的补救——已按 DCO 进入的代码并未授予换证权,若目标是双许可,理论上需要每一个历史贡献者补签 CLA。
补救的具体步骤:先用 git 统计出全部贡献者与联系方式:
git shortlog -sne --all | sort -rn | head -50 # 贡献者排名 + 邮箱
git log --format='%ae' | sort -u | wc -l # 去重后的贡献者总数
git log --format='%an <%ae>%n%cn <%ce>' | sort -u
把这份名单与已签署记录做差集,得到「未覆盖名单」。对其中仍可联系到的人,发补签邀请;对已失联的人,法律上的选择只有两条:一是用 git filter-repo 剔除其提交(破坏历史、影响下游,属最后手段),二是评估其贡献是否达到版权保护门槛(纯格式化、单行改动通常不具备独创性),在法务确认后接受残留风险。实践中,绝大多数项目选择的是「接受风险 + 对新增贡献严格签署」。
另一个方向上的历史遗留是版权转让型协议。Software Freedom Conservancy 的 CAA 要求贡献者把版权转让给 SFC,由 SFC 作为诉讼主体执行 GPL。这类项目若想转向更宽松的协议,同样需要处理历史转让记录——但转让的一个「好处」是权利已经集中,项目方(或基金会)反而拥有完整处分权,迁移时只需机构内部决策,不必联系贡献者。这正是转让派支持者的核心论据。
无论哪个方向,迁移都建议留一个过渡期:公告、生效日期、双轨说明、以及一个「不签会怎样」的明确答案(通常是「该 PR 不会被合并」)。含糊的过渡期比强硬的要求更伤社区。
9. 选型矩阵
把前面所有讨论压缩成一张表。读法是:先定位项目类型,再看治理模式与法务需求,最后一列是建议方案。
| 项目类型 | 治理模式 | 核心法务需求 | 建议方案 |
|---|---|---|---|
| 个人小项目 | 单人维护,MIT/BSD | 无 | 不要求任何协议,靠许可证 |
| 社区型基础设施 | 基金会(CNCF / Apache) | 专利防御 + 合规举证 | 基金会 CLA + EasyCLA |
| 社区型工具 | 独立维护,Apache-2.0 | 举证即可 | DCO + GitHub App |
| Open Core(计划发商业版) | 单一公司主导 | 换证 / 再许可权 | CLA(含 sublicense)+ 可选 CCLA |
| 双许可(GPL + 商业) | 公司或基金会 | 版权集中 | CLA 或 CAA(转让)+ 严格校验 |
| 需要专利诉讼主体 | 基金会 | 可执行的权利 | CAA 转让给基金会 |
| 企业内源(InnerSource) | 公司内部 | 权属已由雇佣合同覆盖 | 无需 CLA,用内部 OSS 政策 |
| 文档 / 硬件 / 数据集 | 社区 | 举证 | DCO(部分场景用 CC 授权声明) |
| 已被大量公司依赖 | 社区 | 专利 + 合规审计 | DCO 起步,必要时升级 CLA |
三条经验规则:能用 DCO 解决就不上 CLA(摩擦成本真实存在);只要计划换证或做闭源商业版,就必须上含 sublicense 的 CLA(且越早越好,历史补签的代价随时间指数增长);公司参与贡献就必须有 CCLA 或雇主授权信(个人签署在法律上无效)。
如果项目已经处于「商业公司主导 + 社区贡献 + 计划商业版」的组合,那么 CLA 几乎是唯一选项,此时的工作重点应从「要不要」转向「怎么把摩擦降到最低」:条款选最弱的、签署走一次点击、企业走 CCLA 批量、文档里写清 FAQ。
一个简单的决策树
按顺序回答四个问题,通常就能得到结论:
- 项目会不会在不联系贡献者的情况下换证或发闭源版? 会 → 必须 CLA(含 sublicense);不会 → 进入下一问。
- 有没有企业员工在代表公司贡献? 有 → 需要 CCLA 或雇主授权信,协议层面可选 DCO;没有 → 进入下一问。
- 许可证是否自带入站许可与专利授予(Apache-2.0 类)? 是 → DCO 已足够;否(MIT/BSD)→ 若在意专利,考虑改证或加 CLA。
- 社区规模是否足够大、是否在意贡献转化率? 是 → 一律选摩擦更小的方案,宁可用 DCO 加雇主授权信,也不要为了「完备」牺牲贡献者。
这个顺序刻意把「换证」放在第一位:它是唯一无法通过其他手段弥补的能力缺口,而其余需求大多有替代方案。把这一条确认清楚,后面三问都只是成本优化。
权衡取舍
| 决策点 | 偏 CLA | 偏 DCO | 建议 |
|---|---|---|---|
| 入站授权强度 | 强,含专利与换证权 | 弱,仅声明 + 许可证 | 看是否需换证,不需要就 DCO |
| 首次贡献转化率 | 低(注册签署流失 10%~30%) | 高(仅一行命令) | 早期项目优先 DCO |
| 企业贡献合规 | 清晰(CCLA + 名单) | 需额外雇主授权信 | 有企业参与就上 CCLA |
| 自动化成本 | 中(机器人 + 名单维护) | 低(校验 trailer) | 基金会用 EasyCLA |
| 举证能力 | 强(独立签署记录) | 中(记录在 git 历史) | 两者均可接受 |
| 社区接受度 | 低,易引发分叉 | 高 | 引入 CLA 必须公开讨论 |
| 迁移难度 | DCO→CLA 极难(需全员补签) | CLA→DCO 容易(确认许可够宽) | 犹豫时先 DCO,保留后路 |
| 换证 / 双许可 | 支持 | 不支持 | Open Core 必须 CLA |
常见坑清单
- 用个人邮箱签了公司代码的 ICLA:职务作品版权归雇主,个人无处分权,签署无效;公司业务相关贡献必须走 CCLA 或取得雇主授权信。
- trailer 邮箱与 author 邮箱不一致:DCO 校验按 author 比对,配置了
user.email为个人邮箱却用公司邮箱提交必然失败;统一git config后再提交。 - merge commit 未签 signoff:DCO 机器人检查分支上每个 commit,含 merge;用
git rebase --signoff补齐或改用 squash 并单独确认。 - squash merge 丢掉全部 trailer:新生成的 commit 不含原 signoff,校验直接失败;改 rebase merge 或把 signoff 写进 PR 描述。
- GitHub Actions 用了浅克隆:
fetch-depth默认 1,git rev-list base..head取不到历史而误报;校验 job 必须fetch-depth: 0。 - 没把校验设为 required check:红了也能合,形同虚设;在分支保护里勾选该 job。
- 机器人 PR 被一起拦下:dependabot 提交不含 signoff,导致自动升级永远红;加
dependabot[bot]白名单例外。 - CLA 只丢一个第三方链接:条款不透明会被默认成最坏意图;把全文放进仓库并在
CONTRIBUTING.md说明动机。 - 选了无 sublicense 的条款却要做商业版:社区贡献的代码不能进闭源商业版;签之前逐字确认
sublicense是否存在。 - 历史贡献者失联后才想起补签:邮箱失效、账号注销,只剩剔除提交这条路;换证决策越早做成本越低。
- CLA 签署记录只存在第三方服务里:服务下线或迁移即失去举证能力;定期把签署记录导出并归档到自有仓库。
- 对纯文档改动也要求签署:摩擦与收益严重不成比例;按目录或改动类型设置例外规则。
小结
CLA 与 DCO 不是同一件事的严格版与宽松版,而是解决两个不同问题的工具:DCO 解决「贡献者确认自己有权提交」的举证问题,成本极低;CLA 解决「项目方拿到足够宽的权利(含专利与换证)」的权利问题,成本较高。选型的第一性原理是问自己一句:未来三年我需不需要在不联系贡献者的情况下换证或发闭源版? 需要就上 CLA,且必须含 sublicense;不需要就老老实实用 DCO,把摩擦降到最低。
落地顺序建议:先确认公司贡献者的权属链条(CCLA 或雇主授权信,这一步最容易出法律风险),再配置自动化校验(DCO App 或 EasyCLA,并设为 required check),最后把条款、动机与 FAQ 写进 CONTRIBUTING.md。对已有历史提交,先用 git shortlog -sne 做一次贡献者盘点,把「未覆盖名单」和残留风险显式记录下来——哪怕结论是「接受风险」,有记录也好过不知道。
进一步阅读方向:入站授权只是合规的一半,另一半是出站侧的许可证选择与兼容性判断,即本文第 5 节讨论的版权授予对应的对外许可问题;基金会项目在 CLA 模板与签署系统上的既有基建,见 开源基金会模式与治理结构 ;而协议落地后最直接的收益是贡献者体验,如何把降低的门槛转化为持续参与,见 贡献者成长路径与留存机制 。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。