Skip to content

docs(rfc): propose plugin hot reload and plugin-defined APIs - #2579

Merged
hidb4ai merged 9 commits into
oceanbase:masterfrom
jasper-yl:rfc/plugin-reload-and-apis
Nov 14, 2026
Merged

hidb4ai merged 9 commits into
oceanbase:masterfrom
jasper-yl:rfc/plugin-reload-and-apis

Conversation

@jasper-yl

@jasper-yl jasper-yl commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

Which issue or RFC does this PR close?

This PR proposes a follow-up RFC to RFC #2549 (plugin interfaces). It builds on the plugin host processes and the in-memory record of active plugins defined there. The plugin APIs and Dashboard pages are implemented in #2616.

Rationale for this change

RFC #2549 deliberately leaves out three things:

  • Plugin changes require a Server restart.
  • Plugins are reachable only through core's existing Source and Artifact APIs.
  • Plugins have no way to upgrade the data an earlier version of them wrote.

Shared central deployments need to update plugins without downtime. Domain plugins such as document knowledge bases need their own operations (file upload, chunk editing, cross-base search and question answering) and their own pages, which do not fit core's generic APIs.

What changes are included in this PR?

New RFC docs/{en,zh}/rfcs/2579-plugin-reload-and-apis.md (Chinese first, English translation in sync):

  • Hot reload:
    • Each Server process rescans the plugin directory every POWERCONTEXT_SERVER_PLUGINS_RESCAN_SECONDS (default 60 s, 0 disables). A directory is checked only after its content digest is the same in two consecutive scans.
    • Plugins are imported from read-only version snapshots named by content digest, so one call never mixes old and new code. Updates replace child processes only: a new host process starts from the new snapshot and the old one drains.
    • A failed update keeps the current version running; a new plugin that fails its check is not loaded.
  • Plugin data upgrades: a plugin may provide upgrade(ctx), which core calls each time it loads a plugin version, before the version serves requests. The plugin decides whether and how to upgrade its data and stays compatible with old data itself; core records no data version and gives no fallback. The hook must be safe to repeat and to run on several instances at once.
  • Plugin-defined APIs:
    • Declared routes mount at /v1/plugins/{name}/api{path} with access authenticated, scope.read, or scope.contribute (with scope_param). Core authenticates the caller; the host process checks Scope access and runs the handler.
    • Handlers confirm write access with the route's access or ctx.require, then use ctx.reads, ctx.writer, ctx.sources, and ctx.scopes as the caller. Writes in plugin APIs record no automatic provenance; to record who did what, a plugin submits the operation as a Source it defines and writes the revision in generate.
    • Routes may declare max_body_bytes under a deployment ceiling POWERCONTEXT_SERVER_PLUGIN_API_MAX_BODY_BYTES (default 32 MiB) that replaces the general HTTP body limit for plugin routes.
    • JSON routes can opt in as MCP tools (mcp=True). Per-plugin OpenAPI is served at /v1/plugins/{name}/openapi.json, outside the main contract.
  • Plugin pages: static files shown in a sandboxed iframe (opaque origin, strict CSP). Pages call only their own plugin's routes, through postMessage forwarding by the Dashboard as the signed-in user, and never see the session cookie. Forwarding stops once the frame navigates away.
  • Persistence: nothing new is persisted.

Are there any user-facing changes?

This PR changes documentation only. The proposed design adds plugin API routes, Dashboard plugin pages, MCP tools for opted-in routes, and two environment variables; it changes no existing API.

How was this change tested?

  • uv run prek run --files docs/en/rfcs/2579-plugin-reload-and-apis.md docs/zh/rfcs/2579-plugin-reload-and-apis.md
  • Checked that both locales have matching sections, tables, and code blocks.

AI usage statement

Drafted with Claude Code (Claude Opus 5.5) under the author's direction and review.

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@hidb4ai hidb4ai 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 proposed reload and page-isolation mechanisms have reproducible gaps in their stated contracts. Details are inline; please address them in both language versions before accepting the RFC.

@hireview001 hireview001 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM

jasper-yl and others added 3 commits November 11, 2026 10:27
…a group

Pin every call to a read-only snapshot named by the content digest so a
draining host never mixes versions and a failed update keeps the old one,
detect changes by content instead of size and mtime, mount plugin routes
under their own group with per-route Scope authorization, let plugins
create Scopes with core's existing permission, and state page isolation
as session isolation, stopping forwarding once the iframe navigates.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Move scan, host replacement, and hosting page flows and the literal CSP header to the implementation design.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s, and record API changes as Sources

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@hidb4ai hidb4ai 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 content digests and immutable snapshots address the previous reload findings. Explicitly allowing external access also supersedes the original form-submission finding. Two issues remain in the revised design: navigation can expose the authenticated forwarding bridge to an external document, and MCP tool names can collide. Please address both language versions.

