Pages are served from your own https origin; the control plane loads them at fixed mount points in <iframe sandbox="allow-scripts allow-forms"> (no allow-same-origin). The frame origin is the opaque null: no access to the control plane’s cookies, localStorage, sessionStorage or IndexedDB, its DOM, or top-level navigation. Only enabled ui_page plugin origins enter the control plane’s frame-src; disabling or uninstalling removes them on the next sync.
Handshake and messages
The page reads the nonce from the URL fragment #cdnca-nonce=<nonce> and posts {protocol, type: "cdnca.hello", nonce} to parent.
The host accepts only this iframe’s window and nonce, answers {type: "cdnca.ready"} and hands over a MessagePort.
From then on every request uses that port: {protocol, nonce, id, method, params}, answered with {protocol, nonce, id, ok, result | error}.
Method
Meaning
context.get
User ID / display name / role, tenant scope, site ID, locale, light or dark theme; no credentials
navigate
{path}: an in-app relative path; sign-in, password recovery, API keys, profile and similar pages are refused
api.request
{method, path, query?, body?}: call your api_base_url through the control plane proxy
Any other message answers message_not_allowed.
Restricted API and backend verification
api.request goes to the control plane’s POST /v1/plugin-ui/{plugin_id}/proxy, which forwards it to api_base_url: public addresses only, no redirects, JSON only, 120 per user per plugin per minute. The control plane never forwards the user’s session or token; it adds three headers instead:
Header
Content
X-Cdnca-Plugin-Context
base64url JSON (cdnca.plugin-ui-context/v1), valid 60 s, with a nonce
X-Cdnca-Plugin-Signature
Ed25519 signature by the installation key (base64url)
path is relative to the path part of api_base_url. Your backend verifies with the installation public key, checks validity and remembers the nonce. The customer’s administrator copies the installation public key to you from “Plugin market → plugin detail → Installation signing key” (GET /v1/plugin-ui/signing-key; key_id is X-Cdnca-Plugin-Key-Id). Writes and refused calls are audited as plugin.ui_page.proxy.
Installation confirmation
When the customer’s administrator confirms an install or upgrade, the dialog checks the version on the plugin market by the manifest fingerprint the licence service signed and lists the menus, page origins and plugin backend it adds; if the fingerprint does not match or the market is unreachable it only asks to verify with the platform and never shows another version’s declaration.
SDK files and self-test
cdnca-plugin-ui.mjs: the in-frame SDK, no dependencies. connect() does the handshake and returns context() / navigate(path) / request({method, path, query, body}); errors are PluginUIError with a code.