Skip to main content

Manifest reference

manifest.json is a plugin’s identity file, located at the root of the .qplugin package. It describes the plugin’s metadata, permissions, entries, and extension points. Generating a valid manifest skeleton with qomicex create <id> is recommended (it sets up id / layers / permissions / entry automatically); then validate it with qomicex verify. This page covers every field so you can adjust them as needed.

Complete structure

Field summary

entry object

If you omit entry.frontend, the plugin is not activated (activatePlugin only renders and marks active when entry.frontend exists), so it cannot register methods or provide UI. Even for purely functional plugins, providing an empty page entry is recommended. The one exception is a pure L3 (wasm) plugin: when layers contains only l3 (no l2/l4), the CLI allows entry to be omitted (the gateway loads it from the fixed file name plugin.wasm), but such a plugin still has no page entry and is never activated or rendered.

contributes object

Plugin-owned i18n (l10n)

Declare "l10n": "l10n" at the manifest top level (a directory inside the package, relative to the .qplugin root):
Values are a flat key → string map. Manifest display strings reference entries with %key% (e.g. "name": "%plugin.name%", installModes labels/descriptions); the host resolves them at manifest load time using the launcher’s current locale (missing keys in the locale bundle fall back to the default bundle, then to the raw %key%). Plugin runtime UI reads the same merged bundle through the bridge:
Every string a plugin contributes for host-side rendering (menus, install modes, overlay titles, progress titles, step labels) should go through this channel — never depend on the launcher’s own i18n.

icon field spec (menus / install modes / top-level icon)

Choose in priority order:
  1. Font Awesome class name (recommended): "fa-solid fa-server". Zero assets, follows the theme color (currentColor), resolved by the launcher (sidebar and install dialogs render uniformly).
    • Do not hardcode the available set: the built-in table evolves with the launcher. Get the full list at runtime with call('listIcons'), or offline from the ICON_NAMES export of @qomicex/plugin-ui.
    • Names outside the table render blank (historically ignored silently). qomicex verify / lint validate them statically, report an error, and suggest the closest built-in names.
    • In-page icons should not call getIconSvg; use <Glyph> with per-icon subpath imports (see Plugin API — Icons).
  2. In-package relative path: "dist/icon.svg" (for multicolor/brand icons; resolved to the plugin file-service URL).
  3. Absolute URL / emoji / plain text: e.g. "https://…/icon.png", "🏆", "A". Emoji do not follow the theme color and do not scale well; prefer option 1 or 2.
  4. Private icons (contributes.iconTheme): point at an in-package icon-theme.json shaped as { "schemaVersion": 1, "icons": { "<icon key>": { "type": "svg", "path": "M…" } } }. Declared keys show up in listIcons() and are resolved first by getIconSvg — you can ship icons the built-in table lacks without waiting for a launcher release.

installModes array elements

When installing modpacks and other resources, the install dialog’s “Install as” option group lists entries contributed by active plugins (the default entry is the launcher built-in “Create instance”). Once the user selects one and confirms, the launcher invokes the installMode:{id} method through the plugin registry (the plugin must register it with __PLUGIN_API__.registerMethod), passing a resource context:
No extra permission is required: the entry point is declared in the manifest, and the method call goes through the existing registerMethod (config:write) permission model. For a worked example see the server manager plugin (top.qomicex.server-manager, the “Install as a server” install mode).

hooks array elements

The declaration documents the hook and allows a permission pre-check; the actual handler is registered at runtime with __PLUGIN_API__.registerHook.

settingsPages array

Collect a plugin’s settings UI into the launcher, so settings entry points are not scattered around.
  • Each path is the relative path of an HTML file inside the .qplugin package; the tab disappears automatically when the plugin is disabled
  • The launcher renders one tab per path under Settings → Plugins → Plugin settings (vertical tabs on the left list each plugin; the right side is an iframe sandbox with the same __PLUGIN_API__ bridge as plugin pages)
  • For the full development guide (writing the settings HTML, using getSettings/setSettings, openPluginSettings navigation, lifecycle rules), see Plugin settings page

