This repository hosts the index of the official Gameyfin plugin marketplace. Every Gameyfin instance knows it out of the box and shows its plugins in Administration → Plugins → Marketplace.
The repository only contains metadata. The plugin JARs stay with their developers (e.g. as GitHub release assets), and each release in the index pins the SHA-512 checksum of its JAR. Gameyfin verifies that checksum before it installs anything.
- Repository layout
- Submitting a plugin (plugin developers)
- Manifest reference
- Reviewing submissions (marketplace maintainers)
- Hosting a custom marketplace
- Testing locally
- The download must not exceed 512 MB, and its SHA-512 checksum must match
sha512sum. Plugin-IdandPlugin-Versionin the JAR'sMANIFEST.MFmust match the plugin ID and release version.Plugin-Requiresmust be compatible with the running Gameyfin version.- Trust levels:
- Unsigned JARs from a marketplace are classified as third-party. They are installed disabled and an administrator has to enable them explicitly.
- JARs signed with a key other than the Gameyfin key are classified as untrusted and can't be enabled at all. This is why third-party plugins must be published unsigned.
- Plugins signed by the Gameyfin team are official and are enabled automatically.
- If a plugin that is already installed gets reinstalled from a different source, it is disabled again unless it is official.
| Path | Description |
|---|---|
plugins/<plugin-id>.json |
One manifest per plugin. This is the only thing contributors edit. |
plugins.json |
Generated index consumed by Gameyfin. Never edit it manually or in a PR. |
schema/plugin-manifest.schema.json |
JSON schema for editor auto-completion. scripts/validate.py is authoritative. |
examples/ |
Example manifest. |
scripts/marketplace.py |
Shared validation and index logic (Python 3.10+, standard library only). |
scripts/validate.py |
Validates manifests, downloads and inspects releases. |
scripts/build_index.py |
Generates plugins.json. |
.github/workflows/ |
CI: PR validation, index publishing, weekly re-check. |
The JAR's META-INF/MANIFEST.MF must contain at least:
Plugin-Id: com.example.gameyfin.awesome-metadata
Plugin-Version: 1.1.0
Plugin-Class: com.example.gameyfin.awesome.AwesomeMetadataPlugin
Plugin-Requires: >=2.5.1
Plugin-Name: Awesome Metadata
Plugin-Description: Fetches game metadata from the Awesome Games Database.
Plugin-Author: Jane Doe
Plugin-License: MIT
Plugin-Url: https://github.com/janedoe/gameyfin-awesome-metadata
Plugin-Id: use a reverse domain name or a GitHub-based ID that you own, e.g.io.github.janedoe.awesome-metadata. IDs starting withorg.gameyfin.are reserved for the Gameyfin team.Plugin-Version: must be a semantic version and has to be increased for every release.Plugin-Requires: the Gameyfin versions the plugin works with, e.g.>=2.5.1or>=2.5.1 & <3.0.0. Gameyfin treats a plain version such as2.5.1as>=2.5.1. The marketplace requires this attribute to be set.- Do not sign the JAR (see What Gameyfin checks).
The plugins in the Gameyfin repository are good examples of how to structure and build a plugin.
Upload the JAR to a permanent, immutable HTTPS URL. A GitHub release asset is ideal:
https://github.com/<you>/<repo>/releases/download/v1.1.0/awesome-metadata-1.1.0.jar.
Never replace the file behind a published URL. The checksum would no longer match, installation would fail for everyone, and the weekly check would report the plugin. Publish a new version instead.
Compute the checksum:
sha512sum awesome-metadata-1.1.0.jar # Linux
shasum -a 512 awesome-metadata-1.1.0.jar # macOS
(Get-FileHash -Algorithm SHA512 .\awesome-metadata-1.1.0.jar).Hash.ToLower() # PowerShell-
Fork this repository.
-
Create
plugins/<plugin-id>.json. Copyexamples/com.example.gameyfin.awesome-metadata.jsonand adjust it. -
Optionally, run the validation locally:
python3 scripts/validate.py plugins/<plugin-id>.json. -
Open a pull request against
mainand fill in the checklist. TheValidate plugin manifestscheck shows any problems in the job summary and as annotations on the changed file.Workflows of first-time contributors only run after a maintainer approves them.
Add a new entry to releases. Order doesn't matter, because the index lists the newest version first. Open a PR the
same way as for the initial submission.
- Already published releases are immutable: their
urlandsha512summust not change. You can still fixchangelog,date, orrequires, but CI doesn't re-check them against the JAR. - To yank a broken release, remove it from
releases. Instances that already installed it keep it, but nobody can install it from the marketplace anymore. - To remove the plugin from the marketplace, delete the manifest.
maintainers lists the GitHub accounts that are responsible for the plugin. This field is not published in
plugins.json. CI warns when a PR is opened by someone who isn't a maintainer of the plugin, and reviewers then make
sure that the plugin's maintainers agree with the change.
See schema/plugin-manifest.schema.json and the
example. Unknown fields are rejected.
| Field | Required | Description |
|---|---|---|
id |
✔ | Plugin ID ([A-Za-z0-9][A-Za-z0-9._-]{1,127}). Must equal Plugin-Id and the file name. |
name |
✔ | Display name (max. 100 characters). |
shortDescription |
Shown on the plugin card (max. 300 characters). | |
description |
✔ | Full description, Markdown supported. |
provider |
✔ | Author or organization. |
license |
✔ | License, preferably an SPDX identifier. |
projectUrl |
✔ | Homepage or source code (https). |
logo |
https URL of a PNG, JPEG, GIF, WebP or SVG image (max. 1 MB). Square images work best. | |
types |
✔ | Implemented extension points: GameMetadataProvider, DownloadProvider, GameyfinTask. |
tags |
Up to 10 lowercase tags ([a-z0-9][a-z0-9-]{0,29}). |
|
maintainers |
✔ | GitHub usernames (not published). |
releases |
✔ | List of releases (see below). |
| Release field | Required | Description |
|---|---|---|
version |
✔ | Semantic version, must equal Plugin-Version. |
date |
✔ | Release date: yyyy-MM-dd or an ISO-8601 date-time. |
requires |
✔ | Compatible Gameyfin versions, must equal Plugin-Requires (e.g. >=2.5.1). |
url |
✔ | Permanent https download URL of the JAR (max. 512 MB). |
sha512sum |
✔ | SHA-512 checksum of the JAR, 128 lowercase hex characters. |
changelog |
Changes in this release, Markdown supported. |
The generated plugins.json has the shape { "plugins": [ ... ] }. It contains the fields above except maintainers
and $schema. Plugins are sorted by ID and releases newest first.
CI covers the mechanical checks. The review is about trust. Before approving a PR:
- The PR only touches
plugins/<plugin-id>.json. Changes to anything else, such asscripts/or.github/, need extra scrutiny. - The
Validate plugin manifestscheck passed. Read its warnings: non-maintainer author, reserved ID, removed releases, changed maintainers. - New plugins:
- The source code is public and matches the plugin description.
- The license allows distribution.
- The submitter is plausibly the author.
- The ID doesn't impersonate another project.
- New releases: the JAR was built from the public source. Ideally it was built by CI and attached to a tagged release in the plugin's repository.
- Updates by non-maintainers: one of the plugin's maintainers approves the change in the PR.
- Prefer squash merges, so
mainkeeps one commit per change.
When a plugin turns out to be malicious, delete its manifest immediately. The index is updated within minutes, and Gameyfin instances see the change within an hour because of the cache. Already installed copies are not removed automatically, so announce the incident as well.
When the scheduled check opens an issue labeled marketplace-check, contact the plugin maintainers. If a release can't
be fixed, remove it.
Anyone can host a marketplace. Administrators add it in Administration → Plugins → Marketplace → Marketplaces. Gameyfin accepts:
| URL | Resolved index |
|---|---|
https://github.com/<owner>/<repo> |
raw.githubusercontent.com/<owner>/<repo>/HEAD/plugins.json |
https://github.com/<owner>/<repo>/tree/<ref>/<dir> |
<dir>/plugins.json at <ref> |
https://github.com/<owner>/<repo>/blob/<ref>/<file> |
<file> at <ref> |
any URL ending in .json |
used as-is |
| any other URL | treated as a directory: <url>/plugins.json |
Additional rules:
- URLs without a scheme get
https://. http://andfile:URLs are only allowed whengameyfin.plugins.marketplace.allow-insecure-urls=true. Never enable this in production.- URLs containing credentials are rejected.
- The index is limited to 5 MB.
- In a custom index, relative release and logo URLs are resolved against the index URL.
sha512summay also be.sha512, meaning the release URL with its extension replaced by.sha512, or the URL of a checksum file. This repository always requires the literal checksum.
The easiest way to run a custom marketplace is to fork this template and use the same scripts and workflows.
Other marketplace-related Gameyfin properties:
| Property | Default |
|---|---|
gameyfin.plugins.marketplace.cache-ttl |
1h |
gameyfin.plugins.marketplace.connect-timeout |
10s |
gameyfin.plugins.marketplace.request-timeout |
2m |
Requirements: Python 3.10+ and git. No packages are needed.
# Structural checks of all manifests (no downloads)
python3 scripts/validate.py --all --offline
# Full check of one manifest (downloads and inspects every release)
python3 scripts/validate.py plugins/com.example.gameyfin.awesome-metadata.json
# Same checks as the PR workflow (only new/changed releases compared to main)
python3 scripts/validate.py --base-ref origin/main --pr-author <your-github-login>
# Check that plugins.json is up to date / regenerate it
python3 scripts/build_index.py --check
python3 scripts/build_index.pyTo test a plugin end-to-end with a local Gameyfin instance before publishing it:
- Serve the JAR locally, e.g.
python3 -m http.server 8000in the directory that contains the JAR. - Write a manifest with
"url": "http://localhost:8000/<file>.jar", then validate it:python3 scripts/validate.py --allow-insecure plugins/<id>.json. - Generate a test index:
python3 scripts/build_index.py --allow-insecure --output /path/served/by/http.server/plugins.json. - Start Gameyfin with
gameyfin.plugins.marketplace.allow-insecure-urls=true, e.g. via the environment variableGAMEYFIN_PLUGINS_MARKETPLACE_ALLOWINSECUREURLS=true. - Add
http://localhost:8000/plugins.jsonas a marketplace and install the plugin.
Don't commit the test manifest or the generated index.