Version
Current contract version: 1.0.0
This contract defines the stable interface between ProxmoxVEx core and its plugins. Plugins that conform to this contract can be loaded and managed by the plugin registry.
Interface
File layout
plugins/{plugin-name}/
├── manifest.json # Required. Machine-readable contract metadata.
├── __init__.py # Required. Must export a `register(app)` function.
├── ui.html # Optional. Frontend iframe markup (receives shared plugin-ui.css).
└── config.json # Optional. Default configuration schema.
manifest.json
{
"id": "plugin-name",
"name": "Plugin Name",
"version": "1.0.0",
"author": "Author or organization",
"description": "Human-readable one-line description.",
"min_ProxmoxVEx": "1.0.0",
"has_frontend": true,
"frontend_route": "ui",
"category": "Tools",
"min_license_tier": "basic"
}
Fields read by the plugin registry (ProxmoxVEx/api/plugins.py):
id— unique plugin identifier (must match the directory name).name,version,author,description— display metadata.min_ProxmoxVEx— minimum core version required.has_frontend/frontend_route—frontend_route: "ui"servesui.htmlthrough the standard plugin-UI route; a custom value maps to the plugin’s own frontend route.category— grouping in the plugin directory.min_license_tier— minimum license tier required to enable the plugin.
__init__.py
def register(app):
"""Register plugin blueprints, handlers, and UI components.
The plugin loader calls ``register(app)`` — not ``load(app)``.
Args:
app: The ProxmoxVEx Flask application instance.
"""
from . import routes
app.register_blueprint(routes.bp)
UI rendering and shared stylesheet
Plugins that expose a frontend iframe should ship a ui.html file in the plugin directory. When ProxmoxVEx serves the standard /api/plugins/{plugin_id}/api/ui route, it automatically injects the shared base stylesheet right after the opening <head> tag:
<link rel="stylesheet" href="/static/css/plugin-ui.css" />
- You do not need to add this
<link>manually inplugins/{plugin-name}/ui.html; the core injection adds it automatically and skips the file if it is already present. - Native/built-in plugins that are not served through the standard route should include the same
<link>directly in their HTML. - Plugins may load additional stylesheets or inline
<style>blocks after the shared sheet for specific overrides.
In-UI configuration editor
Every plugin UI served through _serve_plugin_ui also receives
/static/js/plugin-config.js (after plugin-theme.js, plugin-events.js
and plugin-ux.js). It mounts a configuration surface inside the plugin
iframe that reads and writes the plugin’s config.json via
GET/PUT /api/plugins/{id}/config. The host app’s plugin card “Manage”
window stays a routes-only API explorer; the Settings → Plugins JSON editor
remains as an admin fallback.
Mounting rules (evaluated on DOMContentLoaded):
| Plugin markup | Result |
|---|---|
<meta name="pvx-config-ui" content="off"> |
No floating gear; the PVE.config API stays available |
any [data-pvx-config] element |
Click handler bound to each; no floating gear injected |
| neither of the above | Floating settings gear injected at bottom-left |
PVE.config.open()/PVE.config.close()/PVE.config.mount(el)are exposed (also asPVE.ui.config) so a plugin’s own Settings tab or button can reuse the same modal.- When
manifest.jsondeclaresconfig_schema(spec 092 shape:{field: {type, label?, description?, required?, secret?, enum?, minimum?, maximum?, default?}}), the editor renders typed, validated fields; otherwise it shows a validated raw-JSON editor. Both views are always reachable via the Form / Raw JSON toggle. - The
/configendpoint requires theplugins.managepermission; users without it see a permission notice inside the modal.
Stability guarantees
- Major contract changes break backward compatibility and require a new
contract_version. - Minor contract changes add capabilities without breaking existing plugins.
- Patch contract changes are clarifications or fixes to the contract text only.
Deprecation policy
- Deprecated contract versions are supported for at least one major ProxmoxVEx release.
- A plugin using a deprecated contract version will log a warning on load.
- The plugin registry may refuse to load plugins targeting an unsupported contract version.
Security boundaries
- Plugins run in the same process as ProxmoxVEx core and are therefore trusted by default.
- Plugins must not import or access the database layer directly; use the public API or core hooks.
- Plugin frontend iframes are loaded same-origin and must comply with the configured
Content-Security-Policy.