Skip to content

Authoring Plugins

Doug edited this page Jun 24, 2026 · 4 revisions

Authoring Plugins

How to build a GDX third-party module. This page tracks the implementation — it documents what exists today and marks what is still pending, so you can start against the stable parts now.

What's stable today (step 1)

The plugin contract: your package exports a PluginManifest under the gdx.modules entry-point group, and the host discovers it.

1. Declare the entry point

In your plugin's pyproject.toml:

[project]
name = "gdx-plugin-foo"
version = "0.1.0"

[project.entry-points."gdx.modules"]
foo = "gdx_plugin_foo:manifest"

The left side (foo) is just a label; the right side points at a module attribute that is a PluginManifest.

2. Export a manifest

# gdx_plugin_foo/__init__.py
from gdx_dispatch.plugin_api import PluginManifest

manifest = PluginManifest(
    key="foo",                 # lowercase, stable; becomes the module key + /api/plugins/foo
    name="Foo",                # shown in the admin UI
    tier="professional",       # "starter" | "professional" | "business"
    requires="gdx>=1.0",       # host-version gate; "" = any version
    # router, migrations_path, ui — added in later steps (see below)
)

Validation is enforced at construction (fail-loud): key must be non-empty, lowercase and trimmed; name non-empty; tier one of the three values.

3. The version gate

requires is checked at discovery. v0 grammar is "gdx>=X.Y[.Z]" or "". Anything else fails closed — an unparseable constraint will not load, so a typo can't silently ship an incompatible plugin. Comparison is numeric (gdx>=1.10 is newer than 1.9, not string-compared).

How discovery works

The host calls discover_plugins(), which loads every gdx.modules entry point, keeps the ones that return a valid PluginManifest and pass the version gate, and skips (with a log line) — never crashes on — a plugin that errors on import, returns the wrong type, or is incompatible. One bad plugin can't take down the others.

Pending (don't build against these yet)

  • router — your FastAPI APIRouter, mounted at /api/plugins/<key>. The auth/DB context and require_module re-export land in step 2.
  • migrations_path — your plugin's Alembic branch; tables namespaced plug_<key>_*, run by plugin-host after core migrations (step 3).
  • ui — your declarative screens (lists/forms/actions), rendered by the host. Schema lands in step 4. (No browser JavaScript — ever.)
  • Reference plugin — a complete gdx-plugin-example lands in step 6.

Reference

  • Architecture & rationale: ADR-013
  • Source: gdx_dispatch/plugin_api/

Clone this wiki locally