AviatoAviato
DeveloperPlugins

Plugin Manifest

The full plugin.json field reference enforced by Aviato.

plugin.json is the single source of truth Aviato uses to discover, validate, and configure a plugin. Aviato parses it on startup, validates it against a strict Zod schema, and refuses to start any plugin whose manifest fails validation. Errors surface in the admin UI as Zod issue paths, so the failing field is always obvious.

Identity

FieldTypeRequiredNotes
idstringyesLowercase kebab-case (/^[a-z0-9-]+$/). Must be unique across all installed plugins. By convention, plugins authored by the Aviato team are prefixed aviato-.
namestringyesDisplay name shown in the admin UI.
versionstringyesSemver (/^\d+\.\d+\.\d+/). Bump on breaking manifest or capability changes.
descriptionstringyesOne-sentence summary, surfaced in the plugin list and the Add Library wizard.
authorstringyesAuthor or organization name.
licensestringyesSPDX-style identifier (MIT, Apache-2.0, etc.).
repositorystring (URL)noSource repository link.
homepagestring (URL)noProject homepage.

Runtime

FieldTypeRequiredNotes
engine'bun' | 'node' | 'python' | 'binary'yesProcess runtime. Aviato spawns the plugin with the matching launcher. binary runs the file at entry directly.
entrystringyesPath to the entry script (or binary), relative to the plugin directory. Typically src/index.ts for bun plugins.
aviato.minVersionstringyesMinimum Aviato server version. Aviato refuses to load a plugin whose minVersion is newer than the running server.
dependenciesstring[]noIDs of other plugins this plugin requires. Aviato refuses to start the plugin if any dependency is missing or disabled.

Declared work

capabilities and subscriptions together describe everything a plugin can do. A plugin with neither does nothing.

capabilities

"capabilities": ["filesystem", "indexer", "library"]

string[], required, may be empty. Allowed values:

ValueAdds
filesystemfilesystem.* RPC handlers. See filesystem.mdx.
indexerindexer.* RPC handlers. See indexer.mdx.
librarylibrary.* RPC handlers and a library type. See library.mdx.

Each capability declared here must have a matching capabilityConfig.<name> block when that capability defines one.

A plugin with capabilities: [] is a pure subscriber: it does no RPC work, only reacts to hooks, events, and views. The bundled embedded-metadata, external-metadata, external-subtitles, posters, and thumbnails plugins all work this way.

mediaTypes

Required for library and indexer capabilities. Aviato uses it to short-list which plugins to consider for a given library type.

Two equivalent forms:

// Simple list
"mediaTypes": ["movies", "tv"]
// Per-type config flags
"mediaTypes": {
  "tv":     { "episodic": true },
  "movies": {}
}

Currently understood per-type flags:

FlagDefaultEffect
episodicfalseEnables player "End of current episode" sleep timer (and future auto-advance).
seekabletrueWhen false, hides player seek controls.
idleProtectiontrueWhen false, suppresses the "Are you still there?" prompt.

Unrecognized fields are accepted to keep the schema forward-compatible: a plugin built against a newer SDK can still load on an older Aviato server.

capabilityConfig

Per-capability static configuration. Keys must match declared capabilities.

"capabilityConfig": {
  "filesystem": { "supportsWatch": true, "supportsLocalFileAccess": true, "supportsWrite": true },
  "library":    { "bundling": { "strategy": "per-folder" }, "paths": [...] }
}

filesystem block:

FieldTypeNotes
supportsWatchbooleanPlugin implements filesystem.watch.
supportsLocalFileAccessbooleanFiles exposed by this plugin can be opened with a local path (via filesystem.getLocalPath).
supportsWritebooleanPlugin can persist user-uploaded files.

library block: see library.mdx for the full breakdown.

indexer has no static config; its declared mediaTypes and runtime behavior are enough.

subscriptions

Reactive bindings to events, hooks, and views.

"subscriptions": {
  "events": ["library.item.updated", "library.item.removed"],
  "hooks": [
    { "name": "pipeline.probe.afterProcess", "order": 40, "requiresLocalFile": true }
  ],
  "views": ["ui.item.detail.actions"]
}

Names match ^[a-z0-9*]+(\.[a-z0-9*]+)*$. * is a single-segment wildcard: library.item.* matches library.item.added but not library.item.added.bulk. Hook entries are objects with the following fields:

FieldTypeNotes
namestringHook name. Required.
orderint 0–100Execution order within the chain. Default 50.
requiresLocalFilebooleanThe handler needs the bundle's primary file readable from a real path on disk. Default false. See hooks.mdx for orchestrator behavior. Planned — schema and registry persist the flag; ingestion-pipeline materialization is not yet implemented.

