Every EmDash tutorial tells you the same thing: user roles are built in. Administrators, editors, authors, contributors. Pick one, assign it, done. That advice works for a simple blog. It fails the moment you have multiple content types, external contributors, or compliance requirements. The default roles are a starting point, not a solution. Here’s what they cover, where they break, and how to build the granular RBAC your workflow actually needs.
The Common Advice: Just Use the Default Roles
Cloudflare markets EmDash as a spiritual successor to WordPress, and the user management mirrors that legacy. Out of the box, you get four roles: administrator (full access), editor (publish and manage others’ content), author (publish own content), and contributor (write but cannot publish). The setup wizard asks for your email, creates a passkey, and assigns you the administrator role. From there, you can invite users and assign one of these four roles.
The default roles are a starting point, not a solution.
For a personal blog or a small marketing site, this works. But EmDash is built on Astro, TypeScript, and a plugin sandbox—technologies that attract teams building custom content types, multi-author publications, and agent-driven workflows. The default role system was designed for 2003-era WordPress. It hasn’t evolved for structured content, custom post types, or the granular access control that modern publishing demands.
What the Default Roles Actually Cover
Let’s look at the capabilities each role grants, based on EmDash’s current implementation:
- Administrator: Can do everything—manage users, plugins, settings, all content types, and system configurations. No scope limits.
- Editor: Can publish, edit, and delete any post or page. Can moderate comments. Cannot manage users, plugins, or system settings.
- Author: Can publish, edit, and delete their own posts. Cannot touch pages or other authors’ content.
- Contributor: Can write and edit their own posts but cannot publish. Must submit for review.
These roles map to WordPress’s classic taxonomy. They assume a single blog with posts and pages. But EmDash supports custom content types—projects, portfolios, products, any entity you define. The default roles don’t distinguish between content types. An editor can edit every project page, even if they should only manage blog posts. An author can create a custom post type they’ve never seen before.
The problem is architectural, not accidental. EmDash’s content types are defined in the database, not in code. When you create a new content type via the admin panel, it’s immediately available to all roles. There’s no field-level permission, no per-type role mapping. The four roles are all-or-nothing across every content type.
The Unspoken Problem: Granularity and Context
In real-world publishing, access control rarely fits four buckets. Consider a scenario: a media company with a news site, a lifestyle section, and a podcast hub. Each section has its own editors. A reporter should only submit to their section. A freelancer should only write for one content type. An external reviewer should see draft posts but not published ones.
EmDash’s default roles cannot model this. An editor can edit anything. A contributor can submit to any content type. There’s no concept of “editor for projects only” or “author for posts but not pages.” If you need to restrict a user to a single content type or a specific category, the default system offers no knob to turn.
This is where most teams hit a wall. They either over-privilege users (giving editor access when read-only would suffice) or they hack around the system—creating separate EmDash instances for each section, which defeats the purpose of a unified CMS.
How to Implement Custom RBAC in EmDash
EmDash doesn’t ship with a custom role editor. But it does provide the building blocks for one: plugins, capabilities, and the MCP server. Here’s how to extend RBAC beyond the defaults.
1. Use Plugin Capabilities for Action-Level Control
Every plugin in EmDash declares what it can do in a capability manifest. A plugin that sends email declares email:sendcodecodecode. A plugin that reads content declares content:readcodecodecode. This same model can be used to scope user actions. Instead of a monolithic “editor” role, you can create plugins that gate specific operations—for example, a plugin that only allows publishing to a specific content type.
The plugin runs in a sandboxed dynamic worker. It cannot access the database or file system unless you explicitly grant that capability. By writing a small plugin that checks the user’s role and the content type before allowing an action, you effectively create a custom permission.
Example: A “Project Editor” plugin that checks if the current user has the custom role “project_editor” and the content type is “projects.” If both conditions are met, the plugin allows publishing. Otherwise, it returns a forbidden error.
2. Leverage the MCP Server for Agent-Based Access
EmDash includes a built-in MCP server that AI agents can use to interact with the CMS. This opens a second path for custom RBAC. You can define agent skills that enforce access policies. For example, an agent skill that says “only allow content:read for users with role ‘reviewer’ on content type ‘draft.'”
The MCP server is native to EmDash—not bolted on. It provides structured endpoints for content management. By combining agent skills with the capabilities system, you can build a permission layer that the default roles never anticipated.
3. Extend User Metadata via the API
EmDash exposes API tokens with granular permissions. You can generate a token that only allows content:readcodecodecode and content:writecodecodecode but not content:deletecodecodecode. This is the closest thing to custom roles in the current version. For each user, you could generate a token scoped to their specific needs and store it as user metadata. Then, in your frontend or plugin, you use that token for authentication instead of the user’s role.
This approach is manual and requires backend work, but it works today. It also aligns with EmDash’s security model: every action is scoped by explicit permission, not by a broad role label.
A Real-World Example: Multi-Content-Type Publishing
Let’s apply this to a concrete case. A digital magazine runs on EmDash with three content types: Articles, Podcasts, and Sponsored Posts. They have five editors: two for articles, one for podcasts, two for sponsored posts. They also have a compliance officer who must approve all sponsored content before publishing.
With default roles, every editor gets “editor” access—they can all edit any content type. The compliance officer would need “editor” access to see drafts, but that also lets them publish—a conflict.
The fix: Write a small plugin that intercepts the “publish” action. It checks the content type and the user’s custom metadata field editor_sectioncodecodecode. If the user’s section matches the content type, the action proceeds. If not, it blocks. For the compliance officer, create a custom role “reviewer” via a plugin that grants content:readcodecodecode and content:writecodecodecode but not content:publishcodecodecode. The plugin checks for the “reviewer” role and only allows editing drafts, not publishing.
This setup uses three pieces: the capabilities manifest, a dynamic worker for the plugin, and the MCP server to manage the custom metadata. It’s more work than clicking a dropdown, but it solves the problem that default roles cannot.
The Trade-Offs: Complexity vs. Security
Custom RBAC in EmDash is not for everyone. It requires TypeScript knowledge, comfort with the plugin sandbox, and an understanding of the MCP protocol. The default roles exist for a reason: they are simple, predictable, and cover the 80% use case.
But EmDash’s selling point is security. The plugin sandbox is designed to eliminate the attack surface that makes WordPress vulnerable. If you accept the default roles as sufficient, you are also accepting that any editor-level user has unrestricted access to every content type. In a multi-tenant or compliance-sensitive environment, that’s a security gap.
The trade-off is between convenience and control. For a single-author blog, default roles are fine. For any scenario with multiple content types, external contributors, or audit requirements, you need to build custom RBAC. The architecture supports it—but the documentation doesn’t yet spell it out.
The Missing Piece: Auditing and Future-Proofing
One thing the default roles cannot provide is audit trails tied to specific actions. Even with custom plugins, EmDash’s current logging is limited to revisions per content item. There’s no built-in way to see who changed a permission, when, and why. For organizations that need SOC 2 or GDPR compliance, this is a blocker.
The solution will likely come from the plugin ecosystem. As developers build audit plugins that hook into EmDash’s event system (the same hooks that plugins use), granular RBAC will become more manageable. For now, if you need audit logs, you must build them yourself—or wait for the community to deliver.
That’s the real takeaway. EmDash’s default roles are a starting point, not a finished system. The architecture is capable of far more. But until the ecosystem matures, implementing custom RBAC requires hands-on development. If you’re evaluating EmDash for a production site, budget that effort into your timeline.
- What are the default roles in EmDash CMS?The default roles are administrator, editor, author, and contributor. They mirror WordPress's classic taxonomy.
- Why are default roles insufficient for modern publishing?They don't distinguish between custom content types, so an editor can edit every project page even if they should only manage blog posts.
- How can you fix the role granularity issue in EmDash?You need to build custom RBAC using EmDash's plugin sandbox and event system, as the architecture supports it but documentation doesn't yet spell it out.