Atlas / Skills / emdash-cms / Creating Plugins

Creating PluginsCAUTION

skills/emdash-cms/creating-plugins

EmDash is a full-stack TypeScript CMS based on Astro; the spiritual successor to WordPress

Verdict
CAUTION
Grade
B
Trust score
89 /100
Version
—
Hosts
1 documented
License
MIT
Stars
13,442
01

Overview

EmDash is a full-stack TypeScript CMS based on Astro; the spiritual successor to WordPress

Read from source at commit 2e1ca292efe5OBSERVED · 2026-10-07
02

Host compatibility

What the documentation claims. We have not run a compatibility test.

HostStatusNotes
cursormentioned
03

What it tells the agent

The instruction file, verbatim from the audited commit — this is the text the model reads, and the surface the audit's instruction layer examines. Quoted here so you can judge it without cloning anything.

---
name: creating-plugins
description: Create EmDash CMS plugins with sandboxed hooks, routes, storage, content and media APIs, MCP tools, and declarative admin UI, or native React and Astro extensions. Use when scaffolding or implementing an EmDash plugin.
---

# Creating EmDash plugins

Build against the API that reaches the intended execution mode. Source types and production-boundary tests take precedence over examples in this skill when they disagree.

## Choose a format

| Format    | Runtime source                                      | Admin UI                                                  | Distribution                 |
| --------- | --------------------------------------------------- | --------------------------------------------------------- | ---------------------------- |
| Sandboxed | `src/plugin.ts` default-exports a `SandboxedPlugin` | Block Kit pages, widgets, saved-entry panels, and actions | Plugin CLI and registry      |
| Native    | `definePlugin()` / `createPlugin()`                 | React, Block Kit, and Astro components                    | Trusted site dependency only |

Use a sandboxed plugin unless the feature needs host-process access, React admin code, Astro rendering components, raw page fragments, or custom Portable Text block definitions. Native plugins run with the site's authority and cannot be installed from the registry.

## Scaffold a sandboxed plugin

```sh
pnpm dlx @emdash-cms/plugin-cli init my-plugin
cd my-plugin
pnpm install
pnpm run test
```

The manifest is the identity and trust contract. Runtime hooks and routes live in `src/plugin.ts`; the CLI generates descriptors, manifests, and bundles. Do not create a separate descriptor factory or `sandbox-entry.ts`.

```typescript title="src/plugin.ts"
import type { SandboxedPlugin } from "emdash/plugin";

const plugin: SandboxedPlugin = {
	hooks: {
		"content:afterSave": async (event, ctx) => {
			ctx.log.info("Content saved", { id: event.content.id });
		},
	},
};

export default plugin;
```

Import authoring types from `emdash/plugin` with `import type`. Value imports are limited to lightweight helpers such as `pluginRoute()` and `pluginResponse()`, which the CLI bundles. Sandboxed runtime code can use Web APIs but not Node.js built-ins.

## Declare access

Declare every host API in `emdash-plugin.jsonc`. Adding authority, exposing a route publicly, or adding MCP tools requires renewed administrator approval.

