让代码活下去

Sprites 让 Linux 沙箱的启动快得像 ssh,作者认为「临时沙箱用完就杀」的做法已经过时:agent 需要一个能长期存在、随手可取的计算环境。

中文
复制
插画:一只猫从纸箱里探出身子,旁边是叠起来的容器

图片来自 Annie Ruygt

Agent 隔离的最新技术水平,是一个只读沙箱。在 Fly.io,这套说法我们卖了好几年,现在我们宣布:临时沙箱已经过时了。别再每次用完就把沙箱杀掉。

不先给你们看点我们新做的东西,我这套论证没法成立。都是成年人了,这是家公司,我们做了什么就说什么。开始吧。

我想跑点代码。于是我做的是,运行 sprite create。它跑起来的同时,我来讲讲背后发生了什么——

✓ Created demo-123 sprite in 1.0s
● Connecting to console...
sprite@sprite:~#

妈的,已经好了。

这就是一台 Linux 机器上的 root shell,现在归我们了。它上线花的时间,跟 ssh 到一台已经存在的宿主机上差不多。我们管这东西叫 “Sprites”。

给我们的 Sprite 装上 FFmpeg:

sudo apt-get install -y ffmpeg >/dev/null 2>&1

跟最初创建 Sprite 不一样,用 apt-get 装 ffmpeg 慢得要死。尽量别再来一次:

sprite@sprite:~# sprite-env checkpoints create
# ...
{"type":"complete","data":"Checkpoint v1 created successfully",
"time":"2025-12-22T22:50:48.60423809Z"}

这个瞬间完成。懒得去测了。

我走开去拿咖啡。时间过去。Sprite 察觉到我没动静,就睡了。我在咖啡店碰到一个高中老友。结果一起待了一天。更多时间过去。甚至过了好几天。回来时:

> $ sprite console 
sprite@sprite:~# ffmpeg
ffmpeg version 7.1.1-1ubuntu1.3 Copyright (c) 2000-2025 the FFmpeg developers
Use -h to get full help or, even better, run 'man ffmpeg'
sprite@sprite:~#

一切都在我离开时的位置。Sprites 是持久的。起步 100GB 容量,没有任何仪式。也许我再留几天,也许几个月,无所谓,就是能用。

假设我把一个应用跑起来了。又装了些包。然后:灾难。也许是一条欠考虑的全局 pip3 install。或者 rm -rf $HMOE/bin。或者 dd if=/dev/random of=/dev/vdb。不管是什么,全坏了。于是:

> $ sprite checkpoint restore v1
Restoring from checkpoint v1...
Container components started successfully
Restore from v1 complete

> $ sprite console
sprite@sprite:~#

Sprites 有一等的 checkpoint 和 restore。文字里看不出来,但那次 restore 花了大约一秒。快到可以随手用、交互着用。不是逃生舱。而是:使用 Sprite 的日常流程中本来就该有的一环。像 git,但作用于整个系统。 如果你要问这跟 EC2 实例有什么不同,问得好。这正是我们想要的,只不过:

  • 我可以随手创建几百个(不需要 Docker 容器),每个 1-2 秒就能起来。
  • 它们闲置后自动停止计费,所以养一堆也不贵。我就用了几十个。
  • 它们接入了我们的 Anycast 网络,所以我能拿到一个 HTTPS URL。
  • 尽管如此,它们是完全持久化的。我不发话,它们就不会死。

这几个特性凑在一起,还不常见到有现成的名字,于是我们决定自己起一个,叫“Sprites”。Sprites 就像 BIC 一次性云电脑。

这就是我们做出来的东西。你可以自己去试试。我们还写了 1000 字讲它的工作原理,但我删掉了,因为我现在不想再聊我们的产品,我想说重点。

Claude 不想要无状态容器

多年来,我们一直试图用同一套抽象服务两类截然不同的用户。这行不通。

职业软件开发者受过的训练是构建无状态实例。无状态部署把持久数据限制在数据库服务器上,换来的是简单、灵活的横向扩展和更小的故障影响范围。这是个好主意,好到云端大多数能跑代码的地方看起来都像无状态容器。Fly Machines,我们的旗舰产品,看起来就像无状态容器。

问题在于,Claude 不是职业开发者。Claude 是个效率高得离谱的五岁神童。它聪明得吓人,见着插座就想把手指头捅进去,而最好的办法就是找个法子让它电自己一下。 (有时候是靠逃出容器!)

如果你硬逼一个 agent,它会绕过容器化,照样把活干了。但这么做对 agent 没有任何帮助。它们不想要容器。它们不想要“沙箱”。它们想要计算机。 前几天有人问我,是不是在说 agent 需要声卡和 USB 接口。也许吧?我不知道。反正不是今天。

稍后我会解释为什么。但首先我大概得说清楚,我说的“计算机”到底是什么意思。我想我们都同意:

  • 一台计算机不会因为跑完一个任务就必然消失,而且
  • 它有持久化存储。

既然当下的 agent 沙箱这两样都没有,定义到这里就可以打住,回到我的正题。

简单就是赢

先从这一点说起:有了真正的计算机,Claude 不必每次接手一个 PR 都重建我的整个开发环境。

这听起来很表面,但重建 node_modules 这类东西痛苦到极点,整个行业正砸下数千万美元,研究怎么给临时沙箱做快照和恢复。

