Files
shopdb-flask/docs/PLUGINS.md
cproudlock ad9210b84a Write down the tool that was built, not the one that was tabled
Secure Send shipped on 21 August and nothing outside the code caught up. The
roadmap still listed it under Deferred as "tabled", and gave as the reason that
the tools plugin is browser-only by design with get_blueprint and get_models
both empty - which stopped being true in the same day's commits that gave it a
blueprint, a table, a migration chain, a permission and a setting. A reader
following the roadmap would have concluded the feature does not exist.

The generated API docs were fine: api-inventory.json and openapi.json both
carry /api/tools/secrets, so the contract gates did their job. What was missing
was every word written for a person, and that is the half where the security
properties live.

docs/SECURE-SEND.md is the new page. The parts worth having in prose rather
than inferring from the code: that a lost secret is lost to everybody including
an administrator, which is the same property as "a compromised database yields
nothing" seen from the other side; that the 4096-character limit is the entire
server-side enforcement, because a server that cannot read what it holds cannot
have an opinion about content, so raising it turns a password tool into an
unaudited file transfer; that redeeming is a POST because preview prefetch in
chat and mail clients would otherwise spend a one-view secret before the
recipient ever sees it; and that somebody should send a link to themselves
through their own mail or chat client and confirm the key survives after the #
before a real credential goes through it, because a product that rewrites links
can strip the fragment and nothing on this side can help.

securesend_allow_guests was in the database, on a settings page, and in no
document. It is now in CONFIG.md with what it actually does - an unauthenticated
write endpoint, rate limited per address, failing closed - rather than only its
one-line description.

Also corrected along the way: site_base_url was documented as scheme + host
while the code deliberately tolerates a subpath in it, and the bundled plugin
table in PLUGINS.md had no row for tools at all.

Still missing after this: PLUGINS.md has no row for backups either.
2026-08-24 08:37:25 -04:00

8.4 KiB

Plugins

shopdb-flask is a framework. The plugins listed here are the pieces other GE Aerospace facilities can install, build, or skip per ADR-003. Bundled plugins ship in the framework repo. External plugins live in their own repos and drop into <repo>/plugins/<name>/ at install time.

Bundled (ship with the framework)

These plugins are in plugins/ in this repo. Enable per site with flask plugin install <name>.

Plugin Tracks Notes
machines Manufacturing machinery: 5-axis mills, lathes, broachers, heat treatment ovens Manually entered. See ADR-005. Subtype tables for FOCAS / CLM / MTConnect controller protocols (planned).
computers Shop-floor PCs and engineering workstations Fed by the PXE pipeline collector per ADR-006.
printers Network and shop-floor printers Public installer map page + fleet install contract (/api/printers/install-list, /pc-default, /install-batch; see PRINTER-INSTALLER.md). Optional Zabbix integration for supply tracking. Legacy PrinterData retiring per ADR-001.
network Switches, routers, access points, IDFs as locations Asset-only; cleanest of the bundled set.
usb USB devices issued to shop-floor users Lightweight checkout / check-in.
notifications Shop-floor notifications, recognitions, kiosk feed Used by ShopfloorDashboard.vue.
measuringtools Metrology and inspection instruments: calipers, micrometers, thread/bore/height gages, indicators Per ADR-005. Calibration lifecycle with derived status. First plugin built on the matured scaffold; its walkthrough is PLUGIN-GUIDE.md. Ships default_enabled: false.
employees Read-only employee directory lookup Backed by a separate HR database. Ships default_enabled: false.
geenforce GE-Enforce manifest store: imaging PC-type scopes and their install manifests (apps, scripts, files, registry, version gates) Per ADR-012. Served to the GE-Enforce client as JSON. Requires GE-Enforce lib >= 2.6 on target PCs. Ships default_enabled: false.
knowledgebase Knowledge Base articles linking to external resources Lightweight article store.
printedparts 3D-printed parts inventory Kiosk checkout / check-in. Ships default_enabled: false.
slides Slides for the lobby display and shop-floor screensaver Upload / reorder / delete per surface. Management gated on the slides.manage permission, grantable to non-admin curators; display routes are public.
tools Technician utilities: barcode and QR label generation, unit conversions, Secure Send Mostly browser-side by design, so an air-gapped bench gets them with no network. Secure Send is the exception and is why this plugin has a blueprint, a table and a migration chain: see SECURE-SEND.md.
warranty Asset warranty tracking Manual entry now, Dell / Lenovo / HP provider lookups later. Derived coverage status with report buckets.

