Sandbox de plugins

En esta página

Los plugins sandboxed se ejecutan en un runtime aislado que proporciona un runner de sandbox. Las instalaciones del marketplace y del registro siempre se ejecutan sandboxed, al igual que los plugins listados bajo sandboxed: [] en la integración emdash(). Los plugins listados bajo plugins: [] se ejecutan en el proceso del servidor y no usan el runner.

El runner depende de la plataforma de despliegue. En Cloudflare Workers, cada plugin se ejecuta como un Dynamic Worker creado a través del binding Worker Loader. En Node.js, el servidor inicia workerd, el runtime open-source de Workers, como proceso hijo y ejecuta cada plugin como un servicio dentro de él. La opción sandboxRunner de emdash() selecciona el runner. Sin ella, los plugins bajo sandboxed: [] nunca se cargan, y un marketplace configurado hace fallar el build con “Marketplace requires sandboxRunner to be configured”.

La siguiente tabla resume lo que cada runner necesita y aplica.

Cloudflare WorkersNode.js
sandboxRunnersandbox() de @emdash-cms/cloudflare"@emdash-cms/sandbox-workerd/sandbox"
RequisitosPlan Workers Paid, un binding worker_loaders, PluginBridge exportado desde el punto de entrada del WorkerEl paquete workerd
Acceso a BDEl binding D1 DB, independiente del adaptador configuradoLa base de datos configurada
Límites aplicadosTiempo de CPU, subrequests, wall timeWall time

Cloudflare Workers

Los Dynamic Workers están disponibles en el plan Workers Paid. El runner necesita el binding y la exportación del punto de entrada que se muestran abajo; las plantillas *-cloudflare incluyen ambos.

  1. Agregue el binding Worker Loader a wrangler.jsonc. El runner lo lee bajo el nombre LOADER:

    {
    	"worker_loaders": [
    		{
    			"binding": "LOADER",
    		},
    	],
    }
  2. Exporte PluginBridge desde el punto de entrada del Worker y apunte main a ese archivo. PluginBridge es el punto de entrada a través del cual los plugins sandboxed acceden a contenido, medios, almacenamiento y correo; el runner lo busca en las exportaciones del módulo de entrada:

    import handler, { createScheduledHandler, PluginBridge } from "@emdash-cms/cloudflare/worker";
    
    export { PluginBridge };
    
    export default {
    	...handler,
    	scheduled: createScheduledHandler(),
    } satisfies ExportedHandler;
    {
    	"main": "./src/worker.ts",
    }
  3. Seleccione el runner en la integración emdash():

    import { d1, r2, sandbox } from "@emdash-cms/cloudflare";
    
    emdash({
    	database: d1({ binding: "DB" }),
    	storage: r2({ binding: "MEDIA" }),
    	sandboxRunner: sandbox(),
    });

Node.js

  1. Instale el runner junto con workerd, que es una dependencia peer:

    npm install @emdash-cms/sandbox-workerd workerd

    El paquete workerd instala el binario para la plataforma actual (Linux, macOS y Windows en x64; Linux y macOS en arm64) a través de una dependencia opcional. Instale con dependencias opcionales habilitadas, en la plataforma donde el servidor se ejecuta. En un build Docker multi-stage, ejecute la instalación en una etapa con la misma plataforma que la etapa de runtime.

  2. Seleccione el runner en la integración emdash():

    import { sqlite } from "emdash/db";
    
    emdash({
    	database: sqlite({ url: "file:./data/emdash.db" }),
    	sandboxRunner: "@emdash-cms/sandbox-workerd/sandbox",
    });
  3. Para desarrollo, instale miniflare como dependencia de desarrollo:

    npm install -D miniflare

    Cuando NODE_ENV es development, que astro dev establece, y miniflare está instalado, el runner pasa los plugins a Miniflare, que gestiona su propio proceso workerd; la política de crash de abajo no aplica. astro preview establece NODE_ENV en production y node ./dist/server/entry.mjs lo deja sin establecer; ambos usan workerd.

Cómo se ejecuta el proceso workerd

EmDash inicia workerd mientras se inicializa en la primera solicitud al sitio, una vez que los plugins sandboxed están cargados, y espera hasta 10 segundos para que los servicios de plugins respondan. Instalar o actualizar un plugin desde el admin lo reinicia. Todo lo que workerd escribe en stdout o stderr aparece en la salida del servidor con el prefijo [emdash:workerd].

Los servicios de plugins escuchan en 127.0.0.1, y el canal de vuelta al servidor es un socket de dominio Unix (un puerto TCP 127.0.0.1 en Windows). No es necesario abrir ningún puerto de entrada.

