← ShadowFork 影刻

影刻协议

可衍生性是上游的功能,不是下游的负担。

一份写给开源作者的规范:让别人基于你的项目做产品之后, 还能持续跟随你的更新 —— 而不是分裂成一个再也追不上的孤儿版本。


为什么需要它

同一套工具、同一份方法,衡量两个衍生版相对上游被改动的文件数。 它们做的事几乎一样:换品牌、改默认值、接自有服务、去掉不要的东西。

衍生版上游被改动的上游文件
uu-switchcc-switch21
U-ClawClawX213

差了十倍。差异不在下游的能力,在上游的结构。 cc-switch 把产品名放在两个配置文件的具名字段里 —— 改 2 个字段; ClawX 把它散在 67 个源码文件的字符串中 —— 改到哪算哪,每次跟版都冲突。

上游多花一天做结构准备,下游省下的是每次跟版的永久成本

这对你有什么好处

这不是慈善。fork 你的人跟不上你,你自己也在亏

下游跟不上后果落在你头上
衍生版停在半年前的版本你修的安全漏洞,他们的用户永远等不到
跟版成本太高,索性不跟你的新功能传不到那批用户
衍生版自己打补丁bug 修在他们那儿,永远不回流给你
fork 变成孤儿版本他们的用户遇到问题,仍然来你的 issue 区问
只能整文件删掉你的赞助你的收入没了,而本来只要一个开关
最后一条是真事:某项目把推广链接散在 8 个预设文件和 4 份 README 里, 衍生版想去掉就只能整文件删除。 如果有一个 SHOW_SPONSORS 开关,下游关掉的只是显示 —— 源数据、默认行为、以及你面向自己用户的收入,一分不少。

七条规则

Rule 1

身份单点化

产品名、图标、官网、bundle id、包名,集中在配置文件的具名字段里, 不要散进源码字符串。

判据:衍生者应该能靠改配置字段完成大部分品牌替换, 而不需要全局搜索替换

Rule 2

区分「身份字符串」和「协议字符串」

最疼的一条。某项目的代码里有 290 处产品名字样,散在 67 个文件中, 但它们不是一回事:

身份窗口标题、关于页、菜单 —— 应该改
协议autostart 的 bundle 路径、备份导入校验头、配置迁移用的 legacy id、写进第三方配置的 profile 名 —— 改了就坏

外表一模一样,后果天差地别。衍生者一次「全局替换品牌名」, 自启动失效、备份互导失败、老用户迁移不了 —— 而且编译通过、测试全绿、界面正常

Rule 3

默认值可覆盖,不硬编码

默认模型、端点、遥测、更新通道、功能开关,走配置或 feature flag。 衍生版关闭遥测是正当需求(企业内网、隐私承诺、合规要求)。 没有开关,他们只能改你的代码;漏掉一处,用户数据就悄悄流回你的后台 —— 这对双方都是事故。

Rule 4

扩展点优于修改点

给 hook / policy / plugin,让衍生版用新增文件表达定制。

 直接删你的预设数据      → 你每次加供应商都冲突
 在显示层套一个过滤策略  → 你的数据文件一字未动,永远干净合并

一个 hook 换来的是一整个文件永不冲突。 实测中,某衍生版 97% 的定制代码量是纯新增文件 —— 那部分永远不会冲突。

Rule 5

归因位显式、集中、可关

赞助链接、推广位、aff 参数,集中在一处并提供开关。 散着放的下场是衍生版只能整文件删除,你什么都留不下。

这同时是一条诚实条款:标出归因位,衍生者才知道自己在分发什么。 一个以「无广告」为卖点的衍生版,如果不知道你的文档里有几十处返利链接, 就会在不知情下替你打广告 —— 最终双方都要收拾局面。

Rule 6

界面元素留稳定的非视觉标识

给关键 UI 元素挂 data-* / automation id / 无障碍 id。 衍生版要跑自己的 UI 自动化,没有稳定标识就只能靠类名和文本定位 —— 而这两样正是品牌定制时必然要改的。于是他们改一次品牌,测试全挂。

这条对你自己也有好处:你的类名重构不会再打断任何人的测试。

Rule 7

声明一份机器可读的衍生契约

前六条写给人;第七条写给机器。在仓库根放一份 .shadowfork/upstream.yaml

apiVersion: shadowfork.io/v1alpha1
kind: DerivationContract

identity:            # 衍生版应该改这些
  - file: src-tauri/tauri.conf.json
    path: $.identifier
    note: 必须改,否则与本项目装在同一台机器会互相覆盖配置与自启动

protected:           # 看着像品牌,其实是协议
  - glob: "src-tauri/src/**/*.rs"
    reason: 含 autostart 路径、备份校验头、迁移 legacy id。
            改动会破坏兼容性,且不会有任何报错。

