Skip to content

Plugins

Plugins are how Magellon becomes a full processing platform — every cryo-EM algorithm (CTF estimation, motion correction, particle picking, 2D classification, …) runs through one contract, with form generation, lifecycle control, progress streaming, and multi-backend routing all handled by the framework.

Magellon ships with a curated hub at magellon.org/plugins. Each plugin is published as a .mpn archive — a zip containing the plugin’s code, a manifest.yaml, and an optional bundled SDK wheel. Today’s catalog includes:

PluginCategoryWhat it does
fftfftMagnitude-spectrum FFT — the reference / always-on plugin.
ctfctfCTFFIND4 CTF estimation.
motioncormotioncorMotionCor2 beam-induced motion correction (GPU).
topaztopaz_particle_pickingCNN-based particle picking + companion denoiser.
template-pickerparticle_pickingCross-correlation template matching for particle picking.
ptolemysquare_detectionONNX models for low-mag square + med-mag hole detection.
stack-makerparticle_extractionExtracts boxed particles into RELION-style stacks.
can-classifiertwo_d_classificationConvolutional autoencoder 2D classification.
magellon-sample-plugin(sample)Minimal worked example for plugin authors.

Plugins are categorized by the cryo-EM step they implement. A category (e.g. ctf) can have multiple backends (e.g. ctffind4, future gctf) — operators pick which backend serves each category, or pin a specific backend per-dispatch.

Two paths into your Magellon deployment:

In the Magellon admin panel, open Plugins → Browse hub, pick a plugin, click Install. CoreService fetches the .mpn from the hub, verifies its SHA256 against the catalog, and runs the install pipeline. Done.

Under the hood:

  • POST /admin/plugins/install-from-hub {plugin_id, version}
  • CoreService fetches https://magellon.org/v1/plugins/<id>.json, resolves the archive URL + sha256, downloads, verifies, then delegates to the standard install path. SHA mismatch fails closed.
SHA verification is strict

A checksum mismatch hard-fails the install and leaves no partial state on disk. If you’re installing a self-hosted mirror or a fork, update the catalog YAML’s sha256 field to match your archive before pointing CoreService at it.

For unpublished plugins, air-gapped sites, or forks: drag the .mpn onto the Upload archive dialog. Same install pipeline, different source of bytes.

Each plugin manifest declares one or more install methods. CoreService picks the first whose host predicates pass:

  • uv — pure-Python plugins install into a per-plugin venv under <plugins_dir>/<plugin_id>/. Linux production uses systemctl --user for restart-on-crash; Windows/macOS dev falls back to a Popen supervisor.
  • docker — the plugin runs in a container; CoreService manages it via the docker CLI. Supports the full lifecycle including pause and replicas (next sections).
SLURM install method (HPC)

A slurm install method is scaffolded for HPC deployments but is not wired in by default. If you need Slurm-based dispatch, contributions are welcome — see the plugin authoring guide.

Every installed plugin shows up on the Plugins page with its identity, install location, live status, and a controls row:

  • Start / Stop / Restart — process / container lifecycle.
  • Pause / Unpause — docker-only; freezes the container via cgroups (SIGSTOP). uv plugins show the control greyed out with a tooltip (“pause is docker-only — use Stop instead”).
  • Scale — docker plugins declaring resources.replicas.max > 1 in their manifest get a “N / max” chip. Clicking opens a stepper that calls POST /admin/plugins/<id>/scale {desired: N}. RMQ fan-out across the replica set is automatic.

A live Replicas drawer shows per-instance heartbeat freshness.

When two plugins serve the same category (e.g. ctffind4 + a future gctf both serving ctf), the runner UI shows a Backend picker above the input form. Defaults to the operator-pinned default; selection rides as target_backend on the dispatched TaskMessage so the routing is deterministic. “Auto” preserves today’s default routing.

The per-category drilldown page at /panel/plugins/categories/<slug> lists every backend side-by-side with status, replica count, and a one-click “Set as default” action.

  • GET /admin/plugins/<id>/logs?tail=N — one-shot tail of docker logs --tail N (docker installs) or <install_dir>/app.log (uv-Popen) or journalctl --user (uv-systemd).
  • Socket.IO plugin_log channel — live tail; clients emit("join_plugin_room", {plugin_id}) and receive one event per line. The React UI surfaces this on the per-plugin runner page and inline on cards when Healthy=False.
  • Upgrade — the Update available chip on a card opens a dialog that lists hub-published versions, runs a force-downgrade check, and calls POST /admin/plugins/<id>/upgrade-from-hub. RMQ requeues in-flight tasks from the killed old container to the surviving / upgraded one — no data loss, possibly duplicate work.

Manifest lifecycle.restart_policy is one of no / on-failure / always / unless-stopped. Docker plugins translate this directly to docker run --restart=<policy>:<N>; systemd-user installs parameterize the unit file’s Restart= line. uv-Popen installs are restricted to no (no watchdog).

The platform ships a CLI on magellon-sdk for the full author workflow:

Terminal window
# 1. Scaffold a runnable skeleton.
magellon-sdk plugin scaffold my-plugin --category ctf
# 2. Edit plugin/compute.py + plugin/plugin.py to do your thing.
# 3. Lint before pack — catches stale SDK wheels, missing install
# backing files, unreasonable health-check timeouts, etc.
magellon-sdk plugin lint my-plugin
# 4. Run the author's checks (lint + pytest + optional /health probe).
magellon-sdk plugin test my-plugin
# 5. Pack into a .mpn archive.
magellon-sdk plugin pack my-plugin

Result: a my-plugin-0.1.0.mpn ready to upload via the admin dialog or submit as a hub PR. See Plugin authoring for the full guide.

my-plugin/
├── manifest.yaml # identity + install methods + lifecycle policy
├── main.py # FastAPI lifespan + PluginBrokerRunner
├── plugin/
│ ├── __init__.py
│ ├── plugin.py # PluginBase subclass — execute(input, reporter)
│ └── compute.py # your algorithm (unit-testable, no SDK glue)
├── Dockerfile # for the docker install method
├── pyproject.toml # for the uv install method
├── requirements.txt
└── tests/
└── test_compute.py

The framework handles discovery, schema exposure, job persistence, progress streaming, replicas, and lifecycle. Your plugin’s job is to:

  • Declare inputs / outputs via Pydantic models that subclass the category’s canonical input_model / output_model.
  • Implement execute(input_data, reporter), yielding progress via reporter.progress(percent, message).
  • Optionally expose Capability.SYNC or Capability.PREVIEW for sub-second HTTP interactions (the picker’s slider does this).

The SDK’s magellon_sdk.runner.PluginBrokerRunner ties the wire contract to your execute — bus consume, decode, dispatch, encode result, publish. You won’t touch RMQ or NATS directly.

The magellon.org plugin hub is a curated, git-based catalog:

  1. Pack your plugin: magellon-sdk plugin pack .<id>-<v>.mpn.
  2. Drop the .mpn under magellon-web/public/assets/plugins/.
  3. Create magellon-web/src/content/plugins/<id>-<v>.yaml with metadata (see existing entries for the shape).
  4. Open a PR. A reviewer verifies the SHA256, sets the tier (verified vs community), and merges.
  5. CI/CD redeploys the static site; your plugin appears on /plugins within minutes.
Coming soon: one-command publish

A future magellon-sdk plugin publish will automate the pack → upload → YAML → PR flow end-to-end. For now, the manual steps above take under five minutes.