Skip to content

Publishing plugins

Sign and pack

labdex plugin keygen my.pem                      # my.pem (keep it secret) and my.pem.pub
labdex plugin sign ./pihole --key my.pem --signer "Your Name"
labdex plugin pack ./pihole -o pihole-0.1.zip

People trust your key under Plugins → Signing keys (paste my.pem.pub); then your plugin installs as community.

A plugin in its own git repository

That's how labdex's own plugins live (labdex-plugin-<id>): the plugin at the root of a repository, released by publishing a GitHub release. Start one from a plugin folder:

python3 backend/tools/plugin_repo/make_plugin_repo.py ./pihole ../labdex-plugin-pihole --community

It adds a README from the manifest, a licence (MIT for your own plugins, change it as you like; AGPL-3.0 for official ones), a CHANGELOG and the release workflow. Set the repository secret LABDEX_SIGNING_KEY to your private key. Then each release is: bump version in plugin.yaml, add a ## <version> section to CHANGELOG.md, commit, then publish a GitHub release tagged v<version> (in the web UI, or gh release create v<version> --target main --notes ""). The workflow checks the tag against the manifest, signs exactly what's committed and attaches <id>-<version>.zip to the release with CHANGELOG.md; the release description gets that version's CHANGELOG section (below anything you wrote). Files listed in .releaseignore (one pattern per line, like .gitignore: tests/, *.log, docs/draft.md) stay out of the zip; so do the repository's own files (.git, .github, .gitignore). labdex plugin prune <copy> does that by hand.

A Marketplace from plugin repositories

A Marketplace is a repository with a plugins.yaml:

name: My plugins
description: Plugins by me
plugins:
  - repo: me/labdex-plugin-pihole
    withdrawn: {"0.3": "loses the blocklists on restart"}

Add discover: [{owner: me, prefix: labdex-plugin-}] and every public repository of yours named like that is included too, so a new plugin repository shows up by itself.

labdex plugin build-index plugins.yaml site --key my.pem reads every listed repository's releases (with GITHUB_TOKEN set it can make more API calls), checks each archive's checksum and that it's signed with your key, and writes a signed site/index.json that points at the release assets. Every release is a version people can install, update to or go back to, with its notes; a withdrawn version stays installable on purpose but warns everyone who has it, and updates skip it. Publish site/ on GitHub Pages: the labdex-plugins repository is a ready-made example (it rebuilds every hour, when plugins.yaml changes, and on a plugin-released dispatch from a plugin's release workflow).

A repository from plugin folders

Or put each plugin in its own folder and build a repository with the archives in it:

labdex plugin repo ./plugins ./public --key my.pem --name "My plugins" --description "…"

This signs every plugin, zips them and writes a signed public/index.json. Publish public/ on any web server (GitHub Pages works). People add the URL of index.json under Plugins → Repositories, compare the key id with the one you publish, and install from the marketplace. The key is pinned: changing it means people add the repository again.

Each index entry carries the plugin's labdex range, so the marketplace only offers plugins that work with the labdex looking at it. Building into a folder that already has an index keeps the older versions whose archives are still there, so people can go back to them; labdex plugin fetch-repo <url> <folder> --key my.pem downloads what's published first. A plugin folder's CHANGELOG.md (## <version> sections) gives each version its notes.

Withdraw a version (or undo it) and re-sign the index:

labdex plugin yank ./public pihole 0.3 --key my.pem --reason "loses the blocklists on restart"
labdex plugin yank ./public pihole 0.3 --key my.pem          # offered again

The official Marketplace

Every official plugin lives in its own repository (labdex-app/labdex-plugin-<id>); labdex has them as git submodules in plugins/ for its tests, but doesn't ship them: users install them from the Marketplace. labdex-plugins lists them; its index is signed with the project key and published at https://labdex-app.github.io/labdex-plugins/index.json, which labdex trusts out of the box. Contributions are welcome: see Contributing.

labdex's own versions

labdex releases are MAJOR.MINOR.SUB; a hotfix of a release is MAJOR.MINOR.SUB-N:

Version What it is
1.9.0 a release
1.9.0-1, 1.9.0-2 its hotfixes: bug fixes only, they come after 1.9.0
1.9.1 a small release
1.10.0 the next release

A hotfix is what Python calls a post-release (1.9.0-1 is 1.9.0.post1), so plugin ranges like labdex: ">=1.9,<2" take hotfixes as you'd expect. The image of 1.9.0-1 is tagged 1.9.0-1, 1.9.0, 1.9, 1 and latest: a hotfix replaces its release.

To make one, python backend/tools/bump.py sub|minor|major|patch (or an exact version) writes the version everywhere and opens a changelog heading. Push that to main, then publish a GitHub release tagged v<version> on it (gh release create v1.23.0 --target main --notes ""): the release workflow builds and signs the image and the files and attaches them, with the changelog section as notes if you left them empty. It refuses a tag that doesn't match the code's version.