extensionPoints:     # 从这里扩展,别改核心
  - file: src/config/presetPolicy.ts
    kind: policy-hook

attribution:         # 归因位在哪、能不能关
  - files: ["README*.md"]
    count: 118
    toggle: null

license:
  spdx: MIT
  preserveCopyright: ["Copyright (c) 2025 Jason Young"]
  trademark: 项目名与 Logo 为商标,不在 MIT 授权范围内。

commercial:            # 商用衍生怎么办 —— 见下节
  tier: notify
  contact: author@example.com

衍生版的配置可以直接继承你的契约。 你移动了扩展点或扩大了保护区,所有衍生版在跟版时立刻知道, 而不是等用户报障才发现。

商用分档

许可证解决的是版权:MIT 早就允许别人改你的代码、闭源、再分发。 它解决另外三件事 —— 而那三件才是商用衍生真正卡住的地方: 能不能用你的名字(商标)、你会不会哪天把扩展点重构掉(稳定性)、 以及双方怎么谈钱(没有入口)。

所以本协议不发明新的开源许可证。 新许可证没人做法律审查、企业法务一律拒收,只会加剧许可证泛滥。 commercial.tier 是一份贴在你现有许可证旁边的声明, 不替代它、不改变它,只回答一个问题:有人拿它做商业产品时,你希望怎样。

tier含义适合谁
open 随便改、随便闭源发行,不必告知,免费。 纯粹想让代码传得越远越好的项目 —— 等同现状。
notify 免费,但商用发行前告知作者一声 默认推荐。见下。
revenue-share
license
分成,或一次性商用授权。条款由 terms 指向的页面定义。 已经靠项目吃饭、或衍生版明显在挣钱的项目。
为什么默认推荐 notify 而不是分成。 绝大多数作者不缺衍生版那点钱,缺的是「知道谁在用我的东西」。 一上来谈分成,作者第一反应是防备,谈判成本高于收益; 而 notify 对双方都近乎零成本: 作者拿到用户去向与真实反馈,衍生者拿到一份「我们尊重上游」的公开证据 —— 在向企业客户交付定制版时,这份证据比任何合规声明都硬。

关系先建立,钱的事之后再谈。直接跳到 revenue-share 的项目, 通常两样都拿不到。

tier 不是许可证,不构成授权,也不能收回许可证已经给出的权利。 open 之外的档位表达的是作者的期望洽谈入口; 真正的约束力来自双方另行签署的协议,或来自上游本就采用的双授权模式 (如 GPL + 商业许可)。工具会把 tier 显示在法务闸门里, 但不会据此阻止任何许可证本就允许的行为 —— 把作者的期望伪装成法律义务,对谁都没好处。

合规姿态

本协议不是绕过开源许可证的工具,也不为「换皮再分发」提供正当性。 采用它的项目与衍生版都应当:遵守上游许可证(GPL/AGPL 衍生版必须开源); 保留原作者版权行;更换自己的品牌标识(商标不在 MIT 授权范围内); 显著致谢上游;不得冒充上游或暗示获得背书。

协议的目的是让衍生光明正大且可持续,而不是让它更容易隐匿。

采用

满足七条规则的项目,可以在 README 挂上徽章。分三级,自评即可,无需认证

级别要求
L1 可辨识规则 1、2 —— 身份集中,协议字符串已标注
L2 可覆盖+ 规则 3、4、5 —— 默认值可配,有扩展点,归因可关
L3 可契约+ 规则 6、7 —— 有非视觉标识,提供衍生契约
[![ShadowFork Ready](https://img.shields.io/badge/ShadowFork-Ready-2563eb)](https://shadowfork.net/protocol)

规则 1、3、5 通常是几小时到一天的重构,且对你自己的代码质量是净收益; 规则 2 是把已有常量摘出来;规则 7 是写一份 YAML。 规则 4、6 视项目而定,可以逐步做 —— 三级制就是为了让你分阶段采用。

协议全文与 Schema English

常见疑问

这是不是在帮别人抄我的项目?

抄袭者不需要你的配合 —— 他们本来就会 fork 然后全局替换,改完就跑。 本协议影响的是另一批人:想认真做长期衍生产品、并且愿意持续跟随你的人。 让他们跟得动,你的代码才传得远、bug 才回得来。 而规则 5 恰恰是唯一能让你在衍生版里保住归因的机制。

跟 Copybara / cruft 是什么关系?

那些是下游工具,解决「变更能不能重放」。 本协议是上游规范,解决「衍生版为什么一开始就那么难跟」。 方向相反,互补而非竞争。