Plugin Manifest
Manifest Reference
{
"schemaVersion": 2,
"id": "my-plugin",
"name": "My Plugin",
"version": "1.0.0",
"format": "cadnano_json",
"applyStrategy": "previewOnly",
"entrypointUrl": "/plugins/my_plugin/my_plugin.zip",
"requirements": ["numpy"],
"description": "Optional description shown in the plugin panel.",
"icon": "icon.svg",
"citations": [{ "text": "Author, A. and Author, B., Journal, 2026, 1(2), p.3.", "doi": "10.1234/example" }]
} | Field | Required | Notes |
|---|---|---|
schemaVersion | Recommended | Manifest schema version, currently 2. Read as 1 when missing, but include it; icon and citations need 2. Not your plugin's version; see Versioning. |
$schema | No | Link to the published JSON Schema for editor support. Ignored by Foldnano. |
id | Yes | Unique key used by the registry. Starts with a letter, then letters, digits, ., _ or -, up to 64 characters; kebab-case such as my-plugin is recommended. Checked when the plugin is installed. |
name | Yes | Display name in the panel. |
version | Yes | A semantic version such as 1.0.0 or 1.2.0-beta.1 (not latest or v1). Bump it whenever the plugin's files change. Checked when the plugin is installed. |
format | Yes | "cadnano_json" | "foldnano_json" | "foldnano_edit". ("live" is reserved and rejected at install time.) |
applyStrategy | Yes | Must match the selected format. |
entrypoint | Either/or | Inline Python source string. Only valid for sidePanel.codeEditor plugins with execution: "entrypoint"; see below. |
entrypointUrl | Either/or | URL to a .zip plugin bundle. Required for every other plugin. |
requirements | No | micropip package names, bare .whl filenames shipped in the zip, or wheel URLs fetched at activation. |
network | No | Hosts your plugin may contact, or ["*"] for any HTTPS host. Omit it for no network access; see Network access. |
description | No | Human-readable description. |
icon | No | Path of an SVG icon inside your zip, such as "icon.svg". Needs "schemaVersion": 2; see Plugin icon. |
citations | No | Papers you ask users to cite. Needs "schemaVersion": 2; see Citations. |
parameters | No | Array of PluginParamDef: user-configurable inputs shown in a run dialog. |
sidePanel | No | Optional host-rendered side panel request. Supports form and codeEditor UIs. |
x-… | No | Your own metadata (for example "x-homepage"). Allowed at the top level only; ignored by Foldnano. |
entrypoint and entrypointUrl are mutually exclusive: a manifest that sets both fails validation.
For nearly every plugin, entrypointUrl is required and must point to a .zip bundle (checked by parsed URL pathname, so plugin.zip?v=2 is still recognized). Raw .py URLs are no longer accepted. entrypointUrl can be:
- A relative path to a
.zipbundle served by Foldnano (e.g./plugins/my_plugin/my_plugin.zip) - An absolute URL on Foldnano's own site (see below)
The manifest inside the zip (fplugin.json) may omit entrypointUrl entirely, since the installer already knows the code is in the zip it just unpacked. Only a pointer manifest (a Hub entry that ships with Foldnano) needs entrypointUrl, and its job is purely to locate the zip: the manifest fetched from inside that zip is what actually gets registered, and its id must match the pointer's id.
The only case where inline entrypoint is allowed is a sidePanel.codeEditor plugin with execution: "entrypoint"; see Code-editor execution modes below. Such a manifest must not also set entrypointUrl.
Versioning and unknown fields
schemaVersion tells Foldnano which version of this manifest format you wrote against. It is separate from version, which is your plugin's own release number.
- A manifest without
schemaVersionis read as version 1, so plugins written before the Foldnano beta keep working. Include"schemaVersion"anyway: fields added later (such asiconandcitationsin version 2) only work once you declare the version that introduced them. - A manifest with a higher
schemaVersionthan the running app supports is refused with a request to update Foldnano, rather than having its newer fields silently ignored. - Unknown fields are rejected, at every level, so a typo such as
"netwrok"fails loudly instead of quietly doing nothing. Put your own metadata in top-level fields starting withx-.
When a field is added, schemaVersion normally goes up and the new schema is published next to the old one. During the beta, an optional field that only affects what is displayed can join the current version instead, as citations did. Existing fields never change meaning between versions, so a manifest that works today keeps meaning the same thing.
Machine-readable schema
Each manifest schema version has a JSON Schema (draft 2020-12), published with the app:
| Schema version | Schema |
|---|---|
| 1 | /schemas/fplugin-v1.schema.json |
| 2 | /schemas/fplugin-v2.schema.json (adds icon and citations) |
Point $schema in your manifest at the one matching your schemaVersion (for example "$schema": "https://<foldnano-host>/schemas/fplugin-v2.schema.json") and editors such as VS Code will autocomplete fields and flag mistakes as you type. The schema checks structure, types and the format/applyStrategy pairing. Foldnano checks a little more when installing (allowed URLs, field defaults and ranges, parameters versus side panel forms), so a manifest can pass the schema and still be refused; the install error says why.
Network access
Plugins run in a sandbox with no network access by default. To call a web API, list its hosts in network:
"network": ["api.example.org", "*.data.example.org"] - Entries are lowercase host names, optionally with a
*.prefix or a port ("localhost:8000"). Leave outhttps://and any path. ["*"]on its own allows any HTTPS host. Prefer listing hosts: users see the list before they install your plugin.localhostand127.0.0.1are reached overhttp://, everything else overhttps://. The server has to allow cross-origin requests (CORS), because the sandbox has no origin of its own.- PyPI access for your
requirementsis granted automatically. - A plugin can never reach Foldnano's own server or the user's GitHub account, whatever it declares.
- Declaring network access puts your plugin in the custom environment; see Runtime Model → Environment Tiers.
Entrypoint URL restrictions
During the beta, entrypointUrl must point at Foldnano's own site: in practice, the zips bundled with its Hub. A manifest pointing at any other website is refused at install time, with a suggestion to download the zip and use Import zip instead. To share your plugin, share the zip.
Wheel requirements
A requirements entry ending in .whl is handled one of two ways:
- Bare filename (e.g.
"pkg-1.0-py3-none-any.whl", no slash): the wheel must be present at the root of the plugin zip. It is stored in IndexedDB alongside the plugin at install time; activation never re-fetches it. - Path or URL on Foldnano's own site (e.g.
"/plugins/cadnano_mini/v0.1.0/pkg.whl"): fetched at activation time. Used by first-party plugins; a wheel on any other website is refused, so bundle yours in the zip.
Regular package names (e.g. "numpy", "humanize>=4") are installed from the Pyodide distribution or PyPI. A path, a URL, or a direct reference such as "pkg @ https://..." is refused for anything that isn't a .whl.
For bare package names, prefer packages from the pinned Pyodide distribution and develop against their exact versions; see Packages in Pyodide 314.0.7. Staying within that list (unversioned) keeps your plugin in the shared standard environment; see Runtime Model → Environment Tiers for what that means.
Host-rendered side panels
Plugins can request a dedicated sidebar tab by declaring sidePanel in the manifest. The plugin still ships only Python; the host chooses the UI:
type: "form"renders manifest-declared fields in the upper section and runtime output belowtype: "codeEditor"renders a built-in Python editor in the upper section and runtime output below
For type: "codeEditor", the optional sidePanel.codeEditor.execution field controls what the editor contents mean:
"input"(default): the plugin's manifest entrypoint runs, and the editor contents are passed to Python viactx.get_side_panel_code()"entrypoint": the editor contents themselves are executed as the plugin source for that run
sidePanel.icon (form, code or analysis) is deprecated for packaged plugins: bundle an SVG with the top-level icon instead, which wins when both are set. Code-editor plugins with execution: "entrypoint" have no zip, so sidePanel.icon remains their only icon option.
Plugin icon
Give your plugin its own icon by putting an SVG file in the zip and naming it in the manifest:
"schemaVersion": 2,
"icon": "icon.svg" The icon appears on your plugin's sidebar tab and on its row in the Plugins list. Foldnano checks it when the plugin is installed:
- At most 32 KB, with an
<svg>root that hasxmlns="http://www.w3.org/2000/svg"and aviewBox. - No scripts, event handlers,
foreignObject,<image>or animation elements. - No links to other files:
hrefandurl()may only point at an#idin the same file. - The path is relative to the zip root (
"icon.svg"or"assets/icon.svg") and ends in.svg.
Foldnano draws the icon in a single colour that follows the app theme, so design it as a monochrome glyph. A 24×24 stroke icon in the style of Lucide fits in well. Code-editor plugins with execution: "entrypoint" have no zip, so they can't use icon.
Citations
List the papers you would like people to cite when they use your plugin in their work:
"schemaVersion": 2,
"citations": [
{
"text": "Aksel, T., J. Navarro, E., Fong, N. and Douglas, S.M., Proceedings of the National Academy of Sciences, 2024, 121(48), p.e2406769121.",
"doi": "10.1073/pnas.2406769121"
},
{ "text": "My Plugin source code, version 1.0.", "url": "https://example.org/my-plugin" }
] Users see citations when they review your plugin before installing it, and when they click its name in My Plugins.
textis required and shown as plain text, at most 500 characters. Format the reference however you like.doiis a bare DOI such as10.1073/pnas.2406769121, withouthttps://doi.org/. Foldnano builds the link.urlis anhttps://link for work without a DOI. Setdoiorurl, not both; both are optional.- List at most 10 references.
Code-editor execution modes
execution: "input"
Use this when your plugin has a fixed implementation but wants user-authored code-like text as input. In this mode, entrypoint or entrypointUrl is the real code that runs, and the editor buffer is available through ctx.get_side_panel_code().
execution: "entrypoint"
Use this when the editor buffer is the full Python source of truth. In this mode, the host executes the current editor buffer as pluginCode for that run. This is the only case where a manifest may set inline entrypoint instead of entrypointUrl. The value is a placeholder never actually run, since the real source is the editor buffer. Such a manifest must not also set entrypointUrl. Minimal manifest shape:
{
"schemaVersion": 2,
"id": "my-script-plugin",
"name": "My Script Plugin",
"version": "1.0.0",
"format": "foldnano_edit",
"applyStrategy": "replayMutationLog",
"entrypoint": "def run(ctx):\n raise RuntimeError('Editor contents execute at runtime')\n",
"sidePanel": {
"type": "codeEditor",
"title": "My Script Plugin",
"icon": "code",
"codeEditor": {
"filename": "script.py",
"initialCode": "from foldnano import PluginContext\n\n\ndef run(ctx: PluginContext) -> None:\n pass\n",
"execution": "entrypoint"
}
}
} Next
The ctx your run(ctx) receives is documented in the Context API Reference.