Plugin Manifest Specification
Every plugin version submitted to klenua includes a manifest — a small JSON document describing what the plugin is, what it needs to run, and how the future desktop host should launch it. The manifest is stored verbatim (plugin_versions.manifest_json) alongside the structured fields the store and moderation UI actually query (version, permissions, etc.), so nothing is lost even as the schema around it evolves.
This document specifies the format. The runtime that executes plugins does not exist yet — see docs/plugin-system.md for what's built today versus reserved for later.
Example
{
"id": "com.example.jsonlab",
"name": "JSON Lab",
"version": "1.0.0",
"description": "Format, inspect and transform JSON.",
"author": "Example Developer",
"entry": "main.js",
"minimumHostVersion": "1.0.0",
"permissions": [
"clipboard.read",
"clipboard.write"
]
}
Fields
| Field | Type | Required | Notes |
|---|---|---|---|
id | string | yes | Reverse-DNS bundle identifier, e.g. com.example.jsonlab. Globally unique on klenua, immutable after the plugin is first created (plugins.identifier). |
name | string | yes | Display name. |
version | string | yes | Semantic version — MAJOR.MINOR.PATCH, optionally with a -prerelease suffix. Must be unique per plugin (plugin_versions.version). |
description | string | no | Short description; the store listing's own short_description is authoritative for display, this is informational. |
author | string | no | Free-text attribution; the store's developer identity (plugins.developer_id) is authoritative for who owns the listing. |
entry | string | yes | Relative path inside the package to the plugin's entry point (e.g. main.js). Meaningful only to the future runtime — the marketplace backend never opens, parses, or executes package contents. |
minimumHostVersion | string | yes | Lowest desktop app version this plugin will run under (semver). Mirrored to plugin_versions.minimum_host_version so the store and the update checker can reason about compatibility without re-parsing the manifest. |
maximumHostVersion | string | no | Optional upper bound, for a plugin that's known to break on a future host API change. |
permissions | string[] | no (default []) | Capability keys the plugin is requesting — see below. Everything not listed is implicitly denied. |
requires | string[] | no (default []) | Proposed, not yet built — other plugin ids this plugin depends on, optionally with a semver range, e.g. "com.example.core-utils@^1.0.0". See docs/plugin-system.md#proposed-hooks-filters-settings-and-dependencies. |
settings | object | no | Proposed, not yet built — a schema describing this plugin's configurable options (key, type, default, label), used to generate a settings screen automatically. See docs/plugin-system.md#proposed-hooks-filters-settings-and-dependencies. |
Permissions
A fixed catalog (plugin_permissions table), not free text, so the store, the moderation UI, and the future host can render consistent labels/icons and diff permission changes between versions:
| Key | Risk | Meaning |
|---|---|---|
filesystem.read | medium | Read files/folders the user explicitly selects |
filesystem.write | high | Create/modify files/folders the user explicitly selects |
network | medium | Make outbound network requests |
clipboard.read | low | Read the system clipboard |
clipboard.write | low | Write the system clipboard |
notifications | low | Show system notifications |
shell.execute | high | Execute shell commands |
localStorage | low | Persist small amounts of plugin-local data |
project.read | medium | Read files within the user's current project |
project.write | high | Modify files within the user's current project |
A submitted version's requested permissions are stored per-version (plugin_version_permissions), not per-plugin — so when a developer ships 2.0.0 and adds shell.execute, that's a version-scoped change a user can be shown before accepting an update. The permission-diff-on-update UX itself is a desktop-app concern; the data model already supports it.
Proposed: requires and settings
Not yet built — the full reasoning lives in docs/plugin-system.md#proposed-hooks-filters-settings-and-dependencies. Shape, if adopted:
{
"requires": ["com.example.core-utils@^1.0.0"],
"settings": {
"theme": { "type": "string", "default": "system", "label": "Theme" },
"autoFormat": { "type": "boolean", "default": true, "label": "Format on save" }
}
}
Extending the manifest without breaking existing plugins
- New optional fields may be added at any time; a manifest with fields the
current backend doesn't recognize is still accepted and stored as-is.
- Never repurpose an existing field's meaning or type.
- A genuinely breaking change to the format (e.g. restructuring
permissions) would be introduced as a new top-level manifestVersion field, defaulting to 1 when absent, so old manifests keep parsing under their original rules indefinitely.
Validation today
At submission time the backend validates the structured fields it stores independently of the manifest (version as semver, minimum_host_version, requested permissions against the catalog). Full manifest-schema validation (matching entry against the actual package contents, etc.) is scoped to the future plugin runtime, since it requires safely inspecting package contents — which this backend deliberately never does.