但我也有点没 get 到 runtime 跑在宿主和跑在沙箱会有啥区别,egolite 是咋做的
回复 群友 7309: 区别主要在隔离的边界划在哪里,以及两边的代价。 先说宿主方式和沙箱方式的本质区别。这不只是安全性级别不同,连信任模型也换了。 宿主方式:你写的那层 runtime 直接运行在你自己机器的用户态,生成出来的 JS 就在这个进程里跑,进程本身拥有的权限它全部拥有,比如读你的文件、连外网、读取环境变量。你的安全边界只靠代码这一层来保证:runner 只暴露一个全局对象,生成代码只能调用这个对象上的方法,不能 require、不能访问 fs、不能用 fetch。这种做法的漏洞属于逻辑性的,比如某个方法参数没做校验,代码就能通过这个方法绕过限制,这不是内核能拦住的。 沙箱方式:即使生成代码完全失控,内核也拦着它。没用 --bind 的路径它看都看不到,--unshare-net 之后它没有网络栈。安全性由内核保证,而不是靠你写的校验逻辑。代价是要管挂载、管 socket、管进程生命周期;调试变麻烦,报错是“文件不存在”而不是“权限不足”;每跑一个任务多几十毫秒启动开销,不过这一点其实无所谓。 再直白点:宿主方式是“我相信我的 API 层写对了”,沙箱方式是“我不需要相信,反正它出不去”。 回到你真正关心的,也就是你们的场景。你们是个人本地部署,要复用登录态,用户就是自己的机器。这种情况下宿主方式的风险不是“用户被攻击”,而是模型生成的代码意外干了不该干的事,比如把 workdir 里的文件删了、死循环卡住浏览器,或者顺手读到 ~/.ssh。这些都属于可恢复的问题,不是灾难。所以宿主方式对你们是说得通的,省掉全部沙箱工程量。 但如果以后你想做成对别人也开放的东西,比如让别人把他们的 Agent 接进来,那沙箱方式就是必需的了,因为这时的威胁变成“别人家的模型在跑你的客户端”,你没法假设他们的 API 层写得对。 然后是 ego lite。我拿到了它的 README,它的做法跟这两种都不太一样,值得看一眼。 它的核心不是“把代码关起来”,而是“改浏览器本身”。ego lite 是个定制 Chromium,人和 Agent 共用一个浏览器实例,但每个 Agent 有自己的 Space,也就是独立的 profile、Cookie、扩展、书签。Agent 在后台 Space 里干活,你在前台的标签页一动都不动。所以登录态复用不是靠“拷贝 profile 或者连 CDP 到宿主 Chrome”,而是因为 Agent 的 Space 本身就是这个浏览器的另一个 profile,天然带着登录态。 它的 API 层就是我上一轮说的“收窄能力面”那个形态。ego-browser 是 Agent 和浏览器之间的连接层,把浏览器能力封成一组页内 JavaScript 函数:snapshot、fill、click、wait、navigate、capture。Agent 写一段调用这些函数的 JS,ego-browser 一次性在页面上执行完。所以生成代码是真的在跑,但它能碰到的只有这几个函数,而且是在页面的上下文里,不是在一个有 fs 的进程里。 关键点:它在宿主上跑,没有 bwrap,也没有 wasm。它的隔离靠三件事叠起来。一是浏览器内核层做 Space 隔离,也就是 profile 级隔离。二是 API 面只有六个动作,能力收紧。三是代码执行在页面上下文,本来就拿不到 Node 的文件系统。它赌的是“用户是自己的,威胁来自模型乱来,而模型乱来的破坏面已经被 API 白名单框住了”。 这对你们的直接启示是:如果你们的 runtime 是跑在用户自己的机器上、用户自己就是使用者,那真没必要上 bwrap。把力气花在 ego lite 花的地方:让 API 面足够小,让每次调用的参数校验足够严,让“生成代码”在一个没有宿主能力的上下文里执行。比如 Node 下用 vm.createContext 提供一个只有你们 API 的沙箱上下文,不传 require 进去,再配个超时;或者 vm 都嫌弱,就起一个没有 fs 权限的子进程。 不过有一条 ego lite 有、你们可能没有的:它是从内核层开始定制的,所以 Space 之间的隔离、Snapshot 的质量,都能在浏览器内部做。你们如果只是用 CDP 包一层,那 profile 隔离就得靠“给每个任务起独立的 Chrome user-data-dir”,这个做起来比较麻烦。个人部署下可能不需要那么彻底,一个 data-dir 复用就够了。 所以我的建议变一下:先确认你们的用户是谁。只有你自己或者小圈子自用,那就走宿主方式加 vm 上下文,别把 bwrap 引进来;如果打算面向不确定的第三方,再上沙箱,那时候 bwrap 那一套才值得写。 你们现在是走 CDP 连宿主已有的 Chrome,还是自己起一个带独立 user-data-dir 的实例?这个定了,我就能说清楚登录态到底怎么复用。