Skip to main content

Plugin Manifest

The plugin manifest (plugin.json) is the single source of truth for a plugin: identity, entry points, requested permissions, and declared capabilities.

Package Layout​

A .stplugin package is a ZIP archive that contains plugin.json at the root, the entry files it references, and any assets. The host validates the archive before anything is installed: path traversal, symlinks, executable payloads, and size limits are all rejected.

Manifest Fields​

{
"id": "author.plugin-name",
"name": "Plugin Name",
"version": "1.0.0",
"apiVersion": 2,
"engines": { "neotavern": "^0.1.0" },
"frontend": "dist/frontend.js",
"backend": "dist/backend.mjs",
"styles": "dist/plugin.css",
"permissions": ["chat.read", "ui.messageActions", "network:api.example.com"],
"i18n": { "ru": "locales/ru.json", "de": "locales/de.json" }
}

The core fields are:

  • id — reverse-DNS identifier, for example author.plugin-name. It is unique across all installed plugins and stable across updates.
  • name — human-readable name shown in the Plugin Manager.
  • version — semantic version (major.minor.patch). It feeds version comparisons and cache busting.
  • apiVersion — the SDK API version the plugin targets. The current version is 3; version 2 remains the default until the new runtime lands in production.
  • engines — compatibility constraints such as neotavern: "^0.1.0".
  • frontend — relative path to the browser ESM entry.
  • backend — relative path to the Node.js ESM entry.
  • styles — optional plugin stylesheet.
  • i18n — locale code to relative path of translation JSON files.

Permissions​

The permissions array is the legacy flat list from SDK v2. New manifests should declare scoped capabilities instead through requiredCapabilities and optionalCapabilities:

{
"requiredCapabilities": [
{ "name": "chat.read" },
{ "name": "network", "scope": "api.example.com" }
],
"optionalCapabilities": [{ "name": "lorebook.read" }]
}

requiredCapabilities are capabilities the plugin cannot work without; optionalCapabilities are ones it can degrade without. The user confirms every requested capability at install time. Adding new permissions in an update requires re-consent — see Permissions.

Legacy Entry Points​

{
"legacy": {
"frontend": "legacy/main-window.js",
"backend": "legacy/server.mjs"
}
}

The legacy block points at trusted compatibility entries for existing SillyTavern extensions. Packages using either entry must request the legacy.trusted permission, and the UI shows a stronger warning during consent. Safe mode never loads legacy entry points. See Sandboxing for how this differs from native plugins.

OAuth Clients​

Plugins that connect to an external service can declare public OAuth 2.0 clients using authorization-code flow with PKCE:

{
"authClients": [
{
"serviceId": "com.example.idp",
"name": "Example IdP",
"authorizationUrl": "https://idp.example.com/oauth/authorize",
"tokenUrl": "https://idp.example.com/oauth/token",
"clientId": "neotavern-author.plugin-name",
"scopes": ["profile.read"]
}
]
}

Only public clients are allowed: clientSecret is forbidden because plugin code runs in a sandbox. Endpoints must be HTTPS, with a plain-HTTP loopback exception for local identity providers during development. Changing a descriptor requires reinstalling the package.

Worker and Signing Fields​

Advanced manifests can declare additional modules:

  • workers — package-relative entry modules the plugin may spawn as isolated compute workers. Spawning an undeclared entry is rejected.
  • publisher and signature — package signing. keyId is the ed25519:<hex> fingerprint of the signing public key, and signature is the base64 Ed25519 signature over the canonical manifest. These are set by the plugin build tool, never hand-written.

The validateManifest function in the SDK checks every field, and the generated Plugin SDK reference documents the exact PluginManifest type.