沙箱插件在沙箱运行器提供的隔离运行时中运行。Marketplace 和注册表安装始终在沙箱中运行,emdash() 集成中 sandboxed: [] 下列出的插件也是如此。plugins: [] 下列出的插件在服务器进程中运行,不使用运行器。
运行器取决于部署平台。在 Cloudflare Workers 上,每个插件作为通过 Worker Loader 绑定创建的 Dynamic Worker 运行。在 Node.js 上,服务器启动 workerd(开源 Workers 运行时)作为子进程,并将每个插件作为其中的服务运行。emdash() 的 sandboxRunner 选项选择运行器。没有它,sandboxed: [] 下的插件永远不会被加载,配置的 marketplace 会使构建失败并显示 “Marketplace requires sandboxRunner to be configured”。
下表总结了每个运行器的需求和强制执行的内容。
| Cloudflare Workers | Node.js | |
|---|---|---|
sandboxRunner | @emdash-cms/cloudflare 的 sandbox() | "@emdash-cms/sandbox-workerd/sandbox" |
| 要求 | Workers 付费计划、worker_loaders 绑定、从 Worker 入口点导出的 PluginBridge | workerd 包 |
| 数据库访问 | DB D1 绑定(与配置的适配器无关) | 配置的数据库 |
| 强制限制 | CPU 时间、子请求、墙钟时间 | 墙钟时间 |
Cloudflare Workers
Dynamic Workers 在 Workers 付费计划 中可用。运行器需要以下绑定和入口点导出;*-cloudflare 模板附带了两者。
-
将 Worker Loader 绑定添加到
wrangler.jsonc。运行器以LOADER名称读取它:{ "worker_loaders": [ { "binding": "LOADER", }, ], } -
从 Worker 入口点导出
PluginBridge,并将main指向该文件。PluginBridge是沙箱插件访问内容、媒体、存储和电子邮件的入口点;运行器在入口模块的导出中查找它:import handler, { createScheduledHandler, PluginBridge } from "@emdash-cms/cloudflare/worker"; export { PluginBridge }; export default { ...handler, scheduled: createScheduledHandler(), } satisfies ExportedHandler;{ "main": "./src/worker.ts", } -
在
emdash()集成中选择运行器:import { d1, r2, sandbox } from "@emdash-cms/cloudflare"; emdash({ database: d1({ binding: "DB" }), storage: r2({ binding: "MEDIA" }), sandboxRunner: sandbox(), });
Node.js
-
与作为对等依赖的
workerd一起安装运行器:npm install @emdash-cms/sandbox-workerd workerdworkerd包通过可选依赖为当前平台(x64 上的 Linux、macOS 和 Windows;arm64 上的 Linux 和 macOS)安装二进制文件。在服务器运行的平台上启用可选依赖进行安装。在多阶段 Docker 构建中,在与运行时阶段相同平台的阶段中运行安装。 -
在
emdash()集成中选择运行器:import { sqlite } from "emdash/db"; emdash({ database: sqlite({ url: "file:./data/emdash.db" }), sandboxRunner: "@emdash-cms/sandbox-workerd/sandbox", }); -
对于开发,安装
miniflare作为开发依赖:npm install -D miniflare当
NODE_ENV为development(astro dev设置)且miniflare已安装时,运行器将插件传递给 Miniflare,由 Miniflare 管理自己的workerd进程;以下崩溃策略不适用。astro preview将NODE_ENV设置为production,node ./dist/server/entry.mjs不设置;两者都使用workerd。
workerd 进程如何运行
EmDash 在站点首次请求时的初始化期间启动 workerd,在沙箱插件加载后,等待最多 10 秒让插件服务应答。从管理后台安装或更新插件会重新启动它。workerd 写入 stdout 或 stderr 的所有内容都以 [emdash:workerd] 前缀出现在服务器输出中。
插件服务在 127.0.0.1 上监听,返回服务器的通道是 Unix 域套接字(Windows 上是 127.0.0.1 TCP 端口)。无需打开入站端口。
子进程仅从服务器环境接收 PATH、HOME、TMPDIR、TMP、TEMP、LANG 和 LC_ALL,因此服务器环境中的密钥不会进入沙箱。要传递更多变量,将 EMDASH_WORKERD_PASSTHROUGH_ENV 设置为逗号分隔的变量名列表。
如果 workerd 意外退出,运行器记录 [emdash:workerd] workerd exited with <reason> 并在下次调用时重新启动,延迟从 1 秒开始,翻倍至 30 秒。当 workerd 在 60 秒内崩溃超过五次时,运行器停止重启并记录 [emdash:workerd] workerd crashed 5 times in 60 seconds, giving up。此后,所有沙箱插件钩子和路由都会失败并显示 Plugin sandbox unavailable for <plugin>: workerd is not running,直到服务器重启。向服务器发送 SIGTERM 也会终止 workerd。
资源限制
每个运行器对每次插件调用应用相同的限制集。限制是固定的;emdash() 集成没有相关选项。
| 限制 | 值 | Cloudflare Workers | Node.js |
|---|---|---|---|
| CPU 时间 | 50 ms | 由 Worker Loader 强制;插件达到限制时抛出 | 未强制 |
| 子请求 | 10 | 由 Worker Loader 强制;插件达到限制时抛出 | 未强制 |
| 内存 | 128 MB | 不按插件强制;平台的隔离区内存上限适用 | 未强制 |
| 墙钟时间 | 30 s | 由运行器强制 | 由运行器强制 |
当钩子或路由超过墙钟时间限制时,调用失败并显示 Plugin <id> exceeded wall-time limit of 30000ms during hook:<name>(或 route:<name>)。对于钩子,EmDash 以 EmDash: Sandboxed plugin <id> 前缀记录失败,并在没有该插件结果的情况下继续处理请求。超过限制的插件路由对其调用者失败。
当运行器不可用时
配置的运行器仍可能不可用:在 Cloudflare Workers 上缺少 worker_loaders 绑定或 PluginBridge 导出时,在 Node.js 上 workerd 未安装或其二进制文件无法运行时。EmDash 在运行时启动时记录以下警告:
EmDash: Plugin sandbox is configured but not available on this platform. Sandboxed plugins will not be loaded. If using @emdash-cms/sandbox-workerd/sandbox, ensure workerd is installed.
sandboxed: [] 下的插件不会被加载,已安装的 marketplace 和注册表插件不会运行,从管理后台进行的新安装会失败并显示错误代码 SANDBOX_NOT_AVAILABLE。站点的其余部分不受影响。
在进程内运行沙箱插件
在 emdash() 中设置 sandbox: false 以在没有隔离或限制的情况下在服务器进程中运行 sandboxed: [] 下的插件和已安装的 marketplace 插件。这是一个调试选项,用于区分插件中的错误和沙箱中的错误。以下配置在 Node.js 站点上关闭沙箱:
emdash({
sandboxRunner: "@emdash-cms/sandbox-workerd/sandbox",
sandbox: false,
});
在 Cloudflare Workers 上,运行时拒绝以 sandbox: false is not supported in Cloudflare Workers 启动。
故障排除
每个条目以服务器记录的消息或管理后台返回的错误代码为标题。
“Plugin sandbox is configured but not available on this platform”
在 Cloudflare Workers 上,检查两个要求:wrangler.jsonc 有一个名为 LOADER 的 worker_loaders 绑定,main 指向导出 PluginBridge 的文件。部署绑定需要 Workers 付费计划。
在 Node.js 上,运行运行器使用的二进制文件:
npx workerd --version
如果命令失败,workerd 在 node_modules 中缺失或安装的二进制文件无法在此平台上运行。在目标平台上启用可选依赖重新安装。
“workerd failed to start within 10 seconds”
子进程已启动,但其插件服务未在 10 秒内应答。此消息之前的 [emdash:workerd] 前缀行包含 workerd 本身的输出,包括配置和启动错误。运行器将在下次调用时重试。
“workerd crashed 5 times in 60 seconds, giving up”
运行器已停止重启 workerd。此消息之前的 [emdash:workerd] workerd exited with <reason> 行指出每次崩溃的退出代码或信号。修复原因,然后重启服务器。
安装插件时出现 SANDBOX_NOT_AVAILABLE
由于运行器缺失或不可用,管理后台的安装请求被拒绝。为平台配置运行器,或修复上述启动警告的原因,然后重新部署。