refactor(plugins): no plugin catalog; plugins ship in the image or are mounted (#5374)
CI / lint (push) Successful in 3s
CI / integration (push) Failing after 1m12s
CI / unit (push) Successful in 1m25s
CI / publish (push) Skipped

First-party plugins have shipped inside the image since a7a281c, and the
catalog's only repository (Steward-plugins) answers 404, so Steward polled a
dead URL every minute for nothing. The operator chose to remove the catalog
entirely (#5214 "Review: readable and fast on the live instance").

Removed:
- core/plugin_index.py and the plugin_catalog_refresh startup task;
- the plugins.index_url default, and migration 0035 deletes the stored
  plugins.index_url / plugins.repositories rows;
- the Settings -> Plugins repository editor, catalog, refresh, install and
  reload routes and their templates;
- the download/checksum/extract install path in plugin_manager, and the
  PLUGIN_INSTALL_DIR config it wrote to;
- plugins/index.yaml, docs/plugins/index.yaml.example and the guide's
  "Publishing to the Catalog" section (now "Shipping a Plugin").

Kept: enabling and configuring plugins, hot-reload on enable, restart, orphan
cleanup, and the external root (/data/plugins) for operator-mounted plugins.
tests/test_no_plugin_catalog.py guards the routes, default and task.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-08 13:15:55 -04:00
co-authored by Claude Opus 5.5
parent 501a345aed
commit f67678998c
17 changed files with 70 additions and 1258 deletions
-113
View File
@@ -1,113 +0,0 @@
# steward-plugins / index.yaml
#
# This file is the catalog index for the steward plugin repository.
# It is fetched by the app's Settings → Plugins → Plugin Catalog UI.
#
# Steward reads this file from:
# https://git.fabledsword.com/bvandeusen/Steward-plugins/raw/branch/main/index.yaml
#
# After adding or updating a plugin entry, commit and push — the change is
# live immediately for anyone whose app fetches the catalog (cache TTL: 5 min).
#
# REQUIRED fields for each plugin entry:
# name - must match the plugin's directory name exactly
# version - semver string
# download_url - direct URL to a zip file containing the plugin
# checksum_sha256 - SHA-256 hex digest of the zip (leave empty to skip verification)
#
# OPTIONAL fields:
# description, author, license, min_app_version,
# repository_url, homepage, tags
#
# Generating a checksum:
# sha256sum traefik.zip
# shasum -a 256 traefik.zip (macOS)
#
# Download URL conventions:
# Gitea release assets (recommended):
# https://git.fabledsword.com/bvandeusen/Steward-plugins/releases/download/traefik-v1.0.0/traefik.zip
# Upload the zip as a release attachment in Gitea; paste the URL here.
# Source archive tarballs work too but release assets are preferred (smaller, plugin-only).
version: 1
updated: "2026-03-22"
plugins:
- name: http
version: "1.0.0"
description: "Synthetic HTTP endpoint monitoring — status code, response time, content match, TLS expiry"
author: "Steward"
license: "MIT"
min_app_version: "0.1.0"
repository_url: "https://git.fabledsword.com/bvandeusen/Steward-plugins"
homepage: "https://git.fabledsword.com/bvandeusen/Steward-plugins/src/branch/main/http"
download_url: "https://git.fabledsword.com/bvandeusen/Steward-plugins/releases/download/http-v1.0.0/http.zip"
checksum_sha256: ""
tags:
- monitoring
- http
- synthetic
- uptime
- name: docker
version: "1.0.0"
description: "Docker container status, resource usage, and restart tracking via Docker socket"
author: "Steward"
license: "MIT"
min_app_version: "0.1.0"
repository_url: "https://git.fabledsword.com/bvandeusen/Steward-plugins"
homepage: "https://git.fabledsword.com/bvandeusen/Steward-plugins/src/branch/main/docker"
download_url: "https://git.fabledsword.com/bvandeusen/Steward-plugins/releases/download/docker-v1.0.0/docker.zip"
checksum_sha256: ""
tags:
- containers
- docker
- infrastructure
- name: traefik
version: "1.0.0"
description: "Traefik reverse proxy metrics and access log integration"
author: "Steward"
license: "MIT"
min_app_version: "0.1.0"
repository_url: "https://git.fabledsword.com/bvandeusen/Steward-plugins"
homepage: "https://git.fabledsword.com/bvandeusen/Steward-plugins/src/branch/main/traefik"
download_url: "https://git.fabledsword.com/bvandeusen/Steward-plugins/releases/download/traefik-v1.0.0/traefik.zip"
checksum_sha256: "" # fill in after running: sha256sum traefik.zip
tags:
- proxy
- metrics
- access-log
- name: unifi
version: "1.0.0"
description: "UniFi Network controller integration — WAN health, devices, clients, DPI"
author: "Steward"
license: "MIT"
min_app_version: "0.1.0"
repository_url: "https://git.fabledsword.com/bvandeusen/Steward-plugins"
homepage: "https://git.fabledsword.com/bvandeusen/Steward-plugins/src/branch/main/unifi"
download_url: "https://git.fabledsword.com/bvandeusen/Steward-plugins/releases/download/unifi-v1.0.0/unifi.zip"
checksum_sha256: ""
tags:
- network
- unifi
- ubiquiti
- name: host_agent
version: "1.0.0"
description: "Remote Linux host resource monitoring via a lightweight Python push agent"
author: "Steward"
license: "MIT"
min_app_version: "0.1.0"
repository_url: "https://git.fabledsword.com/bvandeusen/Steward-plugins"
homepage: "https://git.fabledsword.com/bvandeusen/Steward-plugins/src/branch/main/host_agent"
download_url: "https://git.fabledsword.com/bvandeusen/Steward-plugins/releases/download/host_agent-v1.0.0/host_agent.zip"
checksum_sha256: ""
tags:
- host
- monitoring
- cpu
- memory
- storage
+5 -77
View File
@@ -28,7 +28,7 @@ license: "MIT" # any SPDX identifier, e.g. MIT, Apache-2.0, GPL-3.0
# Optional: prevents loading on older app versions
min_app_version: "0.1.0"
# Optional: shown in the catalog UI
# Optional: where the plugin's source lives
repository_url: "https://github.com/yourname/yourrepo"
homepage: "https://github.com/yourname/yourrepo/tree/main/myplugin"
tags:
@@ -393,83 +393,12 @@ async def mcp_overview(db) -> list[dict]:
---
## Publishing to the Catalog
## Shipping a Plugin
The official plugin catalog is hosted at `https://git.fabledsword.com/bvandeusen/Steward-plugins`. Anyone can submit a plugin by opening a pull request — first-party and third-party plugins are treated identically by the catalog system.
Steward never downloads plugins. There is no catalog and no install button: the app runs with no outbound network.
### Repo layout
```
steward-plugins/
├── index.yaml ← catalog index — the only file Steward fetches
├── myplugin/
│ ├── plugin.yaml
│ ├── __init__.py
│ └── ...
└── .github/
└── workflows/
└── publish.yml ← packages each plugin dir into a zip on release
```
### index.yaml entry
Each plugin needs a corresponding entry in `index.yaml`. See `docs/plugins/index.yaml.example` for the full schema. The minimum required fields are:
```yaml
- name: myplugin
version: "1.0.0"
description: "What this plugin does"
author: "Your Name"
license: "MIT"
min_app_version: "0.1.0"
repository_url: "https://github.com/yourname/yourrepo"
homepage: "https://github.com/yourname/yourrepo/tree/main/myplugin"
download_url: "https://github.com/yourname/yourrepo/releases/download/myplugin-v1.0.0/myplugin.zip"
checksum_sha256: "" # fill in after zipping — see below
tags:
- monitoring
```
### Creating a release zip
The zip must contain the plugin files at the top level OR inside a single directory named after the plugin. Both layouts work:
```
# Layout A — flat (preferred)
myplugin.zip
├── plugin.yaml
├── __init__.py
└── ...
# Layout B — single top-level directory (also accepted, GitHub archive default)
myplugin.zip
└── myplugin/
├── plugin.yaml
└── ...
```
Generate the zip and its checksum:
```bash
cd steward-plugins
zip -r myplugin.zip myplugin/
sha256sum myplugin.zip # paste this into index.yaml checksum_sha256
```
Upload `myplugin.zip` as a GitHub release asset, then update `index.yaml` with the release download URL and checksum.
### How install works
When a user clicks **Install** in Settings → Plugins:
1. The app downloads the zip from `download_url`
2. Verifies the SHA-256 checksum (if provided)
3. Extracts the plugin into its `PLUGIN_DIR`
4. Runs any pending Alembic migrations for the plugin
5. Attempts a **hot-reload** — registers the blueprint and scheduled tasks without restarting
6. If the plugin was previously loaded (blueprint already mounted), a **restart** is required to pick up the new code
Hot-reload works reliably for brand-new plugin installs. Updates to already-active plugins require a restart, which can be triggered from the same settings page.
- **First-party plugins** live in this repository under `plugins/` and ship inside the image, so they version with core.
- **A third-party plugin** is a folder you put in the external plugin root, `/data/plugins` by default (`STEWARD_PLUGIN_DIR` moves it). Restart Steward and the plugin appears in Settings → Plugins, where you enable it. A bundled plugin of the same name wins over an external one.
---
@@ -486,4 +415,3 @@ Hot-reload works reliably for brand-new plugin installs. Updates to already-acti
- [ ] MCP tools (if any) registered via `register_mcp_tools(register)` with a role and `read_only`
- [ ] Templates namespaced under `templates/myplugin/`
- [ ] Plugin enabled in app settings
- [ ] `index.yaml` entry added with `download_url` and `checksum_sha256`