Skip to content

Manifest Specification

Every plugin ships with a plugin.json file at the root of its distribution ZIP. The manifest controls how RefMD loads, routes, and authorises the plugin.

Minimal Example

{
  "id": "sample",
  "name": "Sample Plugin",
  "version": "0.1.2",
  "abi": "refmd-plugin/v1",
  "mounts": ["/sample/*"],
  "frontend": { "entry": "index.mjs", "mode": "esm" },
  "backend": { "wasm": "backend/plugin.wasm" },
  "permissions": ["doc.read", "doc.write"],
  "ui": {
    "toolbar": [{ "title": "New Sample Document", "action": "sample.create" }],
    "fileTree": {
      "icon": "FileText",
      "identify": { "type": "kvFlag", "key": "meta", "path": "isSample", "equals": true }
    }
  }
}

Field Reference

Field Type Required Description
id string ✓ Stable, URL-friendly identifier (^[A-Za-z0-9_-]+$). Used for REST routes, storage directories, and UI grouping.
name string – Human-readable label shown in the plugin gallery.
version string ✓ Semantic or build version. Determines cache-busting and storage paths (plugins/<scope>/<id>/<version>/).
abi string – Declares the expected ABI for the Extism module. Current backend uses refmd-plugin/v1 for compatibility checks.
mounts string[] – Route globs the frontend should own. Defaults to /<id>/* when omitted.
frontend.entry string ✓ (if frontend present) Relative path (within the archive) to the ESM bundle. Resolved via /api/plugin-assets/....
frontend.mode string – Only "esm" is currently supported. Lowercase esm is assumed when omitted.
backend.wasm string ✓ (if backend present) Relative path to the Extism WASM module. Defaults to backend/plugin.wasm if missing.
permissions string[] – Requested host capabilities. The host surfaces them for display today; common values are doc.read and doc.write.
commands string[] – Reserved for future use. The host currently ignores this field and instead derives actionable commands from ui.toolbar.
config object – Arbitrary JSON blob stored alongside the manifest. Useful for static plugin configuration.
ui.toolbar array – Adds buttons to the Plugins page or command palette. Each item accepts title and action.
ui.fileTree object – Controls file-tree decorations: icon (Lucide icon name) and optional identify rules. kvFlag rules check plugin KV (scope doc) before attaching the icon.
author string – Attribution shown in the UI.
repository string – URL to the source repository or documentation.
renderers array – Declares Markdown placeholder renderers. Each item needs a lower-cased kind, optional function (Extism export name, defaults to render), and optional hydrate spec.

Renderer Items

Renderer entries bridge server-side placeholder rendering and client-side hydration:

  • kind — matches the placeholder kind emitted by the Markdown service.
  • function — Extism export used for server-side rendering (render when omitted).
  • hydrate.module — Relative module path (for example frontend/hydrate.js). Must stay within the archive—no absolute or parent-directory references.
  • hydrate.export — Optional named export (defaults to the module default export).
  • hydrate.etag — Optional string used to bust hydration caches.

UI Identify Rules

ui.fileTree.identify currently supports the kvFlag strategy:

{
  "type": "kvFlag",
  "key": "meta",
  "path": "isSample",
  "equals": true
}
  • Uses the plugin ID, doc scope, and docId to resolve KV storage.
  • Reads the JSON blob under key and optional dot-delimited path.
  • Compares the value to equals (strict equality). Matching documents receive the plugin’s icon.

Packaging Expectations

A plugin archive is a ZIP with this structure:

plugin.zip
├── plugin.json
├── backend/
│   └── plugin.wasm
└── frontend/
    └── dist/
        └── index.mjs
  • Paths inside the archive must be relative; no absolute or parent-directory escapes.
  • The host extracts archives into plugins/global/<id>/<version>/ (for pre-installed plugins) or plugins/<user-id>/<id>/<version>/ (user installs).
  • Additional assets (CSS, hydration modules, etc.) are allowed as long as they live within the archive and are referenced using relative paths.

Manifest Distribution

  • Global manifests are read from disk when RefMD boots and merged into /api/me/plugins/manifest.
  • User-specific manifests are loaded after installation (InstallPluginFromUrl stores the archive and manifest per user).
  • The frontend sorts manifests by scope (user before global) and plugin ID.

Install your ZIP in a local RefMD stack and open the Plugins page to confirm parsing. The host logs manifest issues to the server console.