{"id":98045,"date":"2026-10-08T12:38:00","date_gmt":"2026-10-08T16:38:00","guid":{"rendered":"https:\/\/overcentral.com\/en\/?p=98045"},"modified":"2026-09-29T07:59:06","modified_gmt":"2026-09-29T11:59:06","slug":"emdash-security-model-plugin-sandbox-98045","status":"publish","type":"post","link":"https:\/\/overcentral.com\/en\/emdash-security-model-plugin-sandbox-98045\/","title":{"rendered":"EmDash\u2019s Security Model: What the Plugin Sandbox Doesn\u2019t Tell You"},"content":{"rendered":"<p>The headline is simple: EmDash sandboxes every plugin in a V8 isolate, and the admin panel uses passkey authentication. That much you already know. But the architecture makes several promises it can\u2019t fully keep, and the gaps are where real-world security lives.<\/p>\n<p>Let\u2019s start with the sandbox. Cloudflare\u2019s dynamic workers run plugin code inside an isolated V8 isolate. The plugin declares its capabilities in a manifest \u2014 read content, send email \u2014 and literally cannot do anything else. No file system access, no database queries, no unrestricted network calls. That\u2019s a structural improvement over WordPress, where every plugin runs in the same process and has full access to <code>wpdb<\/code>code.<\/p>\n<p>But here\u2019s the non-obvious part: the sandbox only applies when the plugin runs on Cloudflare\u2019s paid Workers plan. Self-host EmDash on a Node.js server, and plugins execute in-process \u2014 no isolation at all. The free Cloudflare tier also runs plugins in-process. The feature that defines EmDash\u2019s security model is a paid, vendor-specific add-on. If you deploy on a standard VPS, you get the same risk surface as WordPress, minus the ecosystem.<\/p>\n<p>And even on the paid plan, the sandbox is not a cure-all. A plugin that declares <code>read content<\/code>code and <code>send email<\/code>code can still be buggy or malicious within those bounds. It can send spam, exfiltrate content via email (if you grant that capability), or loop indefinitely. The sandbox prevents direct database theft, but it doesn\u2019t prevent abuse of the granted capabilities. The plugin\u2019s behavior is still opaque \u2014 you trust the manifest, not the code.<\/p>\n<h2>User Authentication: Passkeys and the Hidden Dependency<\/h2>\n<p>EmDash ships with passkey-first authentication using WebAuthn. No passwords to leak, no brute-force vectors. That\u2019s excellent. But the implementation has a catch that matters for anyone running multiple sites or staging environments.<\/p>\n<p>Passkeys are bound to the origin \u2014 the exact domain and port. On a local development server (<code>localhost:4321<\/code>code), a passkey works fine. But if you spin up a staging site on a different subdomain, or if you run multiple EmDash instances on the same machine, each one requires a separate passkey registration. There\u2019s no shared credential store across instances. For an agency managing 50 client sites, that means 50 separate passkey enrollments. The UX is worse than a password manager that auto\u2011fills.<\/p>\n<p>Worse, the passkey is stored in the browser\u2019s credential manager. If you clear browser data or switch machines, you lose access and must use the magic-link fallback. The magic link, in turn, depends on email delivery \u2014 which itself relies on a configured SMTP server. On a fresh playground instance with no email setup, the fallback fails. A user locked out of their admin has no recovery path.<\/p>\n<h2>The Real Threat: Vendor Lock\u2011In as a Security Risk<\/h2>\n<p>The most subtle security concern isn\u2019t technical \u2014 it\u2019s operational. EmDash\u2019s full security model requires Cloudflare\u2019s proprietary runtime. If you decide to move to another provider, you lose the sandbox. The data is portable (D1 is SQLite, R2 is S3\u2011compatible), but the security architecture is not. That means your migration path is either to accept a downgrade in security or to rebuild.<\/p>\n<p>Compare with WordPress: you can move from a shared host to a VPS to a managed platform, and the security model stays the same \u2014 for better or worse. With EmDash, your security posture is tied to Cloudflare\u2019s infrastructure decisions. If they change pricing, deprecate dynamic workers, or suffer a platform\u2011level breach, you have no equivalent alternative.<\/p>\n<h2>What the Sandbox Can\u2019t Prevent<\/h2>\n<p>Let\u2019s be specific. A plugin that declares <code>read content<\/code>code can still read all your posts. If that plugin has a vulnerability in its own logic \u2014 a reflected XSS in the admin interface, for instance \u2014 the sandbox doesn\u2019t protect against that. The plugin\u2019s own code runs inside the isolate, but the output it produces (e.g., rendered content) can still be malicious. The sandbox controls <em>what the plugin can do<\/em>, not <em>what it outputs<\/em>.<\/p>\n<p>Similarly, a plugin that declares <code>send email<\/code>code can be used to send phishing links to your users. The sandbox prevents <a href=\"https:\/\/overcentral.com\/en\/openai-navier-stokes-controversy-80379\/\" title=\"OpenAI Solves 90-Year-Old Math Problem, Faces Data Theft Claims\" data-iacss-internal=\"1\">data theft<\/a> but not abuse of the granted capability. The manifest system is a capability\u2011based permission model, not a full security boundary. It\u2019s analogous to Android\u2019s permissions \u2014 useful, but not a silver bullet.<\/p>\n<h2>The Practitioner\u2019s Takeaway<\/h2>\n<p>EmDash\u2019s security model is a genuine advance. The plugin sandbox eliminates the most common WordPress attack vector: a compromised plugin that reads the entire database. The passkey system removes password\u2011based attacks. But the model only works on Cloudflare\u2019s paid infrastructure, introduces operational lock\u2011in, and leaves gaps in capability abuse and output validation.<\/p>\n<p>If you\u2019re building a greenfield site and can commit to Cloudflare, EmDash offers a better baseline than WordPress. But if you value portability, autonomy, or the ability to self\u2011host with full security, you\u2019ll need to wait for the ecosystem to mature \u2014 or accept that the sandbox is a feature you can\u2019t take with you.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The headline is simple: EmDash sandboxes every plugin in a V8 isolate, and the admin panel uses passkey authentication. That much you already know. But the architecture makes several promises it can\u2019t fully keep, and the gaps are where real-world security lives. Let\u2019s start with the sandbox. Cloudflare\u2019s dynamic workers run plugin code inside an [&hellip;]<\/p>\n","protected":false},"author":7,"featured_media":99769,"comment_status":"closed","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"fifu_image_url":"https:\/\/cards.overcentral.com\/cards\/en\/98045.png","fifu_image_alt":"EmDash\u2019s Security Model: What the Plugin Sandbox Doesn\u2019t Tell You","footnotes":""},"categories":[31],"tags":[],"class_list":["post-98045","post","type-post","status-publish","format-standard","has-post-thumbnail","category-technology"],"fifu_image_url":"https:\/\/cards.overcentral.com\/cards\/en\/98045.png","fifu_image_alt":"EmDash\u2019s Security Model: What the Plugin Sandbox Doesn\u2019t Tell You","_links":{"self":[{"href":"https:\/\/overcentral.com\/en\/wp-json\/wp\/v2\/posts\/98045","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/overcentral.com\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/overcentral.com\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/overcentral.com\/en\/wp-json\/wp\/v2\/users\/7"}],"replies":[{"embeddable":true,"href":"https:\/\/overcentral.com\/en\/wp-json\/wp\/v2\/comments?post=98045"}],"version-history":[{"count":1,"href":"https:\/\/overcentral.com\/en\/wp-json\/wp\/v2\/posts\/98045\/revisions"}],"predecessor-version":[{"id":99770,"href":"https:\/\/overcentral.com\/en\/wp-json\/wp\/v2\/posts\/98045\/revisions\/99770"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/overcentral.com\/en\/wp-json\/wp\/v2\/media\/99769"}],"wp:attachment":[{"href":"https:\/\/overcentral.com\/en\/wp-json\/wp\/v2\/media?parent=98045"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/overcentral.com\/en\/wp-json\/wp\/v2\/categories?post=98045"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/overcentral.com\/en\/wp-json\/wp\/v2\/tags?post=98045"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}