In this page
Reference for the list of available plugins that the Library reads from GitHub: where it lives, the format of the file, how the Library saves and checks it, and what the trust labels mean.
Where this lives in the code
src/MediaEngine.Api/Services/Plugins/PluginDirectoryService.cs(saved copy, checks, switch, install preview)src/MediaEngine.Api/Services/Plugins/PluginDirectoryParser.cs(reads the file)src/MediaEngine.Api/Endpoints/PluginEndpoints.cs(/plugins/directory)docs/reference/approved-plugins.json(the list itself)
How the Library uses the list
Settings > Plugins > Available plugins shows the plugin directory. The Library keeps a saved copy of the list in its data folder (plugin-directory/directory.json) and shows that copy. It goes to GitHub only:
- when an administrator opens Available plugins and the last check was more than 24 hours ago (or there is no saved copy yet), or
- when an administrator presses Check now.
There is no background schedule. The request is a plain GET of the list’s address with the Library’s standard User-Agent; nothing about the person, the Library or the server is sent. A check that fails never replaces the saved copy: the page keeps showing it with the date it is from.
The switch on the section turns the lookup off (plugin_catalog.enabled in config/core.json). With the switch off no request is made at all; the page still shows the saved copy, if there is one, and says the directory is switched off.
The address is configured in config/core.json:
{ "plugin_catalog": { "enabled": true, "approved_plugins_url": "https://raw.githubusercontent.com/Tuvima/tuvima-library/main/docs/reference/approved-plugins.json" }}The Library only accepts HTTPS addresses on raw.githubusercontent.com or github.com. The directory is read-only: reading the list never downloads or installs plugin code.
List format (version 2)
{ "schema_version": "2.0", "updated_at": "2026-10-20T00:00:00Z", "plugins": [ { "id": "example.sample-plugin", "name": "Sample Plugin", "author": "Example", "description": "Short user-facing summary.", "version": "0.1.0", "kind": "declarative", "trust_level": "community", "permissions": ["network.http"], "network": [ { "host": "api.example.com", "sends": "Searches for titles by name" } ], "capabilities": ["metadata-source"], "minimum_library_version": "1.0.0", "maximum_library_version": "1.9.99", "supported_platforms": ["windows", "linux"], "repository_url": "https://github.com/example/tuvima-sample-plugin", "release_url": "https://github.com/example/tuvima-sample-plugin/releases/tag/v0.1.0", "package_url": "https://github.com/example/tuvima-sample-plugin/releases/download/v0.1.0/sample-plugin.zip", "sha256": "64-character lowercase hex checksum", "signature": "base64 signature over the package (verified and official plugins)", "added_at": "2026-10-20T00:00:00Z" } ]}Top level:
| Field | Meaning |
|---|---|
schema_version | The format version. This page describes 2.x; a file with another major version is refused and the saved copy stays. |
updated_at | When the list was last changed (UTC). |
plugins | The listed plugins. |
Each plugin:
| Field | Required | Meaning |
|---|---|---|
id | yes | Unique plugin id, with no spaces (the same id as in the plugin’s manifest). |
name | yes | Name shown to people. |
kind | yes | How it runs: declarative (JSON only), service (a separate program), sandboxed (WebAssembly) or official (ships with the Library). |
author, description, version | no | Shown on the entry. |
trust_level | no | official, verified or community. Missing or unknown counts as community (not verified). |
permissions | no | The permission ids the plugin declares, for example network.http or media.read. The Library words them for people (“Connects to the internet”). |
network | no | Where the plugin sends data and what it sends there: a list of { "host", "sends" } (optionally max_requests_per_minute), written exactly as in the plugin’s manifest (see Building a plugin). When present, the approval step shows one “Contacts host: what it sends” line per entry instead of “Connects to the internet”, and the Engine returns the list as network. A list that gives network entries must also give network.http in permissions, and each entry must be a valid host with a plain sentence; an entry that breaks this is left out and counted, never shown as a smaller list. A plugin that has network.http but no network list (older lists) is still shown, described only as connecting to the internet. |
capabilities | no | Short names of what the plugin provides. |
minimum_library_version, maximum_library_version | no | The Library versions it works with. The minimum defaults to 1.0.0; no maximum means no upper limit. |
supported_platforms | no | Runtime ids (win-x64) or OS families (windows, linux, macos). Empty means every platform. |
repository_url, release_url, package_url | no | HTTPS addresses only; anything else is ignored. |
sha256 | no | Checksum of the package: 64 lowercase hex characters, otherwise ignored. A plugin without one cannot be installed. |
signature | no | Tuvima’s signature over the exact package bytes (ECDSA P-256 with SHA-256, base64, 64 bytes). Required to install a verified or official plugin. Never sent to the Dashboard. |
added_at | no | When the plugin was added to the list (UTC). |
The Library reads the list defensively. An entry that cannot be understood (not an object, no id or name, an unknown or missing kind, or an id already used) is left out and counted; the page says how many entries could not be read. One bad entry never hides the rest of the list.
Blocklist
A list may carry an optional top-level blocklist array for plugins Tuvima has pulled:
"blocklist": [ { "id": "vendor.plugin", "reason": "Why it was pulled.", "versions": ["1.0.0"] }]versions is optional; without it every version is blocked. A blocked plugin that is installed is switched off, the plugin card shows Blocked: <reason>, and it cannot be switched on. The blocklist comes from the saved copy of the list, so it keeps working offline and takes effect after the next directory check.
Trust labels
The label on each entry comes from trust_level:
- Official: built by Tuvima and shipped with the Library.
- Verified: Tuvima checked the publisher and reviewed what the plugin asks for.
- Not verified: listed, but Tuvima has not checked it. The entry shows the warning “Tuvima hasn’t checked this plugin. Only install plugins from people you trust.” beside it, and the same words appear in the approval step before an install, together with a line saying how that kind of plugin runs.
Only Tuvima can set verified: a submission that claims it is treated as a request, and the mark is added to the list after the review.
What is new
The Library remembers which plugin ids it has already shown. A listed plugin that was not in the list the last time Available plugins was opened counts as new, and the page shows a quiet “N new plugins available” line. Opening the section marks the current list as seen.
Install
An Install button appears on an entry when the Engine reports install_supported: true. The install work is built, but it stays switched off (install_supported: false, and the install action answers 400) until plugin keys are encrypted on disk. It opens the same approval step as switching a plugin on, built from what the list declares, and only after the person confirms does the Engine download the package. An installed plugin whose listed version is newer shows Update to x.y.z with the same step.
What the Engine checks, in order, and refuses with a plain sentence if any fails:
- The download address is on
github.comorgithubusercontent.comover https, and still is after any redirect. The package may be at most 64 MB (256 MB unpacked, 2000 files). - The package’s SHA-256 equals the list’s
sha256. A list without a checksum cannot be installed. - For
verifiedandofficialentries, the package’ssignatureverifies against a Tuvima release key built into the Library. With no key built in nothing verifies, so these entries are refused rather than trusted.communityentries are checksum-only and always show the not-verified warning. A plugin that runs inside the Library (kindofficial) is never installed unless it is signed. - The package unpacks safely (no path outside its folder, no links, size counted as it is written), holds a
plugin.json, and that file’s id, version and kind equal the list’s and ask for no permission or address the list does not name. Sandboxed plugins are not installable yet. - The plugin is not on the blocklist and fits this Library version and platform.
Installing never switches a plugin on. The Library records how the plugin was installed (trust level and the checksum of its plugin.json) beside, not inside, the plugins folder; a plugin counts as Verified or Not verified only while that record matches, otherwise it is a private plugin. An update replaces the files and keeps the plugin’s settings and saved records; because the approval is tied to what the plugin may do, an update that asks for more leaves the plugin switched off with “needs approval” until an administrator approves it again. A plugin added to the plugins folder by hand is never replaced by the directory. Removing a plugin first lists anything that would have no other source and says that what the plugin already saved stays.
Submit your plugin
See Submit your plugin.
Plain English summary: Tuvima keeps a public list of plugins on GitHub. The Library saves a copy, checks it at most once a day or when an administrator asks, and labels every entry as Official, Verified or Not verified. Nothing is installed by reading the list.