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 exampleauthor.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 asneotavern: "^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.publisherandsignature— package signing.keyIdis theed25519:<hex>fingerprint of the signing public key, andsignatureis 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.