Plugin authoring
- Python 3.11 or later
- magellon-sdk installed (`pip install magellon-sdk`)
- Docker Desktop (for the docker install method)
This guide walks through writing a Magellon plugin end to end. Verify your setup:
magellon-sdk --version1. Scaffold
Section titled “1. Scaffold”magellon-sdk plugin scaffold generates a runnable skeleton — 11
files including a manifest.yaml, a working main.py, a PluginBase
subclass stub, a Dockerfile, a pyproject.toml, and a unit test:
magellon-sdk plugin scaffold my-fft --category fftcd my-fftls# manifest.yaml main.py plugin/ Dockerfile pyproject.toml# requirements.txt tests/ README.md .gitignoreThe generated output is lint-clean and pack-clean out of the box — you can pack it immediately without editing. Then iterate.
The --category flag pins which TaskCategory your plugin serves
(e.g. fft, ctf, particle_picking, two_d_classification).
Browse /plugins for the full catalog.
2. Fill in your algorithm
Section titled “2. Fill in your algorithm”Two files matter:
plugin/compute.py— your algorithm. Pure Python, takes the decoded input dict, returns the result. No SDK glue — keep it unit-testable.plugin/plugin.py— thePluginBasesubclass. Maps the incomingTaskMessageto your compute call; emits progress events viareporter.progress(percent, message).
def run_compute(input_data: dict) -> dict: # Your algorithm here. return {"result": ...}
# plugin/plugin.pyclass MyFftPlugin(PluginBase): plugin_id = "my-fft" category = "fft"
def execute(self, input_data, reporter) -> dict: reporter.started() reporter.progress(25.0, "loading image") out = run_compute(input_data) reporter.progress(90.0, "writing output") reporter.completed(output_files=[]) return outThe runner handles bus consume / decode / publish-result for you. You won’t touch RMQ or NATS directly.
3. Declare your manifest
Section titled “3. Declare your manifest”Edit manifest.yaml to fill in:
manifest_version: "1.1"plugin_id: my-fftname: "My FFT — fast power spectrum"version: 0.1.0requires_sdk: ">=2.1,<3.0"
category: fftbackend_id: my-fft
# Resources hint the install pipeline uses to pick a host.resources: cpu_cores: 1 memory_mb: 512 gpu_count: 0 # Wave 4: declare replica bounds if your plugin is parallelizable. replicas: desired: 1 min: 1 max: 4
# Wave 4: how should the runtime auto-restart your plugin?lifecycle: restart_policy: on-failure restart_max_retries: 5
# Install methods, in order of preference.install: - method: uv pyproject: pyproject.toml requires: - python: ">=3.11" - method: docker dockerfile: Dockerfile build_context: . requires: - docker_daemon: trueSee the hub catalog for production examples.
4. Lint
Section titled “4. Lint”magellon-sdk plugin lint catches DX papercuts before pack:
magellon-sdk plugin lint .# OK: my-fft — 0 errors, 0 warningsWhat it checks:
- Errors (block the install pipeline): manifest schema invalid, install spec missing Dockerfile / pyproject.toml, replica bounds inconsistent.
- Warnings: health-check timeout outside [5, 300] seconds.
If you bundle an SDK wheel under wheels/, it must match your CLI’s SDK version. An older wheel (e.g. < 2.3) is missing magellon_sdk.paths and the plugin will throw ModuleNotFoundError at container start — long after lint passes cleanly.
Fix: rebuild via scripts/rebuild-sdk-wheels.sh and update the filename references in pyproject.toml and requirements.txt. Run magellon-sdk plugin lint . again to confirm the warning is gone.
- Info (only shown with
--strict): missing tags, description, author.
--strict flips warnings to non-zero exit, suitable as a CI gate.
5. Test
Section titled “5. Test”magellon-sdk plugin test is one stop for pre-pack checks:
magellon-sdk plugin test .# === Linting my-fft ===# OK: my-fft — 0 errors, 0 warnings# === Running pytest tests ===# tests/test_compute.py::test_run_compute_echoes_input PASSED## OK: my-fft — lint + tests passedIt runs lint, then pytest under tests/, then optionally probes
/health against a running instance (--http http://127.0.0.1:8000).
6. Pack
Section titled “6. Pack”magellon-sdk plugin pack .# Packed my-fft v0.1.0# archive: my-fft-0.1.0.mpn# size: 8,392 bytes# sha256: 4c247020...# files: 11 (+ 2 manifest aliases)The .mpn is a deflate-compressed zip with a stamped manifest.yaml
at the root, a regenerated archive_id (UUID v7), and SHA-256
checksums for every file inside.
7. Install locally
Section titled “7. Install locally”# Upload via the admin dialog at /panel/plugins, OR programmatically:curl -X POST http://localhost:8000/admin/plugins/install \ -F file=@my-fft-0.1.0.mpn \ -F install_method=docker \ -H "Authorization: Bearer $TOKEN"The Magellon admin panel surfaces the new plugin on
/panel/plugins. The runner is ready as soon as the install
completes.
8. Publish to the hub
Section titled “8. Publish to the hub”The magellon.org hub is a curated, git-based catalog. Submit a PR to magellon-web:
- Drop your
.mpnunderpublic/assets/plugins/. - Create
src/content/plugins/<plugin_id>-<version>.yamlwith the publication metadata (see existing entries for the shape). - Open a PR.
- A reviewer verifies the SHA256 against your archive, sets the
tier (
verifiedfor maintained plugins,communityfor contributor projects), and merges.
CI/CD redeploys; your plugin appears on /plugins within minutes.
| Command | What it does |
|---|---|
plugin init <dir> | Bare manifest.yaml + README only |
plugin scaffold <dir> --category <cat> | Full runnable skeleton — recommended starting point |
plugin lint <dir> [--strict] | Pre-pack validation; --strict fails on warnings (use as CI gate) |
plugin validate <path> | Schema-only check; accepts a dir, manifest.yaml, or .mpn archive |
plugin test <dir> [--http <url>] | Lint + pytest + optional /health probe |
plugin pack <dir> [-o <out>] [--force] | Produce the .mpn archive |