El proceso hijo recibe solo PATH, HOME, TMPDIR, TMP, TEMP, LANG y LC_ALL del entorno del servidor, por lo que los secretos en el entorno del servidor no entran en el sandbox. Para pasar más variables, establezca EMDASH_WORKERD_PASSTHROUGH_ENV con una lista de nombres de variables separados por comas.

Si workerd sale inesperadamente, el runner registra [emdash:workerd] workerd exited with <reason> y lo reinicia en la siguiente invocación, con un retraso que comienza en 1 segundo y se duplica hasta 30 segundos. Cuando workerd se bloquea más de cinco veces en 60 segundos, el runner deja de reiniciarlo y registra [emdash:workerd] workerd crashed 5 times in 60 seconds, giving up. A partir de entonces, cada hook y ruta de plugin sandboxed falla con Plugin sandbox unavailable for <plugin>: workerd is not running hasta que el servidor se reinicie. Un SIGTERM al servidor termina workerd con él.

Límites de recursos

Cada runner aplica el mismo conjunto de límites por invocación de plugin. Los límites son fijos; la integración emdash() no tiene opción para ellos.

LímiteValorCloudflare WorkersNode.js
Tiempo CPU50 msAplicado por el Worker Loader; el plugin lanza cuando alcanza el límiteNo aplicado
Subrequests10Aplicado por el Worker Loader; el plugin lanza cuando alcanza el límiteNo aplicado
Memoria128 MBNo aplicado por plugin; el techo de memoria de aislamiento de la plataforma aplicaNo aplicado
Wall time30 sAplicado por el runnerAplicado por el runner

Cuando un hook o ruta excede el límite de wall-time, la invocación falla con Plugin <id> exceeded wall-time limit of 30000ms during hook:<name> (o route:<name>). Para un hook, EmDash registra la falla con el prefijo EmDash: Sandboxed plugin <id> y continúa la solicitud sin el resultado de ese plugin. Una ruta de plugin que excede el límite falla para su llamador.

Cuando el runner no está disponible

Un runner configurado puede no estar disponible: en Cloudflare Workers cuando falta el binding worker_loaders o la exportación PluginBridge, en Node.js cuando workerd no está instalado o su binario no ejecuta. EmDash entonces registra la siguiente advertencia cuando el runtime inicia:

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.

Los plugins bajo sandboxed: [] no se cargan, los plugins instalados del marketplace y registro no se ejecutan, y una nueva instalación desde el admin falla con el código de error SANDBOX_NOT_AVAILABLE. El resto del sitio no se ve afectado.

Ejecutar plugins sandboxed en proceso

Establezca sandbox: false en emdash() para ejecutar los plugins bajo sandboxed: [] y los plugins instalados del marketplace en el proceso del servidor, sin aislamiento ni límites. Es una opción de depuración que distingue un bug en un plugin de un bug en el sandbox. La siguiente configuración desactiva el sandbox en un sitio Node.js:

emdash({
	sandboxRunner: "@emdash-cms/sandbox-workerd/sandbox",
	sandbox: false,
});

En Cloudflare Workers, el runtime rechaza iniciar con sandbox: false is not supported in Cloudflare Workers.

Resolución de problemas

Cada entrada está encabezada por el mensaje tal como lo registra el servidor, o por el código de error que devuelve el admin.

”Plugin sandbox is configured but not available on this platform”

En Cloudflare Workers, verifique ambos requisitos: wrangler.jsonc tiene un binding worker_loaders llamado LOADER, y main apunta a un archivo que exporta PluginBridge. Desplegar el binding requiere el plan Workers Paid.

En Node.js, ejecute el binario que el runner usa:

npx workerd --version

Si el comando falla, workerd falta en node_modules o el binario instalado no se ejecuta en esta plataforma. Reinstale en la plataforma objetivo con dependencias opcionales habilitadas.

”workerd failed to start within 10 seconds”

El proceso hijo se inició, pero sus servicios de plugins no respondieron dentro de 10 segundos. Las líneas con prefijo [emdash:workerd] antes de este mensaje contienen la salida de workerd mismo, incluyendo errores de configuración y arranque. El runner reintenta en la siguiente invocación.

”workerd crashed 5 times in 60 seconds, giving up”

El runner ha dejado de reiniciar workerd. Las líneas [emdash:workerd] workerd exited with <reason> antes de este mensaje nombran el código de salida o señal de cada crash. Solucione la causa, luego reinicie el servidor.

SANDBOX_NOT_AVAILABLE al instalar un plugin

La solicitud de instalación del admin fue rechazada porque el runner falta o no está disponible. Configure el runner para la plataforma, o solucione la causa de la advertencia de inicio anterior, y redespliegue.