refactor(plugins): no plugin catalog; plugins ship in the image or are mounted (#5374)
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:
@@ -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
|
||||
@@ -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`
|
||||
|
||||
Reference in New Issue
Block a user