Topics

Organisation de la spécification des plugins EmDash 1.0 — Sandbox, Capability et registre distribué

  • column

Le 28 septembre 2026, EmDash, le CMS natif Astro créé par Cloudflare, a été officiellement publié en version 1.0. Par la suite, des mises à jour se sont succédé rapidement : la version 1.1 le 2 octobre, puis la version 1.2 le 7 octobre.

L'article sur la façon dont nous avons amélioré le blog interne « ゆるろぐ » de la version 0.22 à 1.2 est disponible ici. Cette fois-ci, je vais clarifier le « système de plugins », qui a connu le plus grand changement dans la version 1.0.

Pour comprendre ce qu'est EmDash en tant que CMS, consultez notre premier article.
En résumé, c'est « WordPress réimaginé avec Astro et Cloudflare ».

Comment les plug-ins ont-ils changé dans la version 1.0 ?

À l'époque de la version 0.x, les plug-ins étaient essentiellement des « paquets npm écrits dans le fichier de configuration Astro ». Puisqu'ils s'exécutaient dans le même processus que le site, les plug-ins disposaient des mêmes permissions que le code du site lui-même, ce qui leur permettait d'accéder à la base de données et aux variables d'environnement, tout comme WordPress.

La version 1.0 introduit un sandbox et un registre décentralisé.
C'est une amélioration majeure.

  • Par défaut, les plug-ins s'exécutent dans un environnement isolé et ne peuvent accéder au contenu du site, aux médias, aux utilisateurs, aux secrets ou au réseau.
  • Les permissions requises (Capability) sont déclarées par le plug-in et approuvées par l'administrateur du site lors de l'installation.
  • Les permissions approuvées ne sont pas simplement documentées - le runtime les applique effectivement.
  • Les plugins peuvent être installés en un clic depuis le panneau d'administration. La source de distribution est un registre décentralisé sur AT Protocol.

Le blog de Cloudflare l'explique en contraste avec le modèle WordPress où « tous les plugins partagent le même processus ».
Les personnes familières avec les incidents causés par les plugins WordPress comprendront vraiment l'intérêt de cette approche.

Les plugins ont deux « modes d'exécution »

D'abord, il faut comprendre que le même code de plugin dispose de deux modes d'exécution : Trusted (approuvé) et Sandboxed (isolé).

Trusted

Sandboxed

Lieu d'enregistrement

astro.config.mjs avec plugins: []

sandboxed: [] ou installation via le panneau d'administration

Environnement d'exécution

Même processus que le site

V8 isolate isolé

Capability

Déclaration uniquement (non appliquée)

Appliquée par le runtime

Réseau

Illimité

Bloqué. Seuls les hôtes inscrits dans allowedHosts sont accessibles via ctx.http

Accès aux données

Accès complet à la base de données

Votre stockage/KV uniquement, et dans les portées autorisées

Node.js API

Disponible

Non disponible (API Web uniquement)

Limites de ressources

Aucune

CPU 50ms, sous-requêtes 10, 30s, ~128MB

Cas d'utilisation appropriés

Code interne, paquets npm examinés

Tiers, distribution de registre

L'« isolation » signifie préparer un petit environnement d'exécution dédié pour chaque plugin et l'exécuter séparément du site principal.
Sur Cloudflare Workers, un mécanisme appelé Dynamic Workers démarre le Worker du plugin au moment de la requête.
À partir de la version 1.0, même lors de l'exécution sous Node.js, le même runtime que Workers (workerd) est lancé dans un processus séparé pour héberger le plugin, donc « le sandbox n'est plus limité à Cloudflare ».

Le point clé est que le code du plugin est identique dans les deux modes, ce qui signifie développer localement en mode Trusted et déployer le plugin dans le sandbox en production fonctionne naturellement.

Structure du plugin : séparation des fichiers entre la compilation et l'exécution

Un plugin EmDash est divisé en deux parties qui s'exécutent à des moments différents.

  1. descriptor (descripteur) : métadonnées incluant id, version, les Capabilities nécessaires et la définition du storage, qui sont importées depuis astro.config.mjs, donc elles s'exécutent dans Vite au moment de la compilation. Il ne doit pas avoir d'effets secondaires.
  2. Définition : le contenu des hooks ou des routes. S'exécute sur le serveur au moment de la requête.
my-plugin/
├── src/
│   ├── index.ts          # descriptor。astro.config.mjs から import される
│   └── sandbox-entry.ts  # definePlugin({ hooks, routes })。実行時にロードされる
└── package.json          # "." と "./sandbox" を exports で分ける
// src/index.ts
import type { PluginDescriptor } from "emdash";