| Capability                       | Grants                                                                     |
| -------------------------------- | -------------------------------------------------------------------------- |
| `schema:read`                    | Public collection and field definitions                                    |
| `admin.editor-draft:read`        | Selected unsaved field values after an explicit editor interaction         |
| `admin.editor-draft:patch`       | Host-validated unsaved field changes proposed for editor review            |
| `content:read`                   | Content identity, translations, and published public URLs                  |
| `content:revisions:read`         | Retained revision data; implies content read                               |
| `content:write`                  | Create, update, delete, and translation creation; implies read             |
| `content:publish`                | Revision-fenced publish, unpublish, schedule, and unschedule; implies read |
| `content:restore`                | Revision-fenced reads and restoration of trashed content                   |
| `hooks.content-policy:register`  | Pre-publish, pre-schedule, and pre-unpublish policy hooks                  |
| `taxonomies:read`                | Taxonomy definitions, terms, and entry assignments                         |
| `taxonomies:write`               | Term creation and assignment deltas; implies read                          |
| `bylines:read`                   | Public byline profiles and single or batched entry credits                 |
| `redirects:read`                 | Versioned redirect inspection                                              |
| `redirects:write`                | Versioned redirect creation, update, and deletion; implies read            |
| `comments:read`                  | Stored non-trashed comments and their personal data                        |
| `comments:moderate`              | Expected-status moderation; implies read                                   |
| `media:read`                     | Ready-media metadata and authenticated asset URLs                          |
| `media:bytes:read`               | Bounded media bytes and content hashes                                     |
| `media:metadata:write`           | Alt text, caption, and focal-point updates                                 |
| `media:write`                    | Upload and delete; implies media read                                      |
| `network:request`                | `ctx.http.fetch()` restricted to `allowedHosts`                            |
| `network:request:unrestricted`   | `ctx.http.fetch()` without a host list                                     |
| `users:read`                     | User directory lookup; also required by comment hooks                      |
| `email:send`                     | Email delivery when a transport is configured                              |
| `hooks.email-transport:register` | Exclusive `email:deliver` hook                                             |
| `hooks.email-events:register`    | Email before/after hooks                                                   |
| `hooks.page-fragments:register`  | Trusted-only page fragments; excluded from sandbox registration            |

Settings, KV, declared storage, logging, and cron scheduling are plugin-scoped and need no capability. Use `ctx.settings` for user configuration, `ctx.kv` for internal key-value state, and declared `ctx.storage.<collection>` for queryable recor
04

Trust audit

CAUTIONgrade B · trust 89/100 Install with care. The audit found things worth knowing before you trust its output.

LayerWhat it checksResult
L0Provenance & inventoryWARN
L1Static analysis of the codeNA
L2Instruction surface (what it tells the agent)WARN
L3Class-specific surfacePASS
L4Behavioural (sandbox)SKIPPED

What the source does

Filesystem
none-observed
Network
none-observed
Shell
none-observed
Dependencies
pinned
Secrets in source
none-found

Findings (6)

MEDIUMInventory / provenance · inv.symlink · CWE-1104
.agents/skills
.agents/skills
Why it matters. link not followed
MEDIUMInventory / provenance · inv.symlink · CWE-1104
.claude/skills
.claude/skills
Why it matters. link not followed
MEDIUMInventory / provenance · inv.symlink · CWE-1104
templates/blank/.claude/skills
templates/blank/.claude/skills
Why it matters. link not followed
MEDIUMInventory / provenance · inv.symlink · CWE-1104
templates/blog-cloudflare/.claude/skills
templates/blog-cloudflare/.claude/skills
Why it matters. link not followed
MEDIUMPrompt injection · prompt.hidden_style · CWE-94, CWE-1427
references/hooks.md:535
html: '<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-XXXXX" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>',
LOWInventory / provenance · inv.symlink · CWE-1104
.claude/CLAUDE.md
.claude/CLAUDE.md
Why it matters. link not followed

Gates applied: no_behavioural_pass.

Audited 2026-10-07 · audit v0.4.1 · source sha 2e1ca292efe5full audit observations/trust-audit/skill/emdash-cms__creating-plugins.json · Report an issue / request a re-scan
05

Audit history

Every audit this skill has had.

DateSourceVerdictGradeScoreChange
2026-10-072e1ca292efe5CAUTIONB89first audit
06

Questions

What does the Creating Plugins skill do?

EmDash is a full-stack TypeScript CMS based on Astro; the spiritual successor to WordPress

Is Creating Plugins safe to install?

With care. The audit graded it B (89/100) and found 6 things worth knowing before you trust this skill, listed below with the exact line each was found on.

What can Creating Plugins access on my machine?

The audit observed no filesystem, network or shell use at all in its source.

Which assistants does Creating Plugins work with?

Its documentation mentions cursor. That is what the text claims, not a compatibility test we ran.

How current is this page?

The grade is for one exact copy of the source (2e1ca292efe5), read on 2026-10-07. The repository is watched, and a new audit runs when it changes — this is the first audit.

Advertisement