Skip to content
Doug edited this page Jun 26, 2026 · 9 revisions

GDX Dispatch

A single-tenant, self-hosted field-service dispatch platform — customers, jobs & work-orders, estimates, invoicing, scheduling/dispatch. FastAPI backend, Vue 3 SPA, Postgres + Redis + Celery.

Start here

Pages

Operating

Plugins

Conventions

  • Role & Nav Naming Conventions — how roles are spelled/stored (canonical long form in users.role) and how nav visibility is decided. Read before touching role strings or adding a nav module.

Plugins

GDX supports third-party modules (plugins): full-stack extensions an operator installs into their own instance, each with its own backend and its own (server-rendered) UI. Architecture & rationale: ADR-013.

The model

Plugins are ordinary pip packages that register under the gdx.modules entry-point group. They run inside a dedicated plugin-host container (isolated from the core app — the VS Code "Extension Host" model), which the core app proxies to at /api/plugins/*, forwarding the authenticated principal. Plugins ship no browser JavaScript: their UI is declared as a JSON manifest the host renders. Operators vet what they install; there is no central registry or signing.

Build status — all steps implemented & verified

Step What Status
1 gdx.plugin_api foundation — manifest, discovery, version gate
2 PluginBase + forwarded-identity context, require_module
3 plugin-host container + core /api/plugins/* proxy ✅ verified live E2E
4 UI-manifest schema + Vue host renderer (vitest)
5 plugin_registry + in-app install / reconcile ✅ verified live
6 Reference gdx-plugin-example plugin ✅ verified live

Verified on a live docker stack: authenticated user → core proxy → plugin-host → the example plugin's own (migrated) table; module gating (403 before grant, 200 after), tenant isolation, the catalog + UI manifest, and in-app install via the registry → reconcile (pip-install into a /plugins volume → discover). 34 automated tests pass (30 backend pytest + 4 frontend vitest).

Known gaps (honest)

  • Auto-restart on install isn't built. Registering a plugin records intent; applying it currently needs a manual plugin-host restart. The docker-socket "supervisor" that restarts plugin-host from the app is future work.
  • Migrations use PluginBase.metadata.create_all (idempotent table creation), not yet a per-plugin Alembic branch — fine until a plugin needs schema changes.
  • The UI renderer is unit-tested (vitest) but not yet wired into a route/nav and not browser-E2E'd on the live stack.
  • The /plugins volume must be writable by the plugin-host runtime user (ops).

Clone this wiki locally