Full catalog: hooks.mdx.

User configuration

configuration[] declares user-editable settings rendered as a form in the admin UI.

"configuration": [
  { "key": "tmdbApiKey", "label": "TMDb API Key", "input": "text", "required": false },
  { "key": "language",   "label": "Metadata language", "input": "text", "default": "en-US" }
]

See configuration.mdx for the full input vocabulary and how saved values reach the plugin process.

Rate limiting

rateLimit lets a plugin cooperate with upstream API quotas. Aviato wraps every inbound RPC call to the plugin in a per-plugin limiter.

"rateLimit": {
  "maxConcurrency": 4,
  "requests": [
    { "max": 40, "window": "10s" }
  ]
}
FieldTypeNotes
maxConcurrencypositive intMax in-flight RPCs at once. Defaults to Infinity.
requests[]{ max, window }[]Sliding-window quotas. window is <n>[smh], for example "10s", "1m", "1h". Multiple entries AND together.

See the rate-limit section of indexer.mdx for guidance on choosing values.

Worked examples

Library plugin (movies)

{
  "id": "aviato-library-movies",
  "name": "Movies Library",
  "version": "1.0.0",
  "description": "Movie library with rich metadata, filtering, and sorting",
  "author": "Aviato",
  "license": "MIT",
  "engine": "bun",
  "entry": "src/index.ts",
  "aviato": { "minVersion": "0.1.0" },

  "capabilities": ["library"],
  "mediaTypes": ["movies"],

  "capabilityConfig": {
    "library": {
      "bundling": { "strategy": "per-folder" },
      "paths": [
        { "extensions": ["system:video"] },
        { "type": "trailer", "rules": ["*trailer*", "trailers/**/*"], "extensions": ["system:video"] },
        { "type": "extra",   "rules": ["extras/**/*", "featurettes/**/*"], "extensions": ["system:video"] },
        { "type": "companion", "extensions": ["system:text", ".nfo", ".jpg", ".srt"] }
      ]
    }
  }
}

Filesystem plugin

{
  "id": "aviato-fs-local",
  "name": "Local Filesystem",
  "version": "1.0.0",
  "description": "Add local files to your libraries",
  "author": "Aviato",
  "license": "MIT",
  "engine": "bun",
  "entry": "src/index.ts",
  "aviato": { "minVersion": "0.1.0" },

  "capabilities": ["filesystem"],
  "mediaTypes": ["movies", "tv", "music", "photos", "books"],

  "configuration": [
    { "key": "excludePatterns", "label": "Exclude patterns", "description": "Glob patterns to exclude from scanning", "input": "string-list" },
    { "key": "watchForChanges", "label": "Watch for changes", "input": "toggle", "default": true }
  ],

  "capabilityConfig": {
    "filesystem": {
      "supportsWatch": true,
      "supportsLocalFileAccess": true,
      "supportsWrite": true
    }
  }
}

Pure subscriber (hook plugin)

{
  "id": "aviato-thumbnails",
  "name": "Thumbnail Generator",
  "version": "0.1.0",
  "description": "Generates thumbnails from media files using FFmpeg when no artwork exists",
  "author": "Aviato",
  "license": "MIT",
  "engine": "bun",
  "entry": "src/index.ts",
  "aviato": { "minVersion": "0.1.0" },

  "capabilities": [],
  "subscriptions": {
    "hooks": [
      { "name": "pipeline.probe.afterProcess", "order": 90, "requiresLocalFile": true }
    ]
  }
}

requiresLocalFile: true here declares that this hook shells out to FFmpeg against a real on-disk path, so the file must be materialized locally (via the source filesystem plugin's getLocalPath, or by fetching to a scratch directory) before the hook fires. The orchestrator wiring that consumes this flag is planned but not yet implemented; declaring it now is forward-compatible.

Validation rules

The schema is strict: unknown top-level fields cause validation to fail. The full enforced rules:

  • id is lowercase kebab-case.
  • version matches <n>.<n>.<n> (extra suffix allowed).
  • capabilities is an array of 'filesystem' | 'indexer' | 'library'. Empty is valid (pure subscriber).
  • engine must be one of the four runtimes.
  • Subscription names match ^[a-z0-9*]+(\.[a-z0-9*]+)*$.
  • Hook order is an integer in [0, 100].
  • Hook requiresLocalFile is a boolean (default false).
  • rateLimit.requests[].window matches \d+[smh].

A failed validation surfaces in the admin UI as a Zod error path (for example capabilities.0: ..., aviato.minVersion: ...) so the cause is obvious.

See also

On this page