Plugins sandboxed são executados em um runtime isolado fornecido por um runner de sandbox. Instalações do marketplace e do registro sempre são executadas em sandbox, assim como os plugins listados sob sandboxed: [] na integração emdash(). Plugins listados sob plugins: [] são executados no processo do servidor e não usam o runner.
O runner depende da plataforma de deploy. No Cloudflare Workers, cada plugin é executado como um Dynamic Worker criado através do binding Worker Loader. No Node.js, o servidor inicia workerd, o runtime Workers open-source, como processo filho e executa cada plugin como um serviço dentro dele. A opção sandboxRunner de emdash() seleciona o runner. Sem ela, os plugins sob sandboxed: [] nunca são carregados, e um marketplace configurado faz o build falhar com “Marketplace requires sandboxRunner to be configured”.
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, subrequests, wall time | Wall time |
Cloudflare Workers
Dynamic Workers estão disponíveis no plano Workers Paid. O runner precisa do binding e da exportação do ponto de entrada abaixo; os templates *-cloudflare fornecem ambos.
-
Adicione o binding Worker Loader ao
wrangler.jsonc. O runner o lê com o nomeLOADER:{ "worker_loaders": [ { "binding": "LOADER", }, ], } -
Exporte
PluginBridgedo ponto de entrada do Worker e apontemainpara esse arquivo.PluginBridgeé o ponto de entrada pelo qual plugins sandboxed acessam conteúdo, mídia, armazenamento e email; 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 é executado. Em um build Docker multi-stage, execute a instalação em um estágio com a mesma plataforma que o 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", }); -
Para desenvolvimento, instale
miniflarecomo dependência de desenvolvimento:npm install -D miniflareQuando
NODE_ENVédevelopment, queastro devdefine, eminiflareestá instalado, o runner passa os plugins para o Miniflare, que gerencia seu próprio processoworkerd; a política de crash abaixo não se aplica.astro previewdefineNODE_ENVcomoproductionenode ./dist/server/entry.mjso deixa indefinido; ambos usamworkerd.
Como o processo workerd funciona
O EmDash inicia workerd durante a inicialização na primeira requisição ao site, após os plugins sandboxed serem carregados, e aguarda até 10 segundos para que os serviços de plugins respondam. 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 plugins 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 com uma lista de nomes de variáveis separados por vírgula.
Se workerd sair 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 crasha 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 sandboxed falha com Plugin sandbox unavailable for <plugin>: workerd is not running até que o servidor seja reiniciado. Um SIGTERM para o servidor termina workerd com ele.
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 CPU | 50 ms | Aplicado pelo Worker Loader; o plugin lança quando atinge o limite | Não aplicado |
| Subrequests | 10 | Aplicado pelo Worker Loader; o plugin lança quando atinge o limite | Não aplicado |
| Memória | 128 MB | Não aplicado por plugin; o teto de memória de isolamento da plataforma se aplica | Não aplicado |
| Wall time | 30 s | Aplicado pelo runner | Aplicado pelo runner |
Quando um hook ou rota excede o limite de wall-time, a invocação falha com Plugin <id> exceeded wall-time limit of 30000ms during hook:<name> (ou route:<name>). Para um hook, o EmDash registra a falha com o prefixo EmDash: Sandboxed plugin <id> e continua a requisição sem o resultado daquele plugin. Uma rota de plugin que excede o limite falha para seu chamador.
Quando o runner não está disponível
Um runner configurado ainda pode estar indisponível: no Cloudflare Workers quando falta o binding worker_loaders ou a exportação PluginBridge, no Node.js quando workerd não está instalado ou seu binário não executa. O EmDash então registra o seguinte aviso quando o 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.
Plugins sob sandboxed: [] não são carregados, plugins instalados do marketplace e registro não são executados, e uma nova instalação pelo admin falha com o código de erro SANDBOX_NOT_AVAILABLE. O restante do site não é afetado.
Executar plugins sandboxed no processo
Defina sandbox: false em emdash() para executar os plugins sob sandboxed: [] e plugins instalados do marketplace 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 recusa iniciar com sandbox: false is not supported in Cloudflare Workers.
Solução de problemas
Cada entrada é encabeçada pela mensagem como o servidor a registra, ou pelo código de erro que o admin retorna.
”Plugin sandbox is configured but not available on this platform”
No Cloudflare Workers, verifique ambos os requisitos: wrangler.jsonc tem um binding worker_loaders chamado LOADER, e main aponta para um arquivo que exporta PluginBridge. O deploy do binding requer o plano Workers Paid.
No Node.js, execute o binário que o runner usa:
npx workerd --version
Se o comando falhar, workerd está faltando em node_modules ou o binário instalado não executa nesta plataforma. Reinstale na plataforma alvo com dependências opcionais habilitadas.
”workerd failed to start within 10 seconds”
O processo filho foi iniciado, mas seus serviços de plugins não responderam em 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, 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. Configure o runner para a plataforma, ou corrija a causa do aviso de inicialização acima, e reimplante.