我不是说这些问题无解,我是说它们根本没必要存在。与其去解决它们,不如直接用一台真正的计算机。做完一个 PR,审查、推送,然后接着做下一个,不用重启。

人们会给自己找理由,说每次开始一个 changeset 都从全新的构建环境起步是件好事。斯德哥尔摩综合征。你自己开一个 feature branch 的时候,会为此专门建一套全新的开发环境吗?

Agent 之所以浪费这么多力气,是因为没人预见到它们会来。只读的临时沙箱,是我们当时墙上唯一挂着的、能让人还算清醒地用上它们的工具。

你有没有为了给 agent 访问真实数据而搭过真正的基础设施?有人这么干。因为他们知道自己每次给 agent 下 prompt 面对的都是白板一块,于是就在沙箱外面准备好 S3 bucket、Redis 服务器,甚至 RDS 实例,让 agent 去连。他们搭基础设施,绕过的正是「写个文件、指望它留在原地」这件事做不到。恶心。

临时性意味着时间限制。服务商设计沙箱系统时,参照的是 agent 预期会产生的工作负载。今天 agent 做的大多数事情花不了多少时间;事实上,它们往往只受限于前沿模型处理 token 的速度。测试套件跑得很快。第 99 百分位的沙箱 agent 运行,大概用不到 15 分钟。

Some feature requests involve compute and network time that far exceed token consumption. When I built the documentation site for the Sprites API, I had a Claude Sprite interact with the code and our API, building and testing API examples one by one. For some APIs, client interaction time alone would blow through the sandbox budget.

The limitations of the current approach are evident in how people pass state back and forth through "plan files"—files that are nominally prose but in practice are often just key-value stores encoded in excruciatingly awkward ways.

An agent running on a real computer can take advantage of an application's entire lifecycle. We saw this when Chris McCord built Phoenix.new. The agent behind the Phoenix.new application runs on a Fly Machine and can see the logs of the Phoenix app it generates. When a user action triggers an exception, Phoenix.new notices and sets out to figure out what happened.

Chris put a tremendous amount of work into this, and he was able to do it partly because he wrote his own agent. Today you can achieve the same thing with Claude plus an MCP server or some other approach that ships the logs over. But what you really need is simply to not blow your own sandbox to bits right after the agent finishes writing code.

A Galaxy-Brain Win

What follows will lose you. I know, because it's also what loses most of my team—most of them don't buy into my way of thinking.

The nature of software development is changing beneath our feet, and I think we're deluding ourselves into believing it will ultimately amount to nothing more than a reconfiguration of how professional developers deliver software.

I have kids. They have devices. I want some control over them. So I did what many of you in my situation would do: vibe-coded an MDM.

image

I built this thing with Claude. It's a Go application running on a Sprite with SQLite as the backend. The Anycast URL exported by my Sprite can be used directly as the MDM enrollment URL. Claude also helped me sort out all the hassle of the APNS Push Certificate. It just works. "Editing PHP files over FTP: we weren't wrong, we were just born in the wrong era!"

我这么用已经一个月了,跑在一台 Sprite 上,没觉得有任何理由要停下来。它解决了我一个真实的、重要的问题。需求变了,它可能跟着变,我直接让 Claude 改。也可能不变。对这个应用来说,开发环境就是生产环境,生产环境就是开发环境。

至于为什么不该把面向几百万人的应用跑在 Sprite 上,等我们写搭建过程的时候再细说。但大多数应用本来就不需要服务几百万人。那些最重要的日常应用,恰恰大多不会有百万级的用户。百万级用户的应用里确实有一些很重要,但多数只是在摧毁公民社会、烧坏我们的脑子,再给单个芝士汉堡配个专职司机。

真正解决人们实际问题的应用,会归那些被解决问题的人所有。而且大多数情况下,他们不需要一个专业的软件开发行会来把守功能开发的门。他们提需求,然后拿到东西。

我们所有人正在攻的这个问题,比“安全地加速专业软件开发者”要大得多。沙箱在拖我们的后腿。

去他妈的临时沙箱

显然,我在这里是想卖你东西。但这不代表我说错了。我上面这套论证,正是我们做出我要卖的这个东西的原因。

*## 我们已经把这些东西做出来了。

你现在就可以创建几十台 Sprite,只要一秒。 创建一台 Sprite。→*

走到这一步花了很久。我们自欺欺人了好几年。我们做过一个平台,用微型虚拟机横向扩展生产应用,这些虚拟机启动快到——只要你拿捏得恰到好处——就能拿它们凑合出一个相当不错的代码沙箱。但这事一直是方枘圆凿。

关于 Sprite 怎么工作,我们有很多话要说。它们和 Fly Machines 有关,但在几个要紧的地方截然不同。它们有一套全新的存储栈。编排方式不一样。没有 Dockerfile。

但现在,我只想让你想一想我在这里说的这件事。无论你最终是否会启动一个 Sprite,都值得问一句:如果你可以在任何地方运行一个编码 agent,你希望它更像云上 K8s 集群里一个只读沙箱,还是更像一台你打个响指就能召唤出来的完整 EC2 实例?

我认为答案显而易见。沙箱的时代结束了。一次性计算机的时代已经到来。

来源: Fly.io← 返回首页