Your agent drafts the post. It can't press publish. EmDash CMS, torn down
Every agent-writes-your-content setup I've seen runs on the same dirty secret: the agent logs in as an admin. Full publish rights, full delete rights, the same shared WordPress password that's been sitting in the team vault since 2022. The skill file says "draft only." The credentials say "do whatever you want." When those two disagree, the credentials win. Every time.
In the anatomy lesson over in Where do I start?, part three of a governed skill was a scoped tool, and I said your existing permission system does the enforcing. In most CMSs that scoped tool is something you have to build yourself. This week I read the site, the repo, and the docs of one that ships it.
EmDash is an open source CMS (MIT license, 11k GitHub stars) built on Astro and Cloudflare, currently in beta preview. The pitch is "built for humans and agents": your team gets a normal admin panel, and the CMS ships a built-in MCP server so a coding agent connects to your content the same way your team connects to Slack. WordPress co-founder Matt Mullenweg's quote on their homepage: "The entire functionality of WordPress, and then some." When the person who built WordPress says that about a thing that isn't WordPress, I read the docs. Disclosure: no affiliation, nobody paid for this, and this is a docs-and-repo teardown, not a battle report from production.
Why it matters to this circle: it's the first CMS I've seen that ships parts three, four, and five of the anatomy off the shelf.
- The scoped tool, native. Access is permission scopes (content:read, content:write, media:write, and so on) crossed with roles: a Contributor drafts but cannot publish, an Author publishes their own, an Editor publishes anyone's, and only an Admin touches the schema. Whichever is stricter wins. Give the agent a Contributor login and it structurally cannot press publish. Not "prompted not to." Cannot.
- The gate, native. New content lands as a draft by default, and there's a built-in compare tool that shows the exact diff between the draft and the live version before anything ships. The reviewer sees the actual change, not a summary. That's the gate design from the anatomy post, shipped as a feature.
- The log, native. Revision history per item, restore to any prior version, and conflict detection so two writers (human or agent) can't silently overwrite each other.
Setting it up, least-privilege version:
- Poke it without installing anything: there's a hosted playground (linked at the end). Locally it's one command,
npm create emdash@latest, and the admin runs at /_emdash/admin. Coming from WordPress, there's an import wizard that takes your WXR export, the REST API, or WordPress.com directly: posts, pages, media, taxonomies. - Create the agent its own user. Never your login. Give it the lowest role that does the job: Contributor for a draft-only writer.
- In Claude: Settings, then Connectors, then Add custom connector. Paste your site's MCP address, which is your domain plus /_emdash/api/mcp. Authenticate in the browser.
- The step everyone will skip: on the consent screen, every permission the client asked for arrives pre-selected. Uncheck everything the workflow doesn't need. A changelog writer needs content read and write. It does not need schema, settings, or menus. Fewer boxes, smaller blast radius.
- Run the loop from the anatomy post: the agent drafts, the reviewer asks for the draft-versus-live diff, a human publishes from the admin panel. The publish button stays with people.
- Check the revision history after the first week. That's your log, and reading it is the fifteen-minute monthly habit that keeps the workflow audited.
Where it still leaks, because nothing doesn't:
- It's a beta preview. Playground yes, side project yes. Moving the company blog this week, no. Watch it.
- It's a migration, not a plugin. This is Astro on Cloudflare (or Node), not a thing you bolt onto your existing WordPress hosting. The import wizard shortens the road; it doesn't remove it. And the plugin ecosystem is young: a registry exists, WordPress's twenty years of plugins don't transfer.
- Plugins need paid Cloudflare Workers (from $5 a month) or you switch them off. Small, but it's the kind of line item people discover in week three.
- It governs your content site, not your CRM. The kickoff-email workflow from the anatomy post still needs its gate on the CRM side. This covers the blog, the changelog, the docs, the microsites.
The links: the site, the repo, the agent setup docs, and the playground.
Now the honest part, and this is the thread I actually want: how does your agent connect to your CMS today? Admin password? A full-access API key someone minted in 2024? A plugin nobody's audited? Post your setup, no judgment, and I'll map the least-privilege version for it, whether that's EmDash or the CMS you already run. And if you think a shared admin login is fine for an agent, say that too. I'll take that fight.