icon examples

Relative paths inside the package are resolved by the launcher to http://localhost:5000/api/plugins/{id}/files/dist/icon.png, with nothing extra needed on the plugin side.

Top-level icon (library plugin icon)

Besides menuItems[].icon, icon is also supported at the manifest top level, so library plugins without contributes.menuItems show an icon in plugin management / the list:
  • The supported forms match menuItems[].icon (emoji / absolute URL / relative path inside the package / FontAwesome class name)
  • Plugin management cards read them in this order: contributes.menuItems[0].icon ?? manifest.icon
  • Library plugins should use the top-level icon and should not declare contributes.menuItems (which creates a sidebar entry)
  • Prefer FontAwesome class names for icons (such as fa-solid fa-file-lines, fa-brands fa-markdown; they must be in the launcher’s BuiltinIcons.tsx preset list): SVG paths rendered through <img> stay black and do not follow the theme, whereas FA class names follow the light and dark themes

overlay object

layers layer definitions

layers sorts plugins by complexity, from pure declarations to executable logic, and determines a plugin’s capabilities and how it runs: How to declare: layers is an array, so you can declare several layers at once:
Current implementation status (important):
  • The iframe sandbox is the default rendering mode: plugins with entry.frontend use the iframe sandbox by default (<iframe sandbox="allow-scripts"> plus a postMessage bridge), isolated from the main UI’s DOM/CSS (opaque origin, preventing CSS leakage and same-origin theft). Inline rendering (same context as the main UI, compatible with older plugins) only happens when the manifest explicitly declares "render": "inline".
  • The iframe sandbox also supports all __PLUGIN_API__ methods, .p-* component styles, and registerMethod/callPlugin (relayed across windows)
  • Sandboxed plugin scripts run automatically when the srcdoc is parsed, without visiting the page
  • Plugin activation conditions: after installation a plugin starts in the installed state, and actual activation depends on two things: ① entry.frontend exists (activatePlugin only renders and marks active when entry.frontend exists); ② the user enables it in the plugin list. This is independent of the layers declaration; a pure ["l3"] plugin with entry.frontend is still activated (using the iframe sandbox by default).
  • Differences between the iframe sandbox and inline rendering:
  • L0/L1 are currently reserved declarative layers with no standalone runtime mechanism yet; L3 WASM is implemented: plugins that declare l3 and ship plugin.wasm in the package are loaded and executed by the launcher’s Rust layer (wasmtime) (see WASM plugins)
  • L4 remote WebView is implemented: for plugins that declare render:"webview" or include "l4" in layers, the main window calls createRemoteWebview on activation to open a standalone Tauri WebviewWindow (process-level isolation from the main UI); the plugin UI renders inside that window, and API calls are forwarded to the main window through Tauri events for execution (see L4 remote WebView)
  • Plugins that need an isolated environment or a long-running script (such as AI assistants or tool panels) → use the default iframe sandbox
  • Pure UI/theme plugins, or lightweight plugins that depend on the main UI DOM → explicitly declare "render": "inline"
  • If the plugin will later add WASM backend logic, you can layer "l3" on top
  • Heavy UI / untrusted / process-isolation plugins (such as complex editors or game UI enhancements) → declare "render": "webview" or include "l4" in layers (standalone window, so a busy loop does not affect the main UI)

Version range syntax

dependencies[].version supports the following forms: Comparison rules:
  • Segments are split on . and compared numerically one by one; - (prerelease) and + (build number) suffixes are ignored
  • A missing segment counts as 0 (so 1.0 equals 1.0.0)
  • ^, ~, *, the x wildcard, ||, and commas are not supported
The launcher does not validate minLauncherVersion itself, but the store does: POST /plugins/check-updates compares semver and drops versions whose minimum version is above the current launcher from the update list, and GET /plugins?minLauncherVersion= filters on it as well. The CLI (qomicex verify) additionally requires the field to be present. So always fill it in, otherwise CLI validation fails and the store cannot apply compatibility filtering.