GitHub 为什么驳回了 Photopea 的下架请求
Photopea 作者说有人用 AI 扒走他的 JavaScript、去掉广告再发布,GitHub 一个月后回复「无法确认违反 17 U.S. Code § 1201」。GitHub 两次为他删过同类仓库,我数了 41 个仓库,真的装了构建产物的只有 4 个。
中文
复制

2026 年 9 月 4 日,Photopea 的作者 Ivan Kutskir 向 GitHub 提交了一份下架请求。一个月后,他收到的是这句话。
Thank you for submitting a DMCA takedown notice. We've reviewed the information you've provided, and based on the facts presented to us, we're unable to confirm a violation of 17 U.S. Code § 1201.
10 月 6 日,他把这件事发上 Hacker News,标题是《GitHub 拒绝下架我软件的破解版,已经过了一个月》。到 10 月 12 日,帖子拿到 572 分、343 条评论,评论区出现最多的一句回应是「你提交的通知类型不对」。
我把这件事的材料翻了一遍,包括 GitHub 自己发布的 DMCA 政策、github/dmca 这个公开仓库里 Photopea 过去两次通知的全文,以及 GitHub 上那些离线版 Photopea 仓库到底装了什么。那句高频回应基本成立,但只解释了一半。另一半在 Photopea 这类软件的结构里。
一个 120 星的 Electron 壳,
<webview>直接指向 photopea.com。今天还在。目标 NFXT/Photopea-Desktop-App,理由写的是「有人破解了我的软件,让任何人免费使用 Premium 功能」。通知里还列了三个 fork。
目标 spooknik/Photopea-Appimage。GitHub 的批注和 2022 年那份一字不差。
今天还在 GitHub 上流通的那份副本,文件名里的时间戳是这一天,三个文件的 sha256 在两个仓库里一一相等。
作者提交下架请求,GitHub Trust & Safety 当天回执,然后是一个月的沉默。
收到「无法确认违反 §1201」的回复,同一天发出 Tell HN。
GitHub 让他重新提交,他说新的票号是 4833281,并开始考虑找律师。
GitHub 有两套流程,强度差很多
GitHub 的 DMCA 政策把版权通知和反规避主张分开处理,两者的审查方式完全不同。
标准版权通知走的是政策里的 A 节。政策对这个流程的描述是 GitHub「几乎不行使裁量权,只判断通知是否满足 DMCA 的最低要求」。如果通知主张整个仓库都在侵权,GitHub 直接下架整个仓库;如果只涉及部分文件,GitHub 会联系仓库作者,给大约一个工作日的时间修改,不改就下架。
反规避主张走的是 C 节,法律依据是 17 U.S.C. § 1201,也就是 DMCA 里禁止规避技术措施的那一条。GitHub 对这条要求提交方写清楚三件事,技术措施是什么、它如何有效地控制对受版权保护材料的访问、被指控的项目如何规避它,然后由技术和法务人员一起做更广泛的审查。政策里还有一句决定性的话。
In cases where we are unable to determine whether a claim is valid, we will err on the side of the developer, and leave the content up.
作者收到的回复里点名 §1201,说明走的正是第二条。一个月的等待和「无法确认」的措辞,都能在这段政策里对上。
GitHub 删过他的东西,删过两次
github/dmca 是 GitHub 公开存放所有收到的通知的仓库,22400 多个文件,按年月排。里面有两份 Photopea 的通知。
一份是 2022 年 3 月 22 日,目标 NFXT/Photopea-Desktop-App,作者写的理由是「有人破解了我的软件,把破解版发在这里,让任何人可以免费用我软件的高级功能,我联系不上他」。另一份是 2024 年 7 月 15 日,目标 spooknik/Photopea-Appimage。
这两份通知的批注一字不差。
While GitHub did not find sufficient information to determine a valid anti-circumvention claim, we determined that this takedown notice contains other valid copyright claim(s).
反规避这部分被否了,版权那部分成立,于是内容处理掉了。今天去看这几个仓库,NFXT/Photopea-Desktop-App 返回 404,spooknik/Photopea-Appimage 返回 451,也就是 GitHub 的「因法律原因不可用」页。2022 年那份通知里还列了三个 fork,DUOLabs333/photopea、tim0-12432/photopea、zhtengw/photopea-clone,现在两个是 451,一个是 404。
所以这次的问题不在「GitHub 不管」,而在这一条通道。作者在 2022 年就走过一次,当时 GitHub 的结论和他今天收到的一模一样。
「混淆算不算技术措施」
2022 年那份通知的表格里有一栏问「你是否主张你对受版权保护的内容设置了技术措施」,他答「是」,解释只有一句。
My code was obfuscated, but the person modified my obfuscated code.
四年之后,这条论证还是没被接受。要看它为什么不成立,得回到法条对技术措施的定义。§ 1201(a)(3)(B) 说,一项技术措施之所以算「有效地控制访问」,前提是它在正常运作中要求「应用信息、过程或处理」才能接触到作品。而 Photopea 的那 5 MB JavaScript 是随着页面直接发给任何一台请求它的浏览器的,不需要任何信息,也不需要任何过程。压缩和混淆是这份作品的形态,不是挡住它的门。
HN 上有人不同意这个判断。用户 eli 引了 § 1201(a)(3)(A) 里的一串动词,「avoid, bypass, remove, deactivate, or impair」,认为混淆过的 JS 符合描述。这段动词确实很宽,但它描述的是规避的手段,主语仍然是前面那项必须「有效控制访问」的措施。措施本身不控制访问时,删掉多少行都不会让它变成规避。
这里有件事值得并排看。2022 年和 2024 年那两份通知里,作者写的都是对方解锁了 Premium。这属于绕过付费检查。GitHub 的政策里单独列了一类内容,绕过产品许可检查的软件,并且写明这类主张不走技术与法务的双重审查。到了 2026 年,他在 HN 上描述的核心变成了「去掉广告」。删除广告代码和绕过付费检查是两回事,前者离 § 1201 更远。
我不是律师,上面这几段都是对公开文本的读法。可以确定的事实只有两条。GitHub 在 2022 年、2024 年和 2026 年都没有接受这条论证;而它的政策明确写了,认定不了的时候,内容留在线上。
我去数了那些仓库
作者说这样的仓库「有几十个」。我用 GitHub 的搜索 API 查了四个查询(photopea、photopea in:name、photopea offline、photopea desktop),去重后得到 338 个仓库。其中在名字、描述或 topic 里自称 offline、desktop、免广告、破解的,有 41 个。
41 个我一个一个下载了 tarball,看里面到底装了什么。结果是这样的。
| 实际内容 | 数量 |
|---|---|
| 装了 Photopea 的构建产物(有大于 300 KB 的压缩 JS) | 4 |
| Electron 或 WebView 外壳,代码里指向 photopea.com | 17 |
| 只有一个 README 的空壳 | 9 |
| 其它小项目,没有构建产物 | 6 |
| 下载失败(截断或不是 gzip) | 3 |
| 页面直接把浏览器跳去第三方下载站 | 1 |
| 只发一个 exe,没有源码 | 1 |

