Plugins em sandbox precisam de um runner de plataforma além da declaração do plugin. Instalações do marketplace e do registro sempre usam esse runner, assim como plugins listados em sandboxed: []. Plugins nativos em plugins: [] rodam no processo do servidor EmDash e não obtêm isolamento de sandbox.
O runner depende da plataforma de implantação. No Cloudflare Workers, cada plugin roda como um Dynamic Worker criado através do binding Worker Loader. No Node.js, o servidor inicia o workerd, o runtime Workers open-source, como processo filho e executa cada plugin como um serviço dentro dele. A opção sandboxRunner do emdash() seleciona o runner e habilita o catálogo do registro hospedado. Sem ela, plugins em sandboxed: [] não são carregados. Um registro configurado explicitamente permanece navegável, mas a instalação ou atualização de um plugin em sandbox falha com SANDBOX_NOT_AVAILABLE.
A tabela a seguir resume o que cada runner precisa e aplica.
| Cloudflare Workers | Node.js | |
|---|---|---|
sandboxRunner | sandbox() de @emdash-cms/cloudflare | "@emdash-cms/sandbox-workerd/sandbox" |
| Requisitos | Plano Workers Paid, um binding worker_loaders, PluginBridge exportado do ponto de entrada do Worker | O pacote workerd |
| Acesso ao BD | O binding D1 DB, independente do adaptador configurado | O banco de dados configurado |
| Limites aplicados | Tempo de CPU, sub-requisições, tempo de parede | Tempo de parede |
Cloudflare Workers
Dynamic Workers estão disponíveis no plano Workers Paid. Os templates *-cloudflare incluem a exportação do ponto de entrada abaixo, mas deixam o binding comentado, então novos projetos são implantados no plano Workers gratuito, a menos que você habilite plugins em sandbox durante o scaffolding.
-
Habilite o binding Worker Loader em
wrangler.jsonc. O runner o lê com o nomeLOADERe seleciona o sandbox do Cloudflare somente quando esse binding está presente:{ "worker_loaders": [ { "binding": "LOADER", }, ], }Se a configuração do Wrangler usa ambientes nomeados, defina
CLOUDFLARE_ENVdurante o build do Astro. O plugin Vite do Cloudflare esandbox()lerão então o mesmo ambiente. Bindings não são herdados, então adicioneLOADERa cada ambiente nomeado que executa plugins em sandbox. -
Exporte
PluginBridgedo ponto de entrada do Worker e apontemainpara esse arquivo.PluginBridgeé o ponto de entrada pelo qual plugins em sandbox acessam conteúdo, mídia, armazenamento e e-mail; o runner o procura nas exportações do 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", } -
Selecione o runner na integração
emdash():import { d1, r2, sandbox } from "@emdash-cms/cloudflare"; emdash({ database: d1({ binding: "DB" }), storage: r2({ binding: "MEDIA" }), sandboxRunner: sandbox(), });
Node.js
-
Instale o runner junto com
workerd, que é uma dependência peer:npm install @emdash-cms/sandbox-workerd workerdO pacote
workerdinstala o binário para a plataforma atual (Linux, macOS e Windows em x64; Linux e macOS em arm64) através de uma dependência opcional. Instale com dependências opcionais habilitadas, na plataforma onde o servidor roda. Em um build Docker multi-stage, execute a instalação em um estágio com a mesma plataforma do estágio de runtime. -
Selecione o runner na integração
emdash():import { sqlite } from "emdash/db"; emdash({ database: sqlite({ url: "file:./data/emdash.db" }), sandboxRunner: "@emdash-cms/sandbox-workerd/sandbox", });
O runner declara Miniflare como dependência opcional. Gerenciadores de pacotes a instalam por padrão. Quando NODE_ENV é development, que astro dev define, o runner entrega os plugins ao Miniflare, que gerencia seu próprio processo workerd; a política de crash abaixo não se aplica. Se as dependências opcionais foram omitidas, o runner usa workerd em vez disso. astro preview define NODE_ENV como production e node ./dist/server/entry.mjs o deixa indefinido; ambos usam workerd.
Como o processo workerd roda
EmDash inicia workerd durante a inicialização na primeira requisição ao site, uma vez que os plugins em sandbox estão carregados, e espera até 10 segundos para os serviços dos plugins responderem. Instalar ou atualizar um plugin pelo admin o reinicia. Tudo que workerd escreve em stdout ou stderr aparece na saída do servidor com o prefixo [emdash:workerd].
Os serviços de plugin escutam em 127.0.0.1, e o canal de volta ao servidor é um socket de domínio Unix (uma porta TCP 127.0.0.1 no Windows). Nenhuma porta de entrada precisa ser aberta.
O processo filho recebe apenas PATH, HOME, TMPDIR, TMP, TEMP, LANG e LC_ALL do ambiente do servidor, então segredos no ambiente do servidor ficam fora do sandbox. Para passar mais variáveis, defina EMDASH_WORKERD_PASSTHROUGH_ENV como uma lista separada por vírgulas de nomes de variáveis.
Se workerd encerrar inesperadamente, o runner registra [emdash:workerd] workerd exited with <reason> e o reinicia na próxima invocação, com um atraso que começa em 1 segundo e dobra até 30 segundos. Quando workerd falha mais de cinco vezes em 60 segundos, o runner para de reiniciá-lo e registra [emdash:workerd] workerd crashed 5 times in 60 seconds, giving up. A partir daí, todo hook e rota de plugin em sandbox falha com Plugin sandbox unavailable for <plugin>: workerd crashed 5 times in 60 seconds and the runner stopped retrying; restart the server. Reiniciar o servidor inicia workerd novamente, assim como instalar ou atualizar um plugin pelo admin. Um SIGTERM para o servidor encerra workerd junto.
Limites de recursos
Cada runner aplica o mesmo conjunto de limites por invocação de plugin. Os limites são fixos; a integração emdash() não tem opção para eles.
| Limite | Valor | Cloudflare Workers | Node.js |
|---|---|---|---|
| Tempo de CPU | 50 ms | Aplicado pelo Worker Loader; o plugin lança exceção ao atingir o limite | Não aplicado |
| Sub-requisições | 10 | Aplicado pelo Worker Loader; o plugin lança exceção ao atingir o limite | Não aplicado |
| Memória | 128 MB | Não aplicado por plugin; o teto de memória isolate da plataforma se aplica | Não aplicado |
| Tempo de parede | 30 s | Aplicado pelo runner | Aplicado pelo runner |
Quando um hook ou rota excede o limite de tempo de parede, a invocação falha com Plugin <id> exceeded wall-time limit of 30000ms during hook:<name> (ou route:<name>). Para um hook, EmDash registra a falha com o prefixo EmDash: Sandboxed plugin <id> e continua a requisição sem o resultado desse plugin. Uma rota de plugin que excede o limite falha para quem a chamou.
Quando o runner não está disponível
No Cloudflare Workers, sandbox() verifica wrangler.jsonc em tempo de build. Sem um binding worker_loaders chamado LOADER, ele deixa o runner indefinido e registra o seguinte aviso:
[emdash] Sandboxed plugins are disabled because wrangler.jsonc has no LOADER Worker Loader binding. Worker Loader requires a Workers paid plan.
Um runner selecionado pode ainda não estar disponível em tempo de execução: no Cloudflare Workers quando o binding LOADER implantado ou a exportação PluginBridge estão faltando, e no Node.js quando workerd não está instalado ou seu binário não roda. EmDash então registra um aviso com a causa que o runner reporta após os dois pontos. O seguinte aviso é registrado no Cloudflare Workers quando o binding está faltando:
EmDash: Plugin sandbox is configured but not available on this platform: the worker has no worker_loaders binding named LOADER. Sandboxed plugins will not be loaded.
Plugins em sandboxed: [] não são carregados, plugins instalados do marketplace e registro não rodam, e uma nova instalação pelo admin falha com o código de erro SANDBOX_NOT_AVAILABLE. O resto do site não é afetado.
Executar plugins em sandbox dentro do processo
Defina sandbox: false em emdash() para executar os plugins em sandboxed: [] e plugins do marketplace instalados no processo do servidor, sem isolamento ou limites. É uma opção de depuração que distingue um bug em um plugin de um bug no sandbox. A seguinte configuração desativa o sandbox em um site Node.js:
emdash({
sandboxRunner: "@emdash-cms/sandbox-workerd/sandbox",
sandbox: false,
});
No Cloudflare Workers, o runtime se recusa a iniciar com sandbox: false is not supported in Cloudflare Workers.
Solução de problemas
Cada entrada é precedida pela mensagem como o servidor a registra, ou pelo código de erro que o admin retorna.
”[emdash] Sandboxed plugins are disabled because wrangler.jsonc has no LOADER Worker Loader binding”
O adaptador Cloudflare não selecionou um runner de sandbox porque a configuração Wrangler em tempo de build não tem um binding worker_loaders chamado LOADER. Esta é a configuração esperada no plano Workers gratuito. Em um plano Workers paid, habilite o binding em wrangler.jsonc e reconstrua o site.
”Plugin sandbox is configured but not available on this platform”
O texto após os dois pontos nomeia a causa. No Cloudflare Workers, the worker has no worker_loaders binding named LOADER significa que wrangler.jsonc precisa de um binding worker_loaders chamado LOADER, e the worker entrypoint does not export PluginBridge significa que o arquivo para o qual main aponta deve exportar PluginBridge. Implantar o binding requer o plano Workers Paid.
No Node.js, workerd is missing or its binary does not run on this platform significa que o runner não conseguiu executar workerd. Execute o binário instalado diretamente para que a verificação não possa baixar um pacote faltante:
./node_modules/.bin/workerd --version
No Windows, execute node_modules\\.bin\\workerd.cmd --version. Se o comando falhar, workerd está faltando em node_modules ou o binário instalado não roda nesta plataforma. Reinstale na plataforma alvo com dependências opcionais habilitadas.
”workerd failed to start within 10 seconds”
O processo filho iniciou, mas seus serviços de plugin não responderam dentro de 10 segundos. As linhas com prefixo [emdash:workerd] antes desta mensagem contêm a saída do workerd em si, incluindo erros de configuração e inicialização. O runner tenta novamente na próxima invocação.
”workerd crashed 5 times in 60 seconds, giving up”
O runner parou de reiniciar workerd. As linhas [emdash:workerd] workerd exited with <reason> antes desta mensagem nomeiam o código de saída ou sinal de cada crash. Corrija a causa e então reinicie o servidor.
SANDBOX_NOT_AVAILABLE ao instalar um plugin
A requisição de instalação do admin foi recusada porque o runner está faltando ou indisponível. Quando um runner está configurado, a mensagem de erro termina com a mesma causa do aviso de inicialização acima. Configure o runner para a plataforma, ou corrija essa causa, e reimplante.