A full-stack TypeScript CMS built on Astro and Cloudflare. EmDash takes the ideas that made WordPress dominant -- extensibility, admin UX, a plugin ecosystem -- and rebuilds them on serverless, type-safe foundations. Plugins run in sandboxed Worker isolates, solving the fundamental security problem with WordPress's plugin architecture. Or deploy directly to your Cloudflare account: EmDash runs on Cloudflare (D1 + R2 + Workers) or any Node.js server with SQLite. No PHP, no separate hosting tier -- just deploy your Astro site.
A full-stack TypeScript CMS built on Astro and Cloudflare. EmDash takes the ideas that made WordPress dominant -- extensibility, admin UX, a plugin ecosystem -- and rebuilds them on serverless, type-safe foundations. Plugins run in sandboxed Worker isolates, solving the fundamental security problem with WordPress's plugin architecture.
[!IMPORTANT] EmDash depends on Dynamic Workers to run secure sandboxed plugins. Dynamic Workers are currently only available on paid accounts. Upgrade your account (starting at $5/mo) or comment out the
worker_loadersblock of yourwrangler.jsoncconfiguration file to disable plugins.
npm create emdash@latest
Or deploy directly to your Cloudflare account:
EmDash runs on Cloudflare (D1 + R2 + Workers) or any Node.js server with SQLite. No PHP, no separate hosting tier -- just deploy your Astro site.
EmDash ships with three starter templates:
BlogA classic blog with sidebar widgets, search, and RSS.
|
MarketingA conversion-focused landing page with pricing and contact form.
|
PortfolioA visual portfolio for showcasing creative work.
|
WordPress was built for a different era. Running WordPress today means managing PHP alongside JavaScript, layering caches to get acceptable performance, and knowing that 96% of WordPress security vulnerabilities come from plugins. EmDash is what WordPress would look like if you started from scratch with today's tools.
Sandboxed plugins. WordPress plugins have full access to the database, filesystem, and user data. A single vulnerable plugin can compromise the entire site. EmDash plugins run in isolated Worker sandboxes via Dynamic Worker Loaders, each with a declared capability manifest. A plugin that requests read:content and email:send can do exactly that and nothing else.
export default () =>
definePlugin({
id: "notify-on-publish",
capabilities: ["read:content", "email:send"],
hooks: {
"content:afterSave": async (event, ctx) => {
if (event.content.status !== "published") return;
await ctx.email.send({
to: "editors@example.com",
subject: `New post: ${event.content.title}`,
});
},
},
});
Structured content, not serialized HTML. WordPress stores rich text as HTML with metadata embedded in comments -- tying your content to its DOM representation. EmDash uses Portable Text, a structured JSON format that decouples content from presentation. Your content can render as a web page, a mobile app, an email, or an API response without parsing HTML.
Built for agents. EmDash ships with agent skills for building plugins and themes, a CLI that lets agents manage content and schema programmatically, and a built-in MCP server so AI tools like Claude and ChatGPT can interact with your site directly.
Runs anywhere. EmDash uses portable abstractions at every layer -- Kysely for SQL, S3 API for storage -- that work with SQLite, D1, Turso, PostgreSQL, R2, AWS S3, or local files. It runs best on Cloudflare, but it's not locked to it.
EmDash is an Astro integration. Add it to your config and you get a complete CMS: admin panel, REST API, authentication, media library, and plugin system.
// astro.config.mjs
import emdash from "emdash/astro";
import { d1 } from "emdash/db";
export default defineConfig({
integrations: [emdash({ database: d1() })],
});
Content types are defined in the database, not in code. Non-developers create and modify collections through the admin UI. Each collection gets a real SQL table with typed columns. Developers generate TypeScript types from the live schema:
npx emdash types
Query content using Astro's Live Collections -- no rebuilds, no separate API:
---
import { getEmDashCollection } from "emdash";
const { entries: posts } = await getEmDashCollection("posts");
---
{posts.map((post) => <article>{post.data.title}</article>)}
Content -- Blog posts, pages, custom content types. Rich text editing via TipTap with Portable Text storage. Revisions, drafts, scheduled publishing, full-text search (FTS5), inline visual editing.
Admin -- Full admin panel with visual schema builder, media library (drag-drop uploads via signed URLs), navigation menus, taxonomies, widgets, and a WordPress import wizard.
Auth -- Passkey-first (WebAuthn) with OAuth and magic link fallbacks. Role-based access control: Administrator, Editor, Author, Contributor.
Plugins -- definePlugin() API with lifecycle hooks, KV storage, settings, admin pages, dashboard widgets, custom block types, and API routes. Sandboxed execution on Cloudflare via Dynamic Worker Loaders.
Agents -- Skill files for AI-assisted plugin and theme development. CLI for programmatic site management. Built-in MCP server for direct AI tool integration.
WordPress migration -- Import posts, pages, media, and taxonomies from WXR exports, the WordPress REST API, or WordPress.com. Agent skills help port plugins and themes.
| Layer | Cloudflare | Also works with |
|---|---|---|
| Database | D1 | SQLite, Turso/libSQL, PostgreSQL |
| Storage | R2 | AWS S3, any S3-compatible service, local filesystem |
| Sessions | KV | Redis, file-based |
| Plugins | Worker isolates (sandboxed) | In-process (safe mode) |
EmDash is in beta preview. We welcome contributions, feedback, plugins, themes, and ideas.
npm create emdash@latest
See the documentation for guides, API reference, and plugin development.
This is a pnpm monorepo. To contribute:
git clone https://github.com/emdash-cms/emdash.git && cd emdash
pnpm install
pnpm build
Run the demo (Node.js + SQLite, no Cloudflare account needed):
pnpm --filter emdash-demo seed
pnpm --filter emdash-demo dev
Open the admin at http://localhost:4321/_emdash/admin.
pnpm test # run all tests
pnpm typecheck # type check
pnpm lint:quick # fast lint (< 1s)
pnpm format # format with oxfmt
See CONTRIBUTING.md for the full contributor guide.
packages/
core/ Astro integration, APIs, admin UI, CLI
auth/ Authentication library
blocks/ Portable Text block definitions
cloudflare/ Cloudflare adapter (D1, R2, Worker Loader)
plugins/ First-party plugins (forms, embeds, SEO, audit-log, etc.)
create-emdash/ npm create emdash scaffolding
gutenberg-to-portable-text/ WordPress block converter
templates/ Starter templates (blog, marketing, portfolio, starter, blank)
demos/ Development and example sites
docs/ Documentation site (Starlight)
“Complete the reference field: collection picker in schema editor + content picker in content editor — Current state The `reference` field type exists throughout the codebase as a recognized type, but the admin UI was ne…”
“Standard i18n/translation support for plugin UI and template strings — Problem EmDash currently has no translation infrastructure for **plugins** or **templates**. The only i18n in the system is: | Component | I18n Suppo…”
“Feature Request: Admin UI & Login Page Customization — **Type:** Feature Request **Area:** Admin UI / White-labeling **Priority:** Nice to have --- Summary The `emdash()` integration already supports `admin.logo`,…”
“First-party email provider for Cloudflare Workers (Email Sending / send_email binding) — **Problem:** Production deployments on Cloudflare Workers — the platform `@emdash-cms/cloudflare` targets — have no email provider.…”
“Media usage index — power image sitemaps, "Used in", and safe deletes — Summary EmDash currently has **no record of where a media item is used**. There is no reverse lookup from a media item to the content that reference…”
“Introducing EmDash — the spiritual successor to WordPress that solves plugin security - The Cloudflare Blog — The Cloudflare Blog”
“EmDash: A Clever WordPress Revamp, Hold The PHP - Tedium.co — Tedium.co”
“Right Architecture, Empty Ecosystem: Verdicts on EmDash, Cloudflare's WordPress Challenger - CMSWire — CMSWire”
Data
MySQL, PostgreSQL, SQLite, Redis, MongoDB, DuckDB, ClickHouse, SQL Server, Oracle, Elasticsearch, MariaDB, TiDB, OceanBase, openGauss, GaussDB, KWDB, KingBase, Vastbase, GoldenDB, Doris, SelectDB, StarRocks, Manticore Search, Redshift, DM, TDengine, XuguDB, CockroachDB, Access, HighGo, and more. Agent/JDBC-oriented profiles extend DBX to H2, Snowflake, Trino, Hive, DB2, Informix, Neo4j, Cassandra, CodeMirror 6 with SQL syntax highlighting, metadata-aware autocomplete, Cmd+Enter execution, selected SQL execution, SQL formatting, diagnostics, and 9 editor themes. Persistent query history, saved SQL snippets, tab restore, and SQL file execution keep repeat work close at hand.
Data
A real-time global intelligence dashboard that aggregates live flight tracking, CCTV networks, earthquake monitoring, conflict zone mapping, and 24/7 news feeds into a single GPU-accelerated interface. Osiris is a production-grade OSINT platform that provides situational awareness across multiple intelligence domains. Built with Next.js 16 and MapLibre GL, every data point is rendered via WebGL for 60fps performance even with thousands of concurrent entities on-screen. Open http://localhost:3000. The image is a multi-stage node:22-alpine standalone build (~220 MB, non-root). The compose file also carries CasaOS app metadata (x-casaos:) for one-click install on CasaOS. See DOCKER.md for the full Docker, CasaOS and API-key guide.
Data
AI skills and agents that make each unit of engineering work easier than the last. Each unit of engineering work should make subsequent units easier -- not harder. Traditional development accumulates technical debt. Every feature adds complexity. Every bug fix leaves behind a little more local knowledge that someone has to rediscover later. The codebase gets larger, the context gets harder to hold, and the next change becomes slower.