Skip to content

Plugin manifest (plugin.json) β€” A.3 reference ​

Every Phlix plugin ships a plugin.json at the root of its package. This file is the source of truth for the loader (A.4), the admin UI (A.5), and the signature verifier (A.7+). A.3 defines:

  • The schema β€” docs/plugins/manifest.schema.json (JSON Schema draft 2020-12). IDEs and CI lint manifests against this file.
  • The parser β€” Phlix\Shared\Plugin\Manifest, an immutable PHP value object (shipped in the detain/phlix-shared Composer package). Parses plugin.json into typed properties. The validator (Phlix\Plugins\Manifest\ManifestSchema) stays in phlix-server and emits Phlix\Shared\Plugin\ManifestValidationError instances. The legacy Phlix\Plugins\Manifest and Phlix\Plugins\ManifestValidationError FQCNs remain available as deprecated aliases through 0.11.x.

Scope reminder. A.3 ships the spec and the parser only. The loader, sandbox, signature verification, and event-alias→FQCN resolution all live in A.4.

Canonical example ​

json
{
    "name": "phlix-plugin-lastfm",
    "version": "1.0.0",
    "phlix_min_server_version": "0.10.0",
    "type": "scrobbler",
    "entry": "Phlix\\Plugins\\Lastfm\\Plugin",
    "events": ["phlix.playback.started", "phlix.playback.stopped"],
    "settings": {
        "api_key": { "type": "string", "required": true, "secret": true },
        "api_secret": { "type": "string", "required": true, "secret": true }
    },
    "signature": "sha256:..."
}

This is the same example as PHLIX_EXPANSION_PLAN.md Β§5 β€” keep them in sync. The two valid-*.json fixtures under tests/Fixtures/Plugins/ are minimal, working manifests you can copy to bootstrap a new plugin.

Field reference ​

Required ​

FieldTypeConstraints
namestringKebab-case. Must start with phlix-plugin-. Max 64 chars. Regex ^phlix-plugin-[a-z0-9][a-z0-9-]*$.
versionstringPlugin semver. Regex ^\d+\.\d+\.\d+(?:[-+][A-Za-z0-9.\-]+)?$.
phlix_min_server_versionstringMinimum supported Phlix server semver. Same regex.
typestringOne of the eleven values below.
entrystringFully-qualified entry class name. Regex ^[A-Z][A-Za-z0-9_]*(?:\\[A-Z][A-Za-z0-9_]*)+$.

Plugin types (type enum) ​

Mirrors PHLIX_EXPANSION_PLAN.md Β§5 and Phlix\Shared\Plugin\ManifestType:

ValuePurpose
metadata-providerPulls media metadata (e.g. an alternative TMDb).
subtitle-providerDownloads or generates subtitles.
auth-providerAuthenticates users (OIDC, LDAP, SSO, …).
library-typeAdds a brand-new library kind.
notifierSends notifications (push, email, chat).
scrobblerReports playback to an external service.
tunerLive-TV tuner backend.
transcoder-hookAdjusts the transcoder pipeline.
ui-themeRestyles the web portal.
arr-integrationIntegrates with *arr stack (Sonarr, Radarr).
analytics-sinkExports analytics to external systems.

Optional ​

events ​

An array of manifest aliases. Each alias is a dotted string of the form phlix.<area>.<verb>(.<sub>)* (regex ^phlix\.[a-z]+(?:\.[a-z]+)*$). The canonical alias list lives in docs/dev/event-reference.md. The A.4 loader resolves these aliases to event FQCNs at install time.

settings ​

A keyed object describing the settings the plugin exposes to operators via the admin UI. Each value is an object:

KeyTypeNotes
typestringOne of string, int, bool, float, array. Required.
requiredbooleanWhether the operator must set a value. Default false.
secretbooleanHides the value in UI/logs and stores it encrypted. Default false.
defaultanyDefault value when the operator leaves the field blank.

signature ​

Either null (unsigned plugin) or a string matching ^sha256:[0-9a-f]{64}$. Signature verification lives in A.7; A.3 only validates the format.

Parsing and validating in PHP ​

php
use Phlix\Plugins\Manifest\ManifestSchema;
use Phlix\Shared\Plugin\Manifest;
use Phlix\Shared\Plugin\ManifestType;
use RuntimeException;

try {
    $manifest = Manifest::fromJson(file_get_contents('plugin.json'));
} catch (RuntimeException $e) {
    // Hard parse failure β€” the file isn't even valid JSON.
    throw $e;
}

$errors = (new ManifestSchema())->validate($manifest);
if ($errors !== []) {
    foreach ($errors as $error) {
        printf("[%s] %s β€” %s\n", $error->code, $error->field, $error->message);
    }
    return;
}

assert($manifest->manifestType() instanceof ManifestType);

Error codes returned by validate() ​

The code field on ManifestValidationError mirrors the JSON Schema constraint that failed (required, pattern, enum, type, additionalProperties, …), plus a Phlix-specific unknown_field code for top-level keys that the schema does not allow.

Authoring checklist ​

  1. Copy tests/Fixtures/Plugins/valid-lastfm.json (scrobbler) or valid-oidc.json (auth provider) into your plugin's root.
  2. Replace name, entry, events, and settings to match your plugin.
  3. Run npx ajv-cli validate -s docs/plugins/manifest.schema.json -d plugin.json (optional β€” the loader will validate again at install time).
  4. Commit the manifest alongside your PHP entry class. Wiring the plugin into the runtime arrives in A.4.

See also ​

BSD-3-Clause