上一篇文章写的是 Judge0。这篇写 Piston——同类产品,但设计哲学几乎相反。放在一起看有个好处:同一件事(安全地跑陌生代码),两个项目给出的答案完全不同,而这恰好能说明哪些是「必须」,哪些是「选择」。
Piston 由 engineer-man 维护,MIT 协议,2842 星、475 fork。它的自述只有一句话:高性能通用代码执行引擎,擅长运行不可信、甚至可能恶意的代码而不受其害。它被用在 EMKC 的挑战赛与周赛、4100 多个 Discord 服务器里的「I Run Code」机器人、各种 Web IDE,以及两百多个直接集成上。
而它最近做了一件在开源圈挺少见的事:2026 年 2 月 15 日起,公共 API 不再免费开放。这件事比它的技术细节更值得先讲。
一、先说说「关掉公共 API」这件事
README 里那段说明写得相当直接,大意是:API 不再对公众免费开放;想拿授权,先读完下面的内容、确认自己符合条件,再去 Discord 找作者;授权只发给有正当理由的非商业教育项目。
然后是一份「不给」的清单:
- 收费的项目
- 临时的项目
- 个人的项目
- 作品集项目
- 高中、大学课程作业
- 概念性项目
- 「Vibe-coded AI slop projects」——用 AI 随手生成的垃圾项目
- 「总体上对任何人都没什么好处的项目」
最后补了一句:授权由我完全自行决定;如果你连上面这份清单都没读,我大概会直接忽略你;没拿到授权也没关系,项目是开源的,欢迎你自己部署一个实例。
这段话在中文语境里可能显得「态度不好」,但它其实点出了一个很现实的问题:一个免费的执行 API,最容易被谁用掉?答案是被批量调用、被爬虫、被「生成一堆小项目试试」的人用掉。执行引擎的资源是真实成本(CPU、内存、容器启动),而滥用者的收益接近于零。作者的选择不是涨价,而是把「谁可以用」这件事收回到人手上判断。
顺带说,这份清单里「不给学校作业」这一条挺有意思——很多同类服务反而最爱面向教学场景。他的理由没写,但从上下文能推出来:作业类项目数量极大、生命周期极短、写完就弃,对公共资源是最典型的消耗。
二、它的 API 长什么样
Piston 的接口只有两个主要端点:查运行时(/api/v2/runtimes)和执行(/api/v2/execute),默认端口 2000。
请求体里有三个必填:language、version、files[]。注意 version 是必填的,而且支持 SemVer 选择器——你可以写 3.9.4,也可以写 3.x 甚至 3。这个设计在同类里不常见:多数引擎只让你说「我要 Python」,版本由服务端定;Piston 让你自己钉版本。
为什么要钉版本?因为同一段代码在不同语言版本下行为可能不同。做教学平台时,学生会因为「本机是 3.11、平台是 3.9」而崩溃;做回归测试时,你需要复现某个特定版本的行为。支持多版本共存,是它把自己定位成「引擎」而不是「判题器」的证据之一。
可选参数则是六个「闸门」,全部以毫秒或字节计:编译超时、运行超时、编译 CPU 时间、运行 CPU 时间、编译内存上限、运行内存上限。默认值:编译 10 秒、运行 3 秒、内存不设限。
响应分成 compile 和 run 两段——编译型语言才有前者。每段里都有 stdout、stderr、退出码、信号、状态码、CPU 时间、墙钟时间、峰值内存。把 CPU 时间和墙钟时间分开给,是这块设计里最专业的一笔:两者差值就是排队与进程启动的开销,优化时看这个数比看总耗时有用得多。
状态码用两个字母表示,语义很清楚:
| 状态 | 含义 |
|---|---|
| RE | 运行错误(Runtime Error) |
| SG | 被信号杀死(Signal) |
| TO | 超时(timeout 或 cpu_time 触发) |
| OL | stdout 超长 |
| EL | stderr 超长 |
| XX | 内部错误 |
这套码表比「返回一个布尔值表示成功失败」细致得多,也说明作者确实在生产环境里踩过坑:OL / EL 单独成码,意味着它把「输出爆炸」当成一类独立故障,而不是混在超时里。
三、WebSocket:能边跑边喂输入
这是 Piston 相对 Judge0 最明显的功能差异。它有一个 /api/v2/connect 的 WebSocket 端点,可以实时收发:一边程序在跑,一边你给它喂 stdin,一边收它的 stdout。
消息类型就六种:
init(客户端→服务端):初始化任务,参数和执行接口一样,但 stdin 会被丢弃runtime(服务端→客户端):告知实际使用的语言与版本stage(服务端→客户端):当前处于编译还是运行阶段data(双向):交换 stdin / stdout / stderr 数据流signal(客户端→服务端):向运行中的进程发信号exit(服务端→客户端):某阶段结束,附带退出码或信号
这带来的是什么能力?交互式程序。一个等用户输入的程序、一个 REPL、一个需要中途打断的调试过程,用一次性 stdin 的接口根本做不了。它也能被用来做「边算边看」的流式输出展示,而不是等程序跑完才一次性拿到所有输出。
但要注意:这个端点公共 API 不提供。想用就自己部署。加上前面说的公共 API 收紧,这条限制更明确了——作者显然把「交互式执行」当成需要自己负责的能力。
四、十道闸门,全是具体数字
Piston 的安全说明写得比多数项目都具体,因为这里每一条都对应一类真实攻击:
| 限制 | 挡的是什么 |
|---|---|
| 默认禁止对外网络 | 外连、扫内网、当作跳板 |
| 最大进程数 256 | fork 炸弹——README 直接写了两个经典 payload 作为例子 |
| 最大打开文件 2048 | 各种基于文件句柄的攻击 |
| 每次执行后清理临时空间 | 把磁盘写满 |
| 每次提交换一个非特权用户 | 提权后横向影响 |
| 每次提交独立 Linux namespace | 进程间互相看见、互相干扰 |
| 默认运行 3 秒(CPU 与墙钟都算) | 死循环 |
| 限制全部进程的峰值内存 | 内存炸弹 |
| stdout 默认上限 1024 字符 | yes 炸弹、无限输出 |
| 对越界代码直接 SIGKILL | 兜底 |
底层用的还是 Isolate——国际信息学奥赛(IOI)出品的沙箱工具,靠 Linux namespace、chroot、多个非特权用户和 cgroup 来做隔离与限额。Judge0 也用它。区别在于外壳:Judge0 是 Ruby on Rails,Piston 是 Node。两者都选择「不自己写沙箱」,这个决定本身就是对「沙箱很难写对」的承认。
值得留意的是「默认值」这三个字。这些数字都可在请求里放宽(比如内存不设限就是默认 -1),也就是说安全性最终取决于调用方给的参数。如果你在自己的服务里把 run_timeout 设成 60 秒、内存不设限,那这十道闸门有一半就形同虚设。
五、语言清单:它到底有多「什么都有」
README 的语言清单是按字母排的一长串,读起来没有信息量。我按用途重新分了三堆,差别就出来了:
第一堆是主流生产语言:C/C++、Java、Kotlin、Scala、Python(含 Python 2)、JavaScript/TypeScript、Go、Rust、Zig、C#、PHP、Ruby、Perl、Swift、Dart、Julia、Haskell、OCaml、Elixir、Lua、Groovy、Racket。这一堆是「正常需求」。
第二堆是老语言与专用工具,这堆就有意思了:Fortran、COBOL、Pascal、FreeBASIC、Lisp、Prolog、Smalltalk、NASM/NASM64、LLVM IR、awk/bash/dash、PowerShell、sqlite3、Octave,还有 iverilog(硬件描述语言仿真)和 file(识别文件类型,不是编程语言)。COBOL 和 Fortran 的存在说明它服务的对象里有一批「老系统遗留下来的代码要跑一下」的人;file 和 iverilog 的存在说明它被当成通用工具链的入口,而不只是编程语言执行器。
第三堆是 Esolang(趣味语言),这是最能说明项目气质的一堆:brainfuck(及其变种)、befunge93、lolcode、cow、emojicode、rockstar、golfscript、jelly、vyxal、cjam、matl、pyth、05AB1E(osabie)、retina、husk、japt……
这些语言里有些是用来「用最少的字符解决问题」的 code golf 语言(jelly、05AB1E、vyxal 就是为此而生),有些纯粹是玩笑(cow、lolcode、rockstar——后者让你用歌词的语法写程序)。支持它们不产生任何商业价值,但产生两样东西:社区的乐趣,以及「这个引擎真的什么都能跑」的技术信誉。
从工程角度看,支持 esolang 的成本并不低——每个语言都要写安装脚本、定义编译与执行命令、处理它奇怪的退出行为。愿意为 20 多种玩具语言做这些,说明维护者的动机里有一部分不是「做产品」。
六、部署门槛:cgroup v2 是硬要求
这部分比 Judge0 那篇更值得单列,因为 Piston 的要求写得很具体:
- 必须启用 cgroup v2,且禁用 cgroup v1。这是硬条件,不是建议。老一些的发行版默认还是 v1 或者混合模式,得手动改内核启动参数。
- Node.js 15+(跑 CLI 和 API 外壳)、Docker、Docker Compose。
- 仓库必须用 LF 换行克隆——README 专门加了提醒,因为 Windows 上默认 CRLF 会让脚本挂掉。
部署路径有两条:
| 方式 | 命令要点 | 适合谁 |
|---|---|---|
| 完整版(含 CLI) | 起 api 容器,再进 cli 目录装依赖 | 需要装运行时、管语言版本的人 |
| 只要引擎 | 单条 docker run,需要 --privileged、挂载目录、映射 2000 端口 | 只想跑 API、语言集固定的人 |
注意那个 --privileged:容器要有特权才能让里面的 Isolate 创建 namespace、设置 cgroup。这和「最小权限」的安全直觉是相反的,但它恰好是这类工具的固有矛盾——要隔离别人,自己必须先拿到足够的权限。所以 Piston 不该和其他服务混部在同一台机器上,尤其不该和存放敏感数据的东西放一起。
七、ppman:把语言当软件包管
装完 API 之后,容器里一个语言都没有。要装语言,用 CLI 里的 ppman:
| 动作 | 效果 |
|---|---|
| 列出可用包 | 看当前能装哪些语言、哪些版本 |
| 装最新版 | 装某语言的最新运行时 |
| 装指定版本 | 精确到补丁号,例如某个 3.9.x |
这个设计很像 apt:语言运行时是软件包,可按版本安装,多版本共存。装完之后执行时用 SemVer 选择器指定要用哪个。CLI 也能直接跑本地文件,加 -l 参数选版本,加 -u 参数指向远程实例。
好处很实在:不用为了支持 80 种语言下一堆镜像,需要什么装什么,磁盘占用可控。坏处也明显:「装语言」变成了一步额外操作,不像 Judge0 那样镜像里全带。新手第一次跑会发现「语言不存在」,得先学会 ppman。
八、和 Judge0 怎么选
两篇连着看,选择逻辑可以摊成一张表:
| 维度 | Piston | Judge0 |
|---|---|---|
| 外壳语言 | Node.js | Ruby on Rails |
| 沙箱 | Isolate(Docker 内) | Isolate(Docker 内) |
| 语言安装 | 按需 ppman 安装,多版本共存 | 镜像自带 90+ 语言 |
| 交互式执行 | 有(WebSocket,需自部署) | 无(一次性 stdin) |
| 公共 API | 2026-02 起需申请,仅非商业教育 | 有免费公共实例,有速率限制 |
| 生态 | 200+ 集成、多种语言 SDK、Discord 机器人 | 学术论文大量引用、官方 SaaS |
| 许可 | MIT | GPL-3.0 |
| 上手难度 | 中(要 cgroup v2、装语言) | 中(要 Docker 权限) |
简单说:要交互式执行、或者要精确控制语言版本,选 Piston;要开箱即用的多语言覆盖、或者想要有论文背书的成熟度,选 Judge0。两者都不适合「随手就跑」的场景——没有 Docker 权限和内核配置能力的环境,两个都装不起来。
九、适合谁
值得用:
- 做 Discord 机器人或聊天机器人,需要「贴段代码就能跑」的功能——I Run Code 就是这个用法的成熟范例。
- 需要交互式执行的教学工具:学生能边输入边看输出。
- 要钉死语言版本、复现特定行为的测试平台。
- 对 esolang 有兴趣,想找个现成的「什么都能跑」的环境玩。
不必折腾:
- 只跑一两种语言、量也不大:一个 Docker 容器加十行代码就够了。
- 没有内核配置权限的托管环境:cgroup v2 这条就过不去。
- 只想调个 API 试试:公共 API 已经需要申请了,直接自部署更实际。
一句话总结:Piston 是那种「技术选型很诚实」的项目——它承认沙箱难写,所以用 Isolate;承认资源有限,所以收紧公共 API;承认语言太多装不完,所以做成按需安装。它的功能表看起来比 Judge0 更花哨(毕竟连 cow 和 emoji 语言都支持),但真正决定它是否适合你的,是那三个硬条件:Docker、cgroup v2、以及你愿不愿意自己管语言版本。
本文根据项目公开仓库 engineer-man/piston 的 README 整理撰写。三张配图均依据 README 中的「Execute Endpoint」「Security」「Supported Languages」三节内容自绘(语言清单按用途重新归类,原表为字母序),未使用项目外素材。文中星标数、许可协议、限制参数默认值与公共 API 政策均为撰写时 README 所载,项目迭代较快,请以仓库当前状态为准。本文不含外链入口与推广内容。
本文整理自公开资料并做重写,原始出处:https://github.com/engineer-man/piston。文中配图已去除站点叠加水印与推广信息;版权归原作者所有。