export function myPlugin(): PluginDescriptor {
	return {
		id: "my-plugin",
		version: "1.0.0",
		format: "standard",
		entrypoint: "@my-org/my-plugin/sandbox",
		capabilities: ["content:read", "network:request"],
		allowedHosts: ["api.example.com"],
		options: {},
	};
}
// src/sandbox-entry.ts
import { definePlugin, type PluginContext } from "emdash";

export default definePlugin({
	hooks: {
		"content:afterSave": async (event, ctx: PluginContext) => {
			ctx.log.info(`saved ${event.collection}/${event.content.id}`);
		},
	},
});

Vous écrivez le chemin du module de définition comme entrypoint côté descriptor, et EmDash le charge au moment de l'exécution. Il est tentant de tout regrouper dans le même fichier, mais puisque le code d'exécution serait inclus dans la compilation Vite, il est préférable de les séparer.

Standard et Native

Un autre axe de distinction : les plugins existent en deux formats, Standard et Native.

  • Standard : la structure décrite ci-dessus. L'interface de gestion est écrite en Block Kit (JSON) comme expliqué plus tard. Fonctionne en mode Trusted comme Sandboxed et peut être publié sur le registre.
  • Native : inclut une interface de gestion en React, des blocs personnalisés pour Portable Text (l'éditeur de contenu) et des composants Astro pour le rendu côté site. En contrepartie, ne fonctionne qu'en mode Trusted et ne peut pas être publié sur le registre.

La pratique consiste généralement à écrire en Standard, puis à utiliser Native uniquement quand vous avez besoin d'ajouter des blocs personnalisés à l'éditeur ou d'injecter des composants dans le HTML du site.

Dans yuru-log, nous avons créé des plugins Native personnalisés pour les cartes de produits Amazon et les intégrations vidéo. Les deux ajoutent un bloc dédié à l'éditeur et sont rendus par des composants Astro, ce qui est impossible en sandbox (les plugins installés au moment de l'exécution ne peuvent pas apporter les composants Astro nécessaires au moment de la compilation).

Capability : la grammaire des permissions

La Capability est au cœur du sandbox. Le plugin déclare « à quoi il souhaite accéder » au moyen de chaînes de caractères, et l'administrateur confirme et approuve dans la boîte de dialogue d'installation.

Dans la version 1.x, le vocabulaire a été **organisé sous la forme :**. Voici les plus courants.

Capability

Fonctionnalités

content:read / content:write

Lecture d'articles / Création, mise à jour et suppression

content:publish

Publication, dépublication et programmation

media:read / media:write

Lecture de médias / Téléchargement et suppression

network:request

Requêtes HTTP externes. Uniquement les hôtes déclarés dans allowedHosts

admin.editor-draft:read / patch

Lecture du contenu non enregistré en cours d'édition / Proposition de révisions

hooks.content-policy:register

Examen du contenu et blocage de la publication

Il existe également des taxonomies, commentaires, redirections, utilisateurs, envoi d'e-mails, et bien d'autres encore, pour un total de 27 types.

storage et KV ne nécessitent pas de déclaration et sont toujours disponibles. Ils sont scoped par plugin, de sorte que les données des autres plugins ne sont pas visibles.