真正装着构建产物的那 4 个里,有两个装着完全相同的文件。pp1753360222.js、DBS1751979998.js、ext1753387088.js,三个文件的 sha256 在两个仓库里一一对应相等。这两个仓库一个创建于 2026 年 9 月 2 日,一个创建于 2026 年 10 月 6 日,也就是作者发帖的同一天。
Photopea 把构建时间写进了文件名,所以能直接读出这份副本的年龄。1753360222 换算过来是 2025 年 7 月 24 日。站上此刻的这一版是 pp1791725206.js,2,643,122 字节,构建于 2026 年 10 月 11 日晚上。副本比它老了十五个月。
作者说的「逐字节相同的 JS」在副本里也看得出来。我在 pp1753360222.js 里数到 photopea 这个字样出现 93 次,photopea.com 出现 52 次。品牌和域名都还在,改名的只有仓库标题。
有一项我没做成。我原想把副本和线上当前构建各切成 1 KB 的块,求一下交集,用百分比说明重合度。结果是 0。Photopea 每次构建都整体重新压缩,隔了十五个月的两份构建之间没有一块共同的字节。这个方法只能在同一次构建上做,而站点只发当前版本,2025 年 7 月 24 日那一版我没拿到。副本究竟改了哪几行,我没有验证,只能确认它是同一套文件、同一套命名、同一个作者的品牌字样。
GitHub 删不掉的另外三类
第一类是外壳,41 个里占 17 个,是最大的一类。2017 年那个 120 星的只是把 <webview> 指向 live 站点,广告照旧加载。2025 年的一个用 Playwright 把站点缓存到本地离线运行,缓存时只放行 photopea.com 和 gstatic 两个域名,等于内置了一个广告拦截器。这类项目没有复制他的代码,而拦截广告本身合法,浏览器扩展每天都在做同样的事。
第二类是重实现。这一轮的导火索是另一个项目,跟离线副本是两回事。André Klein 在 10 月 4 日写了一篇《Vibecoding Photoshop》,盘点了四个 AI 时代出现的 Photoshop 克隆,其中一个是 PhotoSuite。10 月 6 日,《Photopea 作者就 PhotoSuite 表态》上了 HN,191 分。作者在评论里的依据是概率推理,他说能完整支持 PSD 格式的编辑器只有 Photopea 和 Photoshop,一个匿名作者忽然做出完整的 PSD 支持,很可能就是抄这两者之一。
我看了那个仓库的完整文件树。1521 个文件、153 MB,其中 762 个是 Rust 源文件,JS、WASM 和 HTML 加起来 4 个,最大的是一个 21 KB 的网页脚本。仓库里没有 Photopea 的任何构建产物。功能相同不构成侵权,代码相同才构成,而这两件事在公开材料里是分开的。作者自己在 HN 上也把它们分开了,他提交给 GitHub 的是那些「逐字节相同」的离线副本。
第三类是名字。GitHub 的 DMCA 政策明确写了这条流程只处理版权,商标有单独的通道。HN 上有人建议他注册商标,下面立刻有人回「商标不保护代码,只保护标识和品牌,而品牌很容易删掉」。这两句都对,商标能解决的是用户拿着别人的改版来向他报 bug 那部分困扰,不是代码被复制那部分。
客户端软件的结构性弱点
Photopea 是纯客户端软件,这是它能做到「文件不离开你的设备」的原因,也是它能被一个人维护十四年的原因。代价是每个访客的浏览器里都有一份完整程序,广告代码和程序在同一个包里,删掉广告的难度等于删掉几行代码。
HN 上这条线吵得很凶。一派认为把业务逻辑放在客户端就该预料到这一天,用户 fg137 的说法是,一个闭源、纯客户端、JavaScript 还被混淆过的应用,今天没有真正的版权保护。另一派认为不该如此,IP 律师 DannyBee 的回复是这件事只有两个选项,花钱找专门做这行的律所打地鼠,或者当成正常损耗接受,因为它无论如何都防不住有心人。
我自己的判断是,这件事里被 AI 改变的部分比我预想的窄。在 GitHub 上流通的副本其实是一份 2025 年 7 月的快照。两个仓库在互相转手同一批文件。这份快照出现的时候,能读懂压缩 JS 的模型还不普及。作者在 HN 上说现在有人「让 AI 把代码扒下来、去掉广告」,这个描述在别处或许是准确的,但在我数到的这 41 个仓库里,证据指向的是复制和转手,不是每一份都由 AI 现场加工。他感受到的压力是真的,压力的形态和帖子里写的有一点出入。
结构性缓解办法是有的。把核心逻辑挪到服务端、用 WebAssembly 编译成二进制、加上许可校验,都能抬高门槛。代价他也清楚,Photopea 现在不需要账号就能用,文件不上传,做到这些就要牺牲其中一部分。
现在的状态
他换了新票号 4833281,公开说打算找律师。我核对了几个仓库的在线状态,4 个装着构建产物的仓库在 10 月 12 日全部还能打开,两个装着同一份快照的分别创建于 9 月 2 日和 10 月 6 日。被下架的那 5 个,全部来自 2022 年和 2024 年那两次通知。
有两个材料我读不到,记在这里。他 9 月 4 日提交的通知正文没有公开。github/dmca 里 2026 年没有 Photopea 的记录,我手上只有 GitHub 回复的片段,以及他自己对通知内容的转述。PhotoSuite 那个 issue 已经被仓库作者删除,接口返回 410。
2022 年他走对了通道,也拿到了结果。这一次他手上有的东西比 2022 年更充分,副本的品牌字样、域名、文件命名和构建时间戳都还在,两个仓库装着同一份文件这件事也查得到。剩下要看他提交的那份材料里,有没有写清楚是哪些文件在侵权,以及有没有把 fork 一起列上。GitHub 的政策在这两点上都写得很直白。