Skip to content

fix(core): populate route/hook metadata for config-declared sandboxed plugins#2214

Open
marcusbellamyshaw-cell wants to merge 1 commit into
emdash-cms:mainfrom
Emdash-Bug-Testing:fix/2078-sandboxed-config-plugin-route-meta
Open

fix(core): populate route/hook metadata for config-declared sandboxed plugins#2214
marcusbellamyshaw-cell wants to merge 1 commit into
emdash-cms:mainfrom
Emdash-Bug-Testing:fix/2078-sandboxed-config-plugin-route-meta

Conversation

@marcusbellamyshaw-cell

Copy link
Copy Markdown
Contributor

What does this PR do?

A plugin registered via sandboxed: [somePlugin()] in astro.config.mjs never gets its route auth metadata populated, so every non-admin route falls back to { public: false } regardless of what the plugin's own definePlugin({ routes }) declares — even a route explicitly marked public: true 401s.

Root cause: the manifest built for config-declared sandboxed plugins (EmDashRuntime.loadSandboxedPlugins) hardcoded routes: [] and hooks: [], unlike marketplace/registry installs, which read this from a downloaded bundle's manifest.json. Config-declared plugins have no such bundle to read from at build time.

Fix: add routes/hooks fields to PluginDescriptor (the object a plugin's factory function returns), mirroring how adminPages/capabilities/storage are already manually declared there. Thread them through generateSandboxedPluginsModuleSandboxedPluginEntry → the manifest built in loadSandboxedPlugins, and populate sandboxedRouteMetaCache the same way the marketplace path already does.

This is additive — a plugin descriptor that doesn't declare routes/hooks behaves exactly as before.

Closes #2078

Type of change

  • Bug fix

Checklist

  • I have read CONTRIBUTING.md
  • pnpm typecheck passes
  • pnpm lint passes
  • pnpm test passes (or targeted tests for my change)
  • pnpm format has been run
  • I have added/updated tests for my changes (if applicable)
  • User-visible strings in the admin UI are wrapped for translation (if applicable) — n/a, no admin UI strings changed
  • I have added a changeset (if this PR changes a published package)
  • New features link to an approved Discussion — n/a, bug fix

AI-generated code disclosure

  • This PR includes AI-generated code — model/tool: Claude Sonnet 5

… plugins

A `sandboxed: []` plugin's routes always fell through to the sandbox's
`{ public: false }` fallback, since the descriptor built at astro.config
resolution time never carried route/hook metadata the way marketplace
bundle manifests do. Add `routes`/`hooks` to `PluginDescriptor` and thread
them through to `sandboxedRouteMetaCache`, mirroring the existing
marketplace-install path.

Fixes emdash-cms#2078.
@changeset-bot

changeset-bot Bot commented Jul 23, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: f44372f

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 17 packages
Name Type
emdash Patch
@emdash-cms/cloudflare Patch
@emdash-cms/sandbox-workerd Patch
@emdash-cms/plugin-mcp-smoke Patch
@emdash-cms/fixture-perf-site Patch
@emdash-cms/perf-demo-site Patch
@emdash-cms/cache-demo-site Patch
@emdash-cms/do-demo-site Patch
@emdash-cms/do-solo-demo-site Patch
@emdash-cms/admin Patch
@emdash-cms/auth Patch
@emdash-cms/blocks Patch
@emdash-cms/gutenberg-to-portable-text Patch
@emdash-cms/x402 Patch
create-emdash Patch
@emdash-cms/auth-atproto Patch
@emdash-cms/plugin-embeds Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Jul 23, 2026

Copy link
Copy Markdown

Open in StackBlitz

@emdash-cms/admin

npm i https://pkg.pr.new/@emdash-cms/admin@2214

@emdash-cms/auth

npm i https://pkg.pr.new/@emdash-cms/auth@2214

@emdash-cms/auth-atproto

npm i https://pkg.pr.new/@emdash-cms/auth-atproto@2214

@emdash-cms/blocks

npm i https://pkg.pr.new/@emdash-cms/blocks@2214

@emdash-cms/cloudflare

npm i https://pkg.pr.new/@emdash-cms/cloudflare@2214

@emdash-cms/contentful-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/contentful-to-portable-text@2214

emdash

npm i https://pkg.pr.new/emdash@2214

create-emdash

npm i https://pkg.pr.new/create-emdash@2214

@emdash-cms/gutenberg-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/gutenberg-to-portable-text@2214

@emdash-cms/plugin-cli

npm i https://pkg.pr.new/@emdash-cms/plugin-cli@2214

@emdash-cms/plugin-types

npm i https://pkg.pr.new/@emdash-cms/plugin-types@2214

@emdash-cms/registry-client

npm i https://pkg.pr.new/@emdash-cms/registry-client@2214

@emdash-cms/registry-lexicons

npm i https://pkg.pr.new/@emdash-cms/registry-lexicons@2214

@emdash-cms/registry-verification

npm i https://pkg.pr.new/@emdash-cms/registry-verification@2214

@emdash-cms/sandbox-workerd

npm i https://pkg.pr.new/@emdash-cms/sandbox-workerd@2214

@emdash-cms/x402

npm i https://pkg.pr.new/@emdash-cms/x402@2214

@emdash-cms/plugin-ai-moderation

npm i https://pkg.pr.new/@emdash-cms/plugin-ai-moderation@2214

@emdash-cms/plugin-atproto

npm i https://pkg.pr.new/@emdash-cms/plugin-atproto@2214

@emdash-cms/plugin-audit-log

npm i https://pkg.pr.new/@emdash-cms/plugin-audit-log@2214

@emdash-cms/plugin-color

npm i https://pkg.pr.new/@emdash-cms/plugin-color@2214

@emdash-cms/plugin-embeds

npm i https://pkg.pr.new/@emdash-cms/plugin-embeds@2214

@emdash-cms/plugin-field-kit

npm i https://pkg.pr.new/@emdash-cms/plugin-field-kit@2214

@emdash-cms/plugin-forms

npm i https://pkg.pr.new/@emdash-cms/plugin-forms@2214

@emdash-cms/plugin-webhook-notifier

npm i https://pkg.pr.new/@emdash-cms/plugin-webhook-notifier@2214

commit: f44372f

@marcusbellamyshaw-cell
marcusbellamyshaw-cell marked this pull request as ready for review July 24, 2026 04:17
@github-actions github-actions Bot added the review/needs-review No maintainer or bot review yet label Jul 24, 2026
@marcusbellamyshaw-cell

Copy link
Copy Markdown
Contributor Author

All checks green, no scope-gate applies (bug fix, closes #2078). Marking ready for review.

@emdashbot emdashbot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The approach is sound: config-declared sandboxed plugins have no downloaded bundle manifest, so they need descriptor-level route/hook metadata just like they already declare capabilities, storage, adminPages, etc. The implementation correctly threads routes/hooks from PluginDescriptor through the virtual module, SandboxedPluginEntry, and the manifest passed to the sandbox runner, and it populates sandboxedRouteMetaCache the same way marketplace/registry installs already do. The new test targets the auth decision path and would fail before the fix.

I checked for logic bugs, type mismatches, cache invalidation gaps, logged-out query-count regressions, and security issues; I didn't find any. I also verified that the catch-all plugin route (api/plugins/[pluginId]/[...path].ts) uses getPluginRouteMeta for its auth decision, so the fix actually reaches the reported 401 behavior.

The only blocking concerns are comment-discipline violations under AGENTS.md. The new test file header references issue #2078 and narrates the bug history; the PluginDescriptor JSDoc comments use "unlike marketplace/registry installs" and "Same rationale as routes", which are exactly the kind of reviewer-facing justification/narrative the conventions prohibit. These need trimming before merge.

No code logic changes are required beyond the comments.

Comment on lines +1 to +7
/**
* A `sandboxed: []` (config-declared, non-marketplace) plugin never got its
* route auth metadata populated -- unlike marketplace/registry installs,
* which read `bundle.manifest.routes` off a downloaded bundle. Every one of
* its non-admin routes fell through to the sandbox's `{ public: false }`
* fallback regardless of what the plugin itself declared. See #2078.
*/

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[needs fixing] This block comment cites issue #2078 and narrates the bug history ("never got its route auth metadata populated", "fell through to the sandbox's { public: false } fallback"). AGENTS.md explicitly forbids issue/PR references and stale bug narrative in source comments — that context belongs in the commit message and PR description. Replace it with a short description of what the test covers.

Suggested change
/**
* A `sandboxed: []` (config-declared, non-marketplace) plugin never got its
* route auth metadata populated -- unlike marketplace/registry installs,
* which read `bundle.manifest.routes` off a downloaded bundle. Every one of
* its non-admin routes fell through to the sandbox's `{ public: false }`
* fallback regardless of what the plugin itself declared. See #2078.
*/
/**
* Integration tests for route auth metadata on config-declared sandboxed plugins.
*/

Comment on lines +142 to +148
/**
* Route declarations (name + `public`/`permission`/`cacheControl`), mirroring
* the plugin's own `definePlugin({ routes })`. Required for `sandboxed: []`
* registration -- unlike marketplace/registry installs, there is no bundled
* `manifest.json` to read at build time, so route auth metadata can only come
* from here. Omitted routes default to non-public.
*/

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[needs fixing] This JSDoc contains reviewer-facing narrative and prohibited phrasing: "unlike marketplace/registry installs", "there is no bundled manifest.json to read at build time", "so route auth metadata can only come from here". AGENTS.md says comments should not justify decisions or narrate alternatives. Keep the API contract concise.

Suggested change
/**
* Route declarations (name + `public`/`permission`/`cacheControl`), mirroring
* the plugin's own `definePlugin({ routes })`. Required for `sandboxed: []`
* registration -- unlike marketplace/registry installs, there is no bundled
* `manifest.json` to read at build time, so route auth metadata can only come
* from here. Omitted routes default to non-public.
*/
/**
* Route declarations for sandboxed config-declared plugins. Mirrors
* definePlugin({ routes }) and drives route auth decisions; omitted routes
* default to non-public.
*/
routes?: Array<ManifestRouteEntry | string>;

Comment on lines +150 to +154
/**
* Hook declarations this plugin implements, mirroring `definePlugin({ hooks })`.
* Same rationale as `routes` -- needed for `sandboxed: []` since there's no
* bundle manifest to read it from.
*/

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[needs fixing] The "Same rationale as routes" phrasing is circular/justifying, and "bundle manifest to read it from" references an internal implementation detail. Trim this to a concise description of the field.

Suggested change
/**
* Hook declarations this plugin implements, mirroring `definePlugin({ hooks })`.
* Same rationale as `routes` -- needed for `sandboxed: []` since there's no
* bundle manifest to read it from.
*/
/**
* Hook declarations for sandboxed config-declared plugins. Mirrors
* definePlugin({ hooks }).
*/
hooks?: Array<ManifestHookEntry | string>;

@github-actions github-actions Bot added review/awaiting-author Reviewed; waiting on the author to respond and removed review/needs-review No maintainer or bot review yet labels Jul 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/core review/awaiting-author Reviewed; waiting on the author to respond size/M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Sandboxed plugins declared via astro.config (sandboxed: []) never get public route metadata — every non-admin route requires auth

1 participant