Les noms de la version 0.x ((read:content, network:fetch, etc.) sont obsolètes mais restent fonctionnels en tant qu'alias. Le runtime les normalise au nom actuel, les plugins existants fonctionnent inchangés et un avertissement s'affiche lors du bundling.

Hooks : ce que vous pouvez surveiller

La version 1.2.0 du runtime définit 30 types de hooks. Enregistrement, publication, programmation et suppression de contenu, téléchargement de médias, envoi d'e-mails, soumission et approbation de commentaires, installation et activation de plugins, et exécution périodique via cron. C'est une augmentation considérable par rapport à la version 0.x, notamment parce que les phases avant et après la publication ((content:beforePublish / afterPublish)) sont désormais des hooks distincts.

Vous pouvez annuler l'enregistrement en utilisant content:beforeSave pour throw, c'est pourquoi les plugins de validation s'inscrivent ici. Si vous souhaitez arrêter la publication elle-même, vous devez déclarer hooks.content-policy:register et utiliser le hook antérieur à la publication.

Un changement discret mais important de la version 1.2 est que lors du premier démarrage après la mise à jour, les hooks plugin:install et plugin:activate des plugins existants sont réexécutés. Les plugins qui injectent des données initiales dans le hook install doivent être écrits de façon à ne pas se casser en cas d'injection double.

Block Kit : un tableau de bord d'administration sans JavaScript

Dans le sandbox, vous ne pouvez pas exécuter le JavaScript d'un plugin dans le navigateur. C'est pourquoi le tableau de bord d'administration est écrit en JSON avec Block Kit. Le plugin retourne un tableau de blocs tels que « en-tête, tableau, formulaire, basculeur » et c'est EmDash qui les affiche.

Voici comment cela fonctionne. Quand un utilisateur ouvre la page du plugin, EmDash envoie une requête au plugin indiquant que « la page a été ouverte », et le plugin retourne un tableau de blocs. Quand l'utilisateur soumet le formulaire, une requête contenant les valeurs saisies arrive, le plugin les enregistre et retourne un nouveau tableau de blocs. Puisque l'interface utilisateur est créée par la seule échange requête-réponse, cette conception est bien adaptée aux contraintes du sandbox.

En plus de la page du tableau de bord d'administration et du widget du tableau de bord, la version 1.x permet également de créer le panneau latéral de l'éditeur ((editorPanels)) avec Block Kit. C'est conçu pour afficher l'état et les actions spécifiques à chaque article dans l'écran d'édition. Par exemple, l'affichage de l'état de traduction, la vérification SEO et textuelle, l'inspection des liens brisés, la génération d'extraits à partir du corps du texte, et d'autres fonctionnalités ciblant « l'article en cours de rédaction » s'intègrent ici.

Registre décentralisé : une architecture sans marché centralisé

Le mécanisme de distribution des plugins a également changé dans la version 1.0. De la perspective de l'utilisateur, cela signifie simplement « rechercher dans le tableau de bord d'administration et installer en un clic », mais en arrière-plan, c'est construit sur AT Protocol, la base de Bluesky, avec une structure un peu différente.

  • Les informations du plugin se trouvent sur le compte de l'auteur. L'auteur se connecte avec un compte Bluesky, signe les informations du package et de la version, et les stocke dans l'espace de données de son compte. Ils ne sont pas confiés au serveur de l'opérateur du registre.
  • Le registre Cloudflare n'est qu'« un catalogue parmi d'autres ». plugins.emdashcms.com collecte simplement les informations publiées par chaque auteur pour les rendre consultables, et n'importe qui peut créer un autre catalogue en collectant les mêmes informations.
  • Valider le contenu lors de l'installation. EmDash vérifie que le plugin téléchargé « a le même contenu que celui signé et publié par l'auteur ». Le hash du fichier, le nom du package, la version, et les Capabilities demandées correspondent-ils aux informations signées ? Si le plugin a été construit par GitHub Actions, EmDash valide également cet enregistrement de compilation. Les plugins remplacés ou modifiés par le catalogue sont rejetés à cette étape.
  • La modération « masque » seulement. Le catalogue Cloudflare examine les descriptions, les liens et les images à l'aide d'un modèle d'IA (Clef) et ne les affiche pas au catalogue en cas de problème. Cependant, l'opérateur ne peut pas modifier le contenu du plugin ni retirer la propriété à l'auteur.

En d'autres termes, c'est un modèle où « même si l'opérateur du registre disparaît, la distribution continue si les informations restent sur le compte de l'auteur » et « l'opérateur peut masquer le catalogue, mais ne peut pas modifier ».
Il est caractérisé par le fait qu'il est conçu sans marketplace central comme npm ou WordPress.org.

Pour l'instant, seuls les plugins gratuits sont disponibles. Les plugins payants seront traités selon le même modèle décentralisé au fur et à mesure de l'évolution d'AT Protocol.

Le mécanisme de traduction semble pouvoir être implémenté comme un plugin.

Chez Yuru Log, nous mettons en place un système qui génère automatiquement une version anglaise brouillon à partir d'articles en japonais à l'aide d'un LLM (voir comment créer un blog multilingue avec EmDash). Actuellement, un script Node récupère le token API EmDash et les clés d'API de traduction depuis le fichier .env, télécharge les articles en japonais, les traduit, et crée un brouillon en anglais avec translationOf, le tout dans un flux continu.

En appliquant ceci aux spécifications de plugin 1.x, cela devient essentiellement un plugin Standard.

  • Déclencheur : Hook content:afterPublish. S'exécute quand un article en japonais est publié.
  • Lecture et écriture d'articles : content:read et content:write. Le résultat de la traduction est créé en tant que brouillon.
  • Appels à l'API de traduction : network:request et un seul hôte API dans allowedHosts.
  • Clé API : champ secret dans settingsSchema. Entrez-la depuis l'écran d'administration et elle est chiffrée. Plus besoin de .env ni d'émission de jeton API.
  • État de la traduction : stockez dans le stockage « quelle révision de l'article d'origine a été traduite », puis affichez dans l'écran d'édition d'articles via editorPanels : « version anglaise disponible / l'article d'origine a été mis à jour, re-traduction requise ».

Les scripts exécutés manuellement fonctionnent désormais simplement avec l'approbation de Capability par l'administrateur. De plus, le runtime garantit que le sandbox ne peut accéder à aucun hôte autre que l'API de traduction. Ce qui est intéressant dans ce système, c'est que le dilemme « où placer le code gérant les clés API » disparaît structurellement.

Un point à surveiller lors de la conception : chaque fois que le plugin dans le sandbox demande quelque chose à l'hôte (lecture de paramètres, récupération d'articles, écriture en stockage), cela compte comme une sous-requête, avec une limite par défaut de 10. Pour une seule traduction d'article, vous devriez être correct, mais pour les traitements du type « tous les articles non traduits en lot », boucler sur ctx.content.get() atteindra rapidement la limite. Une conception avec traitement progressif via des hooks cron sera nécessaire.

