Los plugins en sandbox están aislados por defecto. Para hacer algo más allá de leer y escribir su propio KV y almacenamiento, un plugin debe declarar una capability en su descriptor. El puente del sandbox controla cada API proporcionada por el host basándose en esas declaraciones: un plugin que no declaró content:read no obtiene un ctx.content, y uno que no declaró network:request no obtiene ctx.http.
Esta página cubre qué otorga cada capability, cómo el sandbox las aplica y qué no es aplicable.
Declarar capabilities
Las capabilities residen en el descriptor (el archivo importado por astro.config.mjs), junto con id, version y entrypoint:
export function helloPlugin(): PluginDescriptor {
return {
id: "plugin-hello",
version: "0.1.0",
format: "standard",
entrypoint: "@my-org/plugin-hello/sandbox",
capabilities: ["content:read", "network:request"],
allowedHosts: ["api.example.com"],
};
}
Declara solo lo que el plugin realmente necesita. Las declaraciones de capabilities también son lo que el marketplace muestra a los operadores del sitio en el diálogo de consentimiento: las capabilities adicionales son fricción en el momento de la instalación y una señal de seguridad en las auditorías.
Referencia de capabilities
| Capability | Otorga acceso a |
|---|---|
content:read | ctx.content.get(), ctx.content.list() |
content:write | ctx.content.create(), ctx.content.update(), ctx.content.delete() (implica content:read) |
media:read | ctx.media.get(), ctx.media.list() |
media:write | ctx.media.getUploadUrl(), ctx.media.upload(), ctx.media.delete() (implica media:read) |
network:request | ctx.http.fetch() — restringido a allowedHosts |
network:request:unrestricted | ctx.http.fetch() sin restricción de host (solo para URLs configuradas por el usuario) |
users:read | ctx.users.get(), ctx.users.getByEmail(), ctx.users.list() |
email:send | ctx.email.send() (requiere un plugin proveedor de email configurado) |
hooks.email-transport:register | Permite registrar el hook exclusivo email:deliver (proveedores de transporte) |
hooks.email-events:register | Permite registrar hooks email:beforeSend / email:afterSend |
hooks.page-fragments:register | Permite registrar el hook page:fragments (solo plugins nativos) |
Algunas cosas que vale la pena saber:
- Implicaciones.
content:writeimplica automáticamentecontent:read;media:writeimplicamedia:read;network:request:unrestrictedimplicanetwork:request. No necesitas listar ambos. network:request:unrestrictedexiste para URLs configuradas por el usuario. Un plugin de webhook donde el operador escribe la URL de destino necesita alcanzar hosts que no están en el manifiesto. Los plugins que siempre llaman a APIs conocidas deben usarnetwork:request+allowedHosts.email:sendestá controlado por la configuración, no solo por la capability. Un plugin puede declararemail:send, peroctx.emailsolo se poblará si algún otro plugin ha registrado un transporteemail:deliver.
Listas de permitidos de hosts de red
Los plugins con network:request solo pueden obtener hosts listados en allowedHosts. Se admiten comodines para subdominios:
capabilities: ["network:request"],
allowedHosts: [
"api.example.com", // host exacto
"*.cdn.example.com", // cualquier subdominio de cdn.example.com
],
El puente verifica el host de la URL de solicitud contra la lista de permitidos antes de reenviar la solicitud. Una solicitud a un host que no se declaró arroja dentro del plugin sin salir nunca del sandbox.
network:request:unrestricted omite la verificación de la lista de permitidos por completo. Está destinado a plugins donde el operador configura la URL de destino en tiempo de ejecución (remitentes de webhook, reenviadores HTTP genéricos). Evítalo para plugins donde el destino es parte del diseño del plugin — declara network:request con hosts explícitos en su lugar, para que el diálogo de consentimiento diga a los operadores exactamente a dónde el plugin va a llamar.
Qué aplica el sandbox
Cuando un ejecutor de sandbox está activo, el runtime aplica:
-
Control de capabilities. La fábrica PluginContext solo puebla
ctx.content,ctx.media,ctx.http,ctx.users,ctx.emailcuando se declara la capability correspondiente. Llamar a un método en una capability no declarada no es posible — no hay ningún objeto allí. -
Ámbito de almacenamiento y KV. Cada operación de almacenamiento y KV está limitada al id del plugin. Un plugin no puede leer el KV de otro plugin o sus colecciones de almacenamiento, y solo puede acceder a las colecciones de almacenamiento que declaró en el descriptor.
-
Aislamiento de red. El
fetch()directo y otras primitivas de red están bloqueadas por el ejecutor. La única forma de alcanzar la red esctx.http.fetch(), que pasa por la validación de host del puente. -
Sin vinculaciones de host. Los plugins en sandbox no ven variables de entorno, el sistema de archivos o ninguna vinculación de plataforma — incluso si tu worker host las tiene. El runtime del plugin es un aislado limpio con solo el puente y las capabilities declaradas.
-
Límites de recursos. El ejecutor puede aplicar límites de CPU, subpeticiones, reloj de pared y memoria por invocación. Los límites exactos dependen de qué ejecutor estés usando; el ejecutor de Cloudflare usa los límites del Worker Loader de la plataforma (50ms de CPU por invocación, 10 subpeticiones, 30 segundos de reloj de pared, ~128MB de memoria). Los hooks que exceden los límites del ejecutor se abortan; el timeout del hook de EmDash (
timeouten la configuración del hook) aplica un techo más estricto encima de eso.
Qué no aplica el sandbox
Algunas cosas que el sistema de capabilities no puede cubrir:
- Comportamiento dentro de una capability otorgada. Un plugin con
content:writepuede editar cualquier contenido, no solo el suyo propio. Las capabilities son gruesas — dicen “este plugin puede escribir contenido”, no “este plugin puede escribir solo el contenido que creó”. La revisión en tiempo de auditoría es la única verificación de lo que un plugin realmente hace dentro de su otorgamiento. - Confianza del operador en Node.js. Cuando el ejecutor de sandbox configurado reporta no disponible (sin Cloudflare Worker Loader, sin ejecutor del lado de Node instalado, etc.), los plugins
sandboxed: []se omiten en el inicio. Puedes moverlos aplugins: []para ejecutarlos en proceso — pero entonces no hay aislado V8, sin límites de recursos, y el plugin puede llamar afetch()directamente o leer variables de entorno. Trata eso como confianza de nivel nativo. - Canales laterales. El tiempo, la salida de registros y los datos almacenados son visibles para cualquiera con el acceso apropiado al entorno del host. No uses el sandbox como un límite de confidencialidad contra el operador que lo ejecuta.
Consentimiento de capabilities
Cuando un operador instala un plugin en sandbox desde el marketplace, EmDash muestra un diálogo de consentimiento que lista las capabilities declaradas. Las actualizaciones que agregan capabilities — por ejemplo, un plugin que anteriormente solo leía contenido ahora quiere hacer solicitudes de red — surgen como un diff de capabilities y requieren una aprobación nueva antes de que la nueva versión entre en vigor.
Por eso es importante declarar capabilities adicionales incluso si “podrías usarlas más tarde”. Aparecen como fricción en cada instalación y actualización, y las auditorías de seguridad marcan plugins que piden más de lo que obviamente necesitan. Lista exactamente lo que el plugin usa, y agrega nuevas capabilities en una versión real cuando el plugin realmente comience a usarlas.
Validación en tiempo de empaquetado
emdash plugin bundle y emdash plugin publish realizan verificaciones adicionales:
- Cada capability declarada debe estar en el conjunto conocido (los errores tipográficos fallan la construcción).
- Un plugin que declara
network:requestsin poblarallowedHostsdesencadena una advertencia — declara hosts o cambia anetwork:request:unrestrictedy documenta por qué. - Los nombres de capabilities deprecados desencadenan advertencias durante
bundle/validatey un fallo duro enpublish. - El
backend.jsempaquetado no puede importar módulos integrados de Node.js (fs,path,child_process, etc.) — los runtimes de sandbox no los proporcionan.
Consulta Empaquetado y publicación para la lista completa de verificaciones.