Op 28 september 2026 werd EmDash 1.0, de native Astro CMS van Cloudflare, officieel uitgebracht. Daarna volgden op 2 oktober versie 1.1 en op 7 oktober versie 1.2 - drie updates in minder dan twee weken.
Hoe we ons interne blog "Yurolog" van 0.22 naar 1.2 hebben bijgewerkt, hebben we in dit artikel beschreven. Deze keer zullen we de grootste wijziging in 1.0 organiseren: "hoe plugins werken".
Wat EmDash precies is voor een CMS, kun je zien in het eerste artikel.
In het kort: "WordPress opnieuw gemaakt met Astro + Cloudflare".
Hoe zijn plugins veranderd in versie 1.0?
Plugins in de 0.x-periode waren eenvoudig gezegd "npm-pakketten geschreven in Astro's configuratiebestand", dus omdat ze in hetzelfde proces als de site draaiden, hadden plugins dezelfde machtigingen als de sitecode en konden ze net als WordPress toegang krijgen tot de database en omgevingsvariabelen.
In versie 1.0 zijn sandbox en gedistribueerd register toegevoegd.
Dit is een zeer grote verbetering.
- Plugins draaien standaard in een geïsoleerde omgeving en hebben geen toegang tot site-inhoud, media, gebruikers, geheimen of netwerk.
- Vereiste machtigingen (Capability) worden gedeclareerd door de plugin en goedgekeurd door de sitebeheerder bij installatie.
- Goedgekeurde machtigingen zijn niet alleen opgeschreven - de runtime forceert ze daadwerkelijk.
- Plugins kunnen met één klik vanuit het beheerdashboard worden geïnstalleerd. De distributie vindt plaats via een gedecentraliseerd register op AT Protocol.
De blog van Cloudflare legt dit uit in contrast met het WordPress-model waarbij "alle plugins hetzelfde proces delen".
Dit zal vooral resoneren met mensen die bekend zijn met WordPress-plugin-gerelateerde incidenten.
Plugins hebben twee verschillende "uitvoeringsmodi"
Het belangrijkste om te onthouden is dat dezelfde plugincode twee uitvoeringsmodi heeft: Trusted (vertrouwd) en Sandboxed (sandbox).
Trusted | Sandboxed | |
|---|---|---|
Registratielocatie |
|
|
Uitvoeringsomgeving | Hetzelfde proces als de site | Geïsoleerde V8 isolate |
Capability | Alleen declaratie (niet afgedwongen) | Runtime dwingt af |
Netwerk | Geen beperkingen | Geblokkeerd. Alleen hosts die in |
Gegevenstoegang | Volledige DB-toegang | Alleen uw eigen storage/KV en goedgekeurde bereiken |
Node.js API | Beschikbaar | Niet beschikbaar (alleen Web API) |
Resourcelimiet | Geen | CPU 50ms, subrequests 10, 30 seconden, ongeveer 128MB |
Geschikte gebruikssituaties | Eigen code, geverifieerde npm-pakketten | Derden, distributie via register |
"Isolatie" betekent dat voor elke plugin een kleine, toegewijde uitvoeringsomgeving wordt ingericht en apart van de hoofdsite wordt uitgevoerd.
Op Cloudflare Workers wordt dit bereikt via Dynamic Workers, wat de Worker van de plugin bij elk verzoek opstart.
Vanaf versie 1.0 wordt de plugin, zelfs wanneer deze op Node.js draait, in dezelfde runtime (workerd) geladen die op een afzonderlijk proces draait, net als op Cloudflare — dus "sandbox is niet meer beperkt tot Cloudflare".
Het voordeel is dat de plugincode in beide modi identiek is, wat betekent dat een normale workflow is: lokaal development in Trusted-modus en productie in de sandbox.
Pluginstructuur: bestanden scheiden tussen buildtijd en runtime
EmDash-plugins zijn onderverdeeld in twee onderdelen die op verschillende momenten worden uitgevoerd.
- Descriptor (beschrijver): metagegevens zoals
id,version, benodigde capabilities en storage-definities. Deze wordtastro.config.mjsgeïmporteerd, dus draait tijdens de buildtijd in Vite. Dit onderdeel mag geen bijwerkingen hebben. - Definition(definitie): de inhoud van hooks en routes. Deze worden op de server uitgevoerd wanneer een verzoek binnenkomt
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}`);
},
},
});
In de descriptor geef je het modulepath van de definition op met entrypoint, en EmDash laadt dit bij runtime. Je zou alles in hetzelfde bestand willen plaatsen, maar omdat Vite buildtijd en runtime code samen zou verwerken, is het het beste deze te scheiden.
Standard en Native
Als tweede as hebben plugins twee formaten: Standard en Native.
- Standard: de bovengenoemde structuur. De admin UI wordt beschreven in Block Kit (JSON). Het werkt in zowel Trusted als Sandboxed en kan in het register worden gepubliceerd
- Native: bevat een React admin interface, custom blokken voor de Portable Text body editor en Astro-componenten voor rendering aan de website. Het voordeel is echter dat het alleen in Trusted mode werkt en niet in het register kan worden gepubliceerd
Je schrijft over het algemeen in Standard, en je schakelt alleen naar Native als je custom blokken in de editor wilt toevoegen of componenten in de website HTML wilt injecteren.
Op Yururog hebben we Amazon-productkaarten en video-inbeddingen als Native plugins zelf gemaakt. Met beide voeg je speciale blokken in de editor toe en render je ze met Astro-componenten – dit is in principe niet mogelijk in de sandbox (omdat plugins die bij runtime worden geïnstalleerd, niet de Astro-componenten kunnen meebrengen die bij buildtijd nodig zijn).
Capability: de taalgebruik voor machtigingen
De kern van de sandbox is Capability. Een plugin declareert "wat het wil openen" als een string, en de beheerder bevestigt en keurt dit goed in het installatiedialoogvenster.
In 1.x is de vocabulaire : georganiseerd. Hier zijn enkele veel gebruikte voorbeelden.
Capability | Wat het kan doen |
|---|---|
| Artikelen lezen / maken, bijwerken en verwijderen |
| Publiceren, verbergen, plannen |
| Media lezen / uploaden en verwijderen |
| HTTP-verzoeken naar extern. Alleen de hosts in |
| Niet-opgeslagen inhoud lezen tijdens het bewerken / wijzigingen voorstellen |
| Inhoud controleren en publicatie blokkeren |
Daarnaast zijn er taxonomieën, opmerkingen, omleidingen, gebruikers, e-mailverzending en meer — in totaal 27 soorten.
storage en KV hoeven niet te worden gedeclareerd en zijn altijd beschikbaar. Ze worden per plugin gescheiden, dus data van andere plugins is niet zichtbaar.
De oudere namen uit 0.x (read:content, network:fetch, enzovoort) zijn verouderd, maar werken nog als aliassen. De runtime normaliseert ze naar de huidige naam, dus bestaande plugins werken ongewijzigd en geven alleen een waarschuwing bij bundeling.
Hooks: waarop kan je reageren
Runtime versie 1.2.0 bevat 30 verschillende hooks. Deze omvatten het opslaan, publiceren, plannen en verwijderen van inhoud, het uploaden van media, het verzenden van e-mail, het plaatsen en goedkeuren van opmerkingen, het installeren en activeren van plugins, en periodieke uitvoeringen via cron. Dit is aanzienlijk meer dan in versie 0.x, vooral omdat het moment voor en na publicatie (content:beforePublish / afterPublish) nu in aparte hooks zijn opgesplitst.
Je kunt opslaan annuleren door content:beforeSave te gebruiken met throw, dus validatieplug-ins gaan hier. Als je publicatie zelf wilt blokkeren, declareer je hooks.content-policy:register en gebruik je de hook vóór publicatie.
Een subtiel maar belangrijk verschil in versie 1.2 is dat bij de eerste start na upgraden de bestaande plug-in plugin:install en plugin:activate opnieuw worden uitgevoerd. Plug-ins die initialisatiegegevens injecteren via de install-hook moeten zo worden geschreven dat dubbele injectie geen problemen veroorzaakt.
Block Kit: beheerscherm zonder JavaScript
In de sandbox kun je JavaScript van plug-ins niet in de browser uitvoeren. Het beheerscherm wordt daarom beschreven in Block Kit, een JSON-formaat. Plug-ins retourneren een array van blokken zoals "header, tabel, formulier, toggle", en de EmDash-zijde rendert die.
Hier is de volgorde: wanneer een gebruiker de plug-inpagina opent, stuurt EmDash een verzoek naar de plug-in dat de pagina is geopend, en de plug-in retourneert een array van blokken. Wanneer de gebruiker een formulier verzendt, komt er een verzoek met invoerwaarden binnen, en de plug-in slaat deze op en retourneert een nieuwe blokarray. Omdat UI alleen uit request-response bestaat, is dit ontwerp goed geschikt voor sandboxbeperkingen.
Naast beheerschermpagina's en dashboardwidgets kun je in versie 1.x ook panelen naast de editor (editorPanels) met Block Kit maken. Dit is bedoeld voor statussen en acties per artikel die in het bewerkingsscherm moeten verschijnen. Bijvoorbeeld: vertalingsstatus weergeven, SEO- en tekstcontrole, linkcontrole, samenvatting uit de hoofdtekst genereren — functies gericht op "het artikel dat je nu schrijft" passen hier.
Gedistribueerde registry: geen centraal marktplaatsontwerp
De plug-indistributie is ook veranderd in versie 1.0. Wat gebruikers zien is "zoeken in het beheerscherm en met één klik installeren", maar achter de schermen is het gebouwd op AT Protocol, de basis van Bluesky, en heeft het een vrij ongebruikelijke structuur.
- De plugininformatie bevindt zich in het account van de auteur. De auteur logt in via een Bluesky-account, ondertekent de gegevens van het pakket en de release, en plaatst deze in de gegevensruimte van zijn eigen account. Het wordt niet opgeslagen op de servers van de registerbeheerder.
- Het Cloudflare-register is slechts "een van de catalogi".
plugins.emdashcms.comverzamelt alleen informatie die door auteurs is gepubliceerd en maakt deze doorzoekbaar. Iedereen kan dezelfde informatie verzamelen en een andere catalogus maken. - Verifieer de inhoud tijdens installatie. EmDash controleert of de gedownloade plugin dezelfde inhoud heeft als wat door de auteur is ondertekend en gepubliceerd. Het controleert of de bestandshash, pakketnaam en versie, de aangevraagde Capabilities en (indien gebouwd met GitHub Actions) de buildrecord overeenkomen met de ondertekende gegevens. Vervangen of gewijzigde versies van de catalogus worden hier geweigerd.
- Moderatie "verbergt" alleen. De Cloudflare-catalogus beoordeelt beschrijvingen, links en afbeeldingen met behulp van een AI-model (Clef). Bij problemen worden deze niet in de catalogus weergegeven. De inhoud van de plugin wordt echter niet gewijzigd, en eigenaarschap wordt niet aan auteurs ontnomen.
Met andere woorden: "Als de registerbeheerder verdwijnt, blijft distributie mogelijk zolang de informatie in het account van de auteur aanwezig is" en "De beheerder kan uit de catalogus verbergen, maar kan niet wijzigen".
Het kenmerk is dat het is ontworpen zonder de aanname van een centrale marktplaats zoals npm of WordPress.org.
Op dit moment zijn alleen gratis plugins beschikbaar. Voor betaalde plugins is het plan om dezelfde gedistribueerde aanpak te volgen terwijl we de ontwikkelingen in AT Protocol volgen.
De vertaalmechanisme lijkt geschikt voor plugins.
Op Yururog voeren we een systeem uit dat conceptversies in het Engels van Japanse artikelen genereert met behulp van LLM ("Een verhaal over het maken van meertalige blogs met EmDash"). Momenteel gebruikt een Node-script API-tokens van EmDash en sleutels van vertaal-API's uit .env, haalt Japanse artikelen op, vertaalt ze, en maakt conceptversies in het Engels met translationOf.
Wanneer dit wordt toegepast op de pluginspecificatie van versie 1.x, wordt het vrijwel direct een Standard-plugin.
- Trigger:
content:afterPublishhook. Wordt geactiveerd wanneer een Japans artikel wordt gepubliceerd. - Artikelen lezen en schrijven:
content:readencontent:write. Vertaalresultaten worden als concept aangemaakt. - Aanroepen van vertaal-API:
network:requesten één API-host toevoegen aanallowedHosts. - API-sleutel:
settingsSchema-veldsecret. Ingevoerd via het beheerdashboard en versleuteld opgeslagen..enven API-tokenuitgifte zijn niet meer nodig. - Vertaalstatus: Sla op in opslag welke revisie van het originele artikel is vertaald, en toon in het artikel-bewerkingsscherm via
editorPanels'Engelse versie beschikbaar / Originele artikel bijgewerkt - hertaling vereist'.
Scripts die voorheen handmatig werden uitgevoerd, werken nu gewoon nadat een beheerder de Capability goedkeurt. Bovendien garandeert de runtime dat deze geen verbinding maakt met hosts buiten de vertaal-API. Het voordeel van dit systeem is dat het structurele zorgen over 'waar plaats ik code die API-sleutels afhandelt' verdwijnen.
Een belangrijk ontwerpaandachtspunt: telkens wanneer de sandboxplugin iets aan de host vraagt (instellingen lezen, artikelen ophalen, naar opslag schrijven), telt dit als één subaanvraag met een standaardlimiet van 10. Voor het vertalen van één artikel past dit waarschijnlijk, maar voor processen zoals 'meerdere niet-vertaalde artikelen tegelijk', zit je snel aan de limiet als je ctx.content.get() in een lus aanroept. Een ontwerp met verwerking in stappen via cronhooks is waarschijnlijk nodig.
Bovendien kan UI-logica zoals taalswitching niet vanuit de sandbox geactiveerd worden - dat moet aan de themakant gebeuren.
Samenvatting
- EmDash 1.0-plugins gaan uit van sandbox. Je declareert Capabilities, de beheerder keurt goed, en de runtime handhaaft dit af.
- Dezelfde code draait in zowel Trusted als Sandboxed. Ontwikkeling doorgaans in Trusted, productie in sandbox.
- Voor aangepaste editorblokken of Astro-componenten is Native (alleen Trusted) nodig.
- descriptor (bij build) en definition (bij runtime) moeten altijd in afzonderlijke bestanden staan
- De naam van Capability is in 1.x gewijzigd naar
content:read-indeling. De oude naam werkt via aliasing maar is niet meer aanbevolen - Het register is gedecentraliseerd op AT Protocol. Catalogi kunnen verborgen zijn, maar kunnen niet worden vervalst
- In de sandbox geldt: 'één host-aanroep = één subaanvraag' met een limiet van 10. Ontwerp met dit aantal aanvragen in gedachte
Met WordPress accepteer je elke keer dat je een plugin installeert dat 'deze plugin volledige rechten over jouw site heeft'.
Als er één kwetsbare plugin is, wordt jouw hele site blootgesteld aan risico's.
Bij EmDash 1.0 toont de runtime bij sandboxed plugins de machtigingen die een plugin kan gebruiken op het moment van installatie en staat de runtime alleen de goedgekeurde bereik toe.
Voor mensen die van WordPress migreren, kan de veiligheid van plugins een van de redenen zijn om EmDash te kiezen.
Er zijn nog niet veel plugins beschikbaar en we zijn ver verwijderd van het WordPress-scenario waarin 'je altijd iets kunt vinden'. We hopen dat het aantal plugins vanaf hier gaat groeien.
Aangezien we nu ook operationele ervaring hebben opgedaan met onze eigen site, willen we dit toevoegen aan onze diensten.
Vanuit DTP de wereld van het web in gestapt en merkte al snel dat hij markering, frontend, directie en accessibility allemaal beheerst — een echte 'meester van techniek'. Sinds de oprichting van Liberogic een multitalent en inmiddels een levend naslagwerk in het bedrijf. Tegenwoordig is hij geïnteresseerd in de vraag "Kunnen we accessibility-implementatie meer aan AI overlaten?" en experimenteert hij graag met efficiëntie via prompts. Zowel technisch als mentaal nog volop in ontwikkeling.
Ayumi Futamata
IAAP-gecertificeerd webtoegankelijkheidsspecialist (WAS) / Opmaakingenieur / Frontend-ingenieur / Webdirecteur