De plus, les éléments UI du site comme le changement de langue ne peuvent pas être générés depuis le sandbox, ils doivent donc être écrits du côté du thème.

Conclusion

  • Les plugins EmDash 1.0 supposent un sandbox. Déclarez les Capabilities, l'administrateur les approuve, et le runtime les applique.
  • Le même code fonctionne à la fois en mode Trusted et Sandboxed. Le développement en Trusted, la production en sandbox, c'est la base.
  • Si vous avez besoin de blocs personnalisés d'éditeur ou de composants Astro, utilisez Native (Trusted uniquement).
  • descriptor (au moment du build) et definition (à l'exécution) doivent obligatoirement être dans des fichiers distincts
  • Le nom de Capability a changé en version 1.x au format content:read. L'ancien nom fonctionne toujours comme alias, mais est déprécié
  • Le registre est décentralisé sur AT Protocol. Les catalogues peuvent être masqués mais ne peuvent pas être modifiés
  • En mode sandbox, « 1 appel hôte = 1 sous-requête », avec une limite de 10. Concevez votre système en tenant compte de ce nombre

Avec WordPress, chaque plug-in que vous installez vous oblige à accepter que « ce plug-in dispose de toutes les permissions du site ».
Si un seul plug-in contient une vulnérabilité, l'ensemble du site est exposé à des risques.

Avec EmDash 1.0, pour les plug-ins de type sandbox, les permissions disponibles sont affichées lors de l'installation, et l'exécution n'autorise que les actions approuvées.
Pour ceux qui migrent depuis WordPress à la recherche d'une solution plus sécurisée, la gestion des plug-ins peut être un argument en faveur du choix d'EmDash.

Il n'y a pas encore beaucoup de plug-ins, et nous sommes loin de l'état « il y a forcément quelque chose si on cherche » comme WordPress. Nous espérons toutefois que l'écosystème des plug-ins continuera à s'enrichir.
Depuis que nous avons déjà du retour d'expérience en exploitation sur notre propre site, nous envisageons d'ajouter cela à nos services.

Auteur de cet article

Passé du DTP au monde du web, il s'est avéré être un « sage des techniques » maîtrisant le markup, le frontend, la direction et l'accessibilité. Actif depuis la fondation de Liberogic, il est devenu une référence incontournable en interne. Récemment, il explore l'optimisation via des prompts IA, se demandant « Pourrions-nous déléguer davantage la conformité en accessibilité à l'IA ? ». Sa technologie et sa réflexion continuent d'évoluer.

Ayumu Futamata

Spécialiste en accessibilité web certifié par l'IAAP (WAS) / Ingénieur markup / Ingénieur frontend / Directeur web

Voir les articles de ce membre

Notre équipe fiable et nos capacités de réactivité font notre fierté

Chez Liberogic, nos équipes expérimentées sont reconnues pour diriger activement les projets et sont hautement appréciées par nos clients.
Nous assignons correctement un chef de projet et un directeur, et veillons à assurer le déroulement fluide de l'ensemble du projet. Nous évitons une augmentation inutile des coûts en engagements complets, en allouant les ressources de manière optimale. Notre approche est réputée pour sa rapidité dans la compréhension des besoins, la création et la soumission des devis.

* Veuillez noter que nous n'engageons pas activement de missions d'intégration type SES.

Slack, Teams, Redmine, Backlog, Asana, Jira, Notion, Google Workspace, Zoom, Webex, et pratiquement tous les principaux outils de gestion de projet et de communication que vous utilisez.

Consultez-nous pour toute question ou préoccupation concernant le web.

Études de cas