Plugin permissions

Plugins may register their own permissions (for example slides.manage). Admins implicitly hold them; grant them to specific roles or users to delegate curation without admin. Each plugin's registered permissions appear in its plugin.py get_permissions().

Building your own

Guides:

  • PLUGIN-QUICKSTART.md - generate, customize, install, and test a plugin in 30 minutes using flask plugin new.
  • PLUGIN-GUIDE.md - the full narrative walkthrough of building the measuringtools plugin, the exemplar that exercises every current framework feature (models, per-plugin migrations, authz, hooks, frontend integration, tests).
  • PLUGIN-EXTERNAL-REPO.md - developing a plugin in its own repo per ADR-003: repo layout, symlink dev workflow, core_version pinning, and a runnable CI harness (scripts/test-external-plugin.sh) that tests the plugin against a pinned framework ref.
  • CONTRACT-STABILITY.md - path to contract 1.0: what is settled vs still churning, the bump rules, and how much a sister site can safely build on today.

The contract is locked in ADR-001 and versioned per ADR-002.

Quick path:

flask plugin new cameras --description "Tracks shop-floor surveillance cameras"
# edit plugins/cameras/models/cameras.py with your fields
flask plugin install cameras

Migrations (per-plugin chains)

Each plugin that owns tables carries its own Alembic chain under plugins/<name>/migrations/, with a per-plugin version table alembic_version_<name> independent of the core alembic_version. Ownership is split at a fixed cutover (see ADR-008):

  • The core chain (flask db upgrade) created every table that existed through its head, including the bundled-plugin tables. Each bundled plugin's 0001 migration is a stamp-only no-op anchor recording that fact.
  • From the cutover forward, a change to a plugin's schema lands as plugins/<name>/migrations/versions/000N_*.py, never in the core chain. The core chain is reserved for core tables.
  • A plugin built AFTER the cutover (e.g. measuringtools) is different: the core chain never created its tables, so its 0001 is a REAL baseline that creates them, not a no-op anchor. See PLUGIN-GUIDE.md for the anchor-vs-baseline distinction.

Deploys and upgrades run flask db upgrade then flask plugin upgrade-all. upgrade-all stamps every plugin anchor and applies any later plugin migrations; it is idempotent. The registry (instance/plugins.json) records which revisions each plugin has applied in migrations_applied.

Distribution conventions

For sister-site plugins (per ADR-003):

  • Plugin lives in its own git repo: <git-host>/<your-site>/<pluginname>
  • Adopting site clones or symlinks into their <repo>/plugins/<name>/
  • Plugin manifest declares core_version range matching the framework version they target
  • Plugin readme explains: what it tracks, who maintains it, where to file issues

Lean per-site builds

A site ships only the plugins it chose; a site that never wants printedparts / usb / network never carries that code (see ADR-013 and ADR-014). Three layers make a build lean:

  • Backend code - scripts/build-site.sh <profile> stages shopdb/core plus only the chosen plugins' directories (and their hard-dependency closure). A plugin a site did not choose is absent from the backend tree.
  • Frontend code - SITE_PLUGINS=machines,printers npm run build (via scripts/stage-frontend.mjs) stages only those plugins' frontend/ dirs and codegens the route table. Exception: a plugins/<name>/frontend/ dir with no manifest.json is a CORE feature (e.g. applications), not a per-site plugin, and is ALWAYS staged regardless of SITE_PLUGINS - otherwise a lean build would lose a core page.
  • Database - the shared core Alembic baseline creates every plugin's tables, so a lean site provisions them and then drops the ones it does not use with flask plugin prune-schema (ADR-014). Run it once at provisioning, after flask db upgrade and flask plugin upgrade-all; see DEPLOY.md.

Menus follow the build, not a plugin flag. The sidebar nav, the settings rail, and the Displays links all gate on whether the target route was actually staged into this build (the router's own route table), not on a registry "enabled" flag. So a lean site never shows a menu entry that dead-ends on a blank page - an omitted plugin's nav item, settings cards, and kiosk links all disappear together. Shopfloor Dashboard is a core view but is gated on the notifications plugin (its only data source), so it drops when notifications is not in the build.

Naming policy

Plugin names follow the framework's naming convention (lowercase concatenated, no underscores or dashes; full words preferred over acronyms). See CONTRIBUTING.md. Plugin name collisions across sites are not enforced; the convention recommends prefixing site-specific plugins with the site code (e.g., wjsf-shippingstation) when there is risk of overlap.