Repository navigation
Conversation
…pabilities (#97, step 4) Admin no longer fills core's adminContext bag or implements mountEarly: it declares auth.session and scheduler.executor in setup(), reads inline-edit, editor.content and admin.pages through MountApi.capabilities, and reads the job scheduler from BootstrapResult.jobScheduler. mountDuneAdminEarly() is removed. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ssion checks (#97)
A plugin contributes { method, path, permission?, handler } and the admin
registers it under its prefix; the handler only runs after the admin login, the
CSRF check on non-GET methods and the permission check. Replaces registering
guarded mutation routes in mount() with withGuards(), which stops working now that
the admin no longer mounts first.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
An entry without a permission is skipped and logged instead of being exposed to every authenticated admin. The pages mounting moves into mountPluginAdminPages() so it can be tested. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Restore, staging publish, translate x2, dev apply, reorder (new onPageMove) and collab auto-save wrote pages and fired nothing. They now share announcePageEvent(), which fires the hook and the content webhooks. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… result (#97) Adds mountAdminPanel() (panel only); mount() no longer re-registers public plugin routes and form endpoints, which createDuneApp() owns. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
mount() casts app to Fresh's App (the admin uses fsRoutes, layout, route) and the block editor declares a FreshContext handler. deno task check now also type-checks mod.ts. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…s; drop the createSubmissionManager re-export (#97, slice 4) Core's createDuneApp() owns both registrations (the admin's second call was a no-op), and the submission store is PluginRuntime.submissions. Lets core stop exporting registerPluginPublicRoutes, registerFormRoutes and createSubmissionManager. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Both authenticate() implementations threw, and mount() refused ldap/saml config anyway. Custom providers still go through the authProvider plugin option. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…arries it Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…in.runtime_dir Core moved runtime_dir and max_revisions to system.* because its own history, job state and analytics use them. The admin takes the resolved value from services.paths.runtimeDir (webhook logs, email preview, CSRF secret, workflow and page events). /api/introspect reports pageCacheEnabled: core no longer has system.cache. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Core stops reading admin.path and learns where the admin is mounted from this slot. With the admin disabled nothing is provided and core treats /admin as an ordinary path. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
AuditLogger, the audit types and the DeepL/Google/LibreTranslate translators move here from @dune/core (the @dune/core/audit and @dune/core/mt entry points are gone). The admin builds both in setup(); translators take the fetch they use, and the admin passes services.security.safeFetch so core's SSRF policy still applies. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…e site facts it cannot get from PluginApi
createAdminPlugin(opts) no longer takes config and storage (they come from PluginApi in setup()),
and the default export is the plugin-loader factory (config, { root, dev }) so
plugins: [{ src: jsr:@dune/plugin-admin@^4 }] is enough. admin.enabled is gone (core refuses it:
list the admin to run it). Tests list the admin explicitly; the conformance driver uses the
2.0.0 @dune/testing shape (hostsContributions, prefix from the admin.prefix slot).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…gin config name dune-admin -> admin, so the settings are config.plugins.admin (the config: of its entry in plugins:). adminSettings(config) is the one reader; it applies the defaults and refuses a path or auth_provider that would desync the admin. WebhookEndpointConfig, WebhookContentEvent and AuthProviderConfig move here from @dune/core, which now has no admin config at all. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Read from config.plugins.admin.machine_translation with api_key / base_url (were apiKey / baseUrl). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Matches @dune/core's rename (duneorg/dune#130): the resource names the site content being protected, not the panel. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…une#125) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This was referenced Nov 7, 2026
Closed
zumbrunn
marked this pull request as ready for review
November 10, 2026 16:33
Member
Author
|
Landed in 4.0.0 as part of #26 (one squash of the whole stack: its intermediate commits only type-check against the final contract). Verified against the published @dune/core 0.36.0 and @dune/testing 2.0.0. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of duneorg/dune#97 (step 4) and #64. Stacked on #16 (slice 3b); base is that branch. Draft until the admin stack (#12 to #16) and core 0.36 are out; it also needs the core capability PRs (duneorg/dune#109, #110, #111).
The admin now meets core through the typed capability registry instead of the untyped
adminContextbag,adminServices/adminPagesandmountEarly.Provides (declared in
setup(), resolved by core beforemount()):auth.session: who is the admin on a request. Core uses it for the admin bar's response transform and the inline-edit WebSocket's admin fallback.scheduler.executor: runs due scheduled actions;dune servestarts core's scheduler loop when it exists.Both delegate lazily to the admin context built in
mount()and answer "nobody" (unauthenticated, no-op) before that.Consumes (
MountApi.capabilities): theinline-editandeditor.contentslots (falling back to the built-in block editor), and theadmin.pagescontributions. The jobs view readsBootstrapResult.jobScheduler, whichdune servesets after mount.Removed:
bootstrap.adminContext = ..., themountEarlyimplementation andmountDuneAdminEarly().mountDuneAdmin(app, ctx, adminContext?)falls back to the contextmount()stored, notctx.adminContext.permissionis required onadmin.pagesandadmin.routesentries (core types it that way). The admin enforces it at mount too: an entry without one, with an unknown method or with a path that does not start with/is skipped and logged (admin.pages.invalid/admin.routes.invalid), so an untyped plugin cannot expose a page or a mutation endpoint to every authenticated admin. The pages mounting moved intomountPluginAdminPages()so it is unit-tested.Guarded plugin routes:
admin.routes. Routes a plugin registers in its ownmount()are no longer behind the admin's middleware (that is whatmountEarlyprovided), sowithGuards()cannot work there. The admin now mounts contributions to the newadmin.routespoint ({ method, path, permission, handler }) under its prefix behind its own login, the CSRF check on non-GET methods and the permission check (mountPluginAdminRoutes()); the handler only runs when all pass. Invalid entries are skipped and logged asadmin.routes.invalid.auth.sessionalso declaressessionCookies: ["dune_session"]so core can decide when to authenticate a public page and when to keep a response out of the shared page cache.Tests.
deno task checkanddeno test -A --unstable-kvpass (254) against the core capability stack in a scratch workspace. New:tests/admin/capabilities_e2e_test.tsruns against real core: the admin provides both capabilities (andanonymousis reported unauthenticated), anadmin.pagespage contributed by another plugin is mounted behind the login, and a plugin'seditor.contentslot is picked up. Removed: themountEarlyordering test.Conformance.
tests/admin/conformance_test.tsruns@dune/testing'srunAdminConformanceagainst the real admin (temp site, seeded user, real login): all 9 checks pass. Needs@dune/testingreleased with the suite (deno.jsonpinsjsr:@dune/testing@^1).Page events (#97). Whoever writes a page fires its event; core does not. Six admin paths wrote pages and fired nothing: history restore, staging publish, both translate routes, dev apply and collab auto-save (reorder renamed pages and had no event at all). They now share
announcePageEvent()(hook plus the configured content webhooks); reorder fires the newonPageMove; collab auto-save firesonPageUpdatewhen a save changed the file (hook only, it saves every few seconds). Newtests/admin/page_events_test.tsdrives create, update, reorder, delete and restore through the real routes; the conformance driver gainedcreatePage/deletePage. 265 tests. Needs core withonPageMove(duneorg/dune#112). Not tested over HTTP: staging publish (its route is unreachable, being fixed separately), translate, dev apply and collab auto-save.mount()readsMountApi.runtime(#97, core#114). Core'smount()argument no longer carries the whole bootstrap result; the admin takes engine, search, history, hooks, submissions, flex, image cache, metrics, sections, authz and the job scheduler from the curatedPluginRuntime(authzreplaces the oldauthz && authzAdaptercheck).mount()now calls a newmountAdminPanel()(panel routes, context middleware,admin.pages/admin.routes) and no longer registers public plugin routes or form endpoints a second time, since core'screateDuneApp()owns those; the headlessmountDuneAdmin(app, bootstrapResult)is unchanged. 267 tests pass against core#114 plus the mutation-events commits.Narrows the contract's
PluginAppto Fresh (#97, core#115). Core's contract no longer names Fresh types:MountApi.appis aPluginApp, handlers get aPluginRouteContext. The admin uses Fresh-only features (fsRoutes,layout,route), somount()castsappto Fresh'sAppand the block editor declarespageEditorHandler(ctx: FreshContext). No behaviour change.deno task checknow also type-checksmod.ts, which it silently skipped (that is how the cast error was found late). 267 tests pass.Slice 4 (#97).
mountDuneAdmin()no longer registers plugins' public routes or the form/webhook endpoints, and thecreateSubmissionManagerre-export is gone. Core'screateDuneApp()already registers both (the admin's second call was a no-op), and the submission store isPluginRuntime.submissions. The admin now imports neitherregisterPluginPublicRoutesnorregisterFormRoutes, which lets core stop exporting them later. Works with core as it is today (267 tests). Note: the documented 'headless' flow (ownApp+mountDuneAdmin(), nevermountPlugins()) already 500s on every admin route because the admin context is never set;mountDuneAdmin()is left as a thin wrapper and that flow is not fixed here.Not mergeable until
@dune/coreis^0.36.0. Still to do for step 4:plugin-inline-edit(providesinline-edit, readsbar.actions).🤖 Generated with Claude Code