Skip to content

Manifest & config reference ​

plugin.json ​

Read before your module is imported — the contract that decides whether your code runs.

ts
interface PluginManifest {
  id: string;
  name: string;
  version: string;
  entry: string;
  capabilities: PluginCapabilityName[];
  description?: string;
  homepage?: string;
  license?: string;
}
FieldNotes
idReverse-domain identifier. Only [A-Za-z0-9._-].
versionYour plugin's semver.
entryModule path, relative to the plugin directory, default-exporting the plugin object.
capabilitiesWhat the plugin uses from the host. Using an undeclared capability aborts loading.
description / homepage / licenseMetadata for catalogs and the admin UI.

Capabilities ​

Capabilities are the only access gate — there is no separate permission list.

ts
type PluginCapabilityName =
  | "metadataProvider" | "providerAccess" | "subtitleProvider" | "mediaAnalyzer"
  | "mediaRead" | "metadataRead" | "artifactsRead" | "artifactsWrite" | "ffmpegRun"
  | "markers" | "jobs" | "eventHandler" | "storage"
  | "httpRoute" | "accessPolicy" | "notification" | "httpFetch";
CapabilityUnlocks
metadataProviderhost.providers.register
providerAccesshost.providers.list/search/getDetails/getSeasonDetails/resolveDetails/discover/getGenres
subtitleProviderhost.subtitles.register
mediaAnalyzerhost.media.registerAnalyzer
mediaReadhost.media.get/getRevision/listEpisodeFilesBySeason/listAllMediaFiles
metadataReadhost.metadata.get/findByExternalId/findManyByExternalIds
artifactsReadhost.artifacts.list
artifactsWritehost.artifacts.write/deleteByKind
ffmpegRunhost.ffmpeg.runAnalyse/extractFrame/extractSprite
markershost.markers
jobshost.jobs and host.tasks
eventHandlerhost.events.on, host.hooks.*, host.realtime.*
storagehost.storage
httpRoutehost.routes.register
accessPolicyhost.access.register
notificationhost.notifications.create
httpFetchhost.http.fetch

Declare the narrowest set you can — capabilities are shown to the admin at install time and enforced at runtime.

Config schema ​

Configuration is declared with defineConfig next to the entry (see Configuration):

ts
import { defineConfig, field, type InferConfig } from "@reelvault/sdk/plugin";

const config = defineConfig({
  apiKey: field.secret({ label: "API key", required: true, default: "" }),
  language: field.string({ label: "Language", default: "en-US" }),
  region: field.select({ label: "Region", options: [{ label: "US", value: "us" }], default: "us" }),
  maxResults: field.number({ label: "Max results", default: 20, min: 1, max: 50 }),
  includeAdult: field.boolean({ label: "Include adult", default: false }),
});

Each builder returns a typed field. The admin form descriptors (PluginConfigField[]) are derived from the specs, and host.config is the parsed, typed result.

BuilderValue typeExtras
field.stringstringdefault, required, pattern, minLength, maxLength
field.secretstringdefault, required
field.numbernumberdefault, min, max, step
field.booleanbooleandefault
field.selectunion of options[].valueoptions, default

A field with a default is always present after parsing; without one it can be undefined.

UI manifest (ui.json) ​

ts
interface PluginUiManifest {
  name: string;
  version: string;
  defaultLocale?: string;
  entry: string;                                  // required only for custom-element tags
  pages?: PluginPageContribution[];
  dialogs?: PluginDialogContribution[];
  tabs?: Partial<Record<"details" | "admin-plugin" | "settings", PluginTabContribution[]>>;
  slots?: Partial<Record<PluginSlotName, PluginSlotContribution[]>>;
  playbackPreRoll?: { endpoint: string };
  searchProvider?: { endpoint: string; requestEndpoint?: string; itemPage?: string };
}
ts
type PluginSlotName =
  | "root-floating-overlay" | "player-footer" | "dashboard-section"
  | "details-action-bar" | "details-dropdown" | "media-file-card-actions"
  | "admin-sidebar-plugin-section" | "navbar-profile-menu";

type PluginLocalizedText = string | Record<string, string>;

A surface renders through exactly one of tag, schema or schemaRef. Full details in Frontend (ui.json).

Catalog entry ​

A catalog is any static URL serving reelvault-catalog.json with apiVersion: 1.

ts
interface PluginCatalogEntry {
  id: string;
  name: string;
  version: string;       // latest release, semver
  category: "metadata" | "subtitles" | "automation" | "integrations" | "ui" | "other";
  downloadUrl: string;   // https only
  checksum: string;      // "sha256-" + 64 hex chars
  description?: string;
  homepage?: string;     // https only
  iconUrl?: string;      // https only
  changelog?: string;
  date?: string;
  capabilities?: string[];
  versions?: PluginCatalogVersionEntry[]; // older installable releases, newest first
}

interface PluginCatalogVersionEntry {
  version: string;       // semver, unique within the entry
  downloadUrl: string;   // https only
  checksum: string;      // "sha256-" + 64 hex chars
  date?: string;
  changelog?: string;
}

Top-level fields describe the latest release; versions[] keeps older installable archives (max 50 per entry). Installing a specific version is the update/rollback path. See Publishing & catalogs.

Versions ​

ConstantImportValue
PLUGIN_UI_PROTOCOL_VERSION@reelvault/sdk/plugin / @reelvault/sdk/ui2

Released under the GNU GPL v3.