Comment on lines +529 to +532
- **Stop forwarding after navigation**: a sandboxed iframe can navigate itself to an external site, which CSP cannot
prevent. The external page can also send `postMessage` to the hosting page and passes the `event.source` check. The
hosting page therefore stops forwarding after the iframe's second load, so an external page cannot borrow the user's
session. Plugin pages should be single-page applications; navigating between plugin pages also stops forwarding.

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.

[P1] Bind forwarding to the original document before navigation completes

Waiting for the second iframe load is too late: scripts in the destination document run before that event. In a Chromium fixture implementing this rule, the plugin navigated to another local origin, whose script immediately posted a request for a declared plugin route while a slow image delayed load. The parent accepted it with loads === 1 and event.origin === "null", fetched the route with its HttpOnly; SameSite=Strict session cookie and Sec-Fetch-Site: same-origin, and returned the synthetic private response to the external document before disabling forwarding. The server-side checks therefore do not close this window. Bind requests and replies to a capability or message channel held only by the original document, rather than relying on the second load event and the frame's persistent WindowProxy identity.

Comment on lines +474 to +476
- The tool name is `<plugin name>_<handler>` (with `-` replaced by `_`), and its description comes from `description`,
which is required when opting in. Tool parameters combine path parameters, query parameters, and `request_model`; the
parameter named by `scope_param` is the Scope ID.

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.

[P2] Make MCP tool identities unambiguous across routes and plugins

The prescribed name is not unique for otherwise valid declarations: plugin docs with handler search_all and plugin docs-search with handler all both become docs_search_all. The same plugin can also expose two distinct (method, path) routes using one handler, which produces the same collision. A tools/call with that name and identical arguments cannot select which route to execute, so at least one opted-in route is unreachable or resolves to the wrong operation. Define collision-free tool identities, or reject duplicate generated names across all active plugin and built-in tools before activation, including during hot reload.

jasper-yl added a commit to jasper-yl/powercontext that referenced this pull request Nov 13, 2026
… and change their own Artifacts

Implements the Artifact plugin part of RFC oceanbase#2549 and the plugin APIs and
Dashboard pages of RFC oceanbase#2579, and replaces the managed Artifact plugin
framework (oceanbase#2246).

- A plugin is a directory with plugin.py assigning an ArtifactPlugin. Server
  checks it in a child process; generate runs in Supervisor Workers, and
  render_artifact, search_artifacts, user changes, Source writes of the
  plugin's own types, and plugin routes run in a per-plugin host process.
- Plugins present (GET .../rendering) and search their own families: generic
  search passes the complete request to search_artifacts, and Prepared Context
  sections may list plugin families. Core builds no index for them.
- Revision v1_5_0_005 removes the previous framework's storage only when it is
  empty, adds pc_artifacts.metadata (plugin name and code digest), and accepts
  plugin: bindings; frozen revision resources are unchanged. Portable revision
  records move to schema version 3 and still restore version 2.
- Plugin APIs mount at /v1/plugins/{name}/api with a configurable body
  ceiling, and plugin pages run in a sandboxed Dashboard frame.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
jasper-yl and others added 2 commits November 12, 2026 16:58
RFC oceanbase#2549 renamed the factory to setup and requires render_artifact; standalone
Source plugins moved to RFC oceanbase#2617, so the examples declare routes and pages on
an Artifact plugin that brings its own Source type.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ls without collisions

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@hidb4ai hidb4ai 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.

Re-reviewed 02089a8. The setup factory rename and ArtifactPlugin example updates leave both previously reported blockers unchanged: navigation can expose authenticated forwarding to an external document and generated MCP tool names can collide. The affected sections are unchanged in both locales, so the existing findings still apply to this head. Please address them in the original threads.

@hidb4ai
hidb4ai merged commit 17a67db into oceanbase:master Nov 14, 2026
39 checks passed
jasper-yl added a commit to jasper-yl/powercontext that referenced this pull request Nov 14, 2026
… and change their own Artifacts

Implements the Artifact plugin part of RFC oceanbase#2549 and the plugin APIs and
Dashboard pages of RFC oceanbase#2579, and replaces the managed Artifact plugin
framework (oceanbase#2246).

- A plugin is a directory with plugin.py assigning an ArtifactPlugin. Server
  checks it in a child process; generate runs in Supervisor Workers, and
  render_artifact, search_artifacts, user changes, Source writes of the
  plugin's own types, and plugin routes run in a per-plugin host process.
- Plugins present (GET .../rendering) and search their own families: generic
  search passes the complete request to search_artifacts, and Prepared Context
  sections may list plugin families. Core builds no index for them.
- Revision v1_5_0_005 removes the previous framework's storage only when it is
  empty, adds pc_artifacts.metadata (plugin name and code digest), and accepts
  plugin: bindings; frozen revision resources are unchanged. Portable revision
  records move to schema version 3 and still restore version 2.
- Plugin APIs mount at /v1/plugins/{name}/api with a configurable body
  ceiling, and plugin pages run in a sandboxed Dashboard frame.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

1.6.0 v1.6.0 release tracking

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants