Files
sub2api-add/plugins/plugin-admin/README.md
T
Qiufeng 5feae3ad41
Business Plugins CI / check (plugin-admin) (push) Successful in 3m13s
Business Plugins CI / check (subscription-admin) (push) Successful in 1m41s
chore: initialize standalone business plugin repository
2026-08-27 23:36:08 +08:00

3.3 KiB

Sub2API Business Plugin Control Plane V1

This directory contains the independent administrator-only control plane for Business Plugins. It is deliberately separate from Sub2API Core and from the existing .s2plugin OpenAI OAuth transport runtime.

The control plane owns the plugin catalog, signed package verification, revision directories, lifecycle state, encrypted configuration metadata, menu preview/apply, and its own audit log. Core remains authoritative for users, administrator roles, balances, subscriptions, billing, and audit records. The service never connects to Core PostgreSQL/Redis and never sends a Core JWT or Admin Key to the browser.

Run locally

CORE_BASE_URL=http://127.0.0.1:8080 \
PLUGIN_HOST=127.0.0.1 PLUGIN_PORT=8090 \
PLUGIN_REGISTRY_DIR=./data \
PLUGIN_ENV=development \
PLUGIN_ALLOW_UNSIGNED=true \
PLUGIN_CONFIG_KEY=local-development-secret-at-least-32-chars \
go run .

PLUGIN_ALLOW_UNSIGNED=true is a development-only switch. Production packages must contain signature.json, use Ed25519, and match a trusted key from PLUGIN_TRUSTED_PUBLISHERS (a JSON object of key ID to base64 public key). PLUGIN_CONFIG_KEY is required in every environment; use a randomly generated secret in production and keep it stable across restarts so encrypted plugin configuration remains decryptable.

Open /admin/ directly or expose the service through the reverse proxy in deploy/. The first login is the existing Core administrator login; no plugin user table is created. The control plane stores only a short-lived server-side session and encrypted plugin configuration.

Control-plane endpoints

GET  /healthz
GET  /readyz
POST /login                 POST /login/2fa       POST /logout
GET  /api/me                GET  /api/plugins     GET /api/plugins/{id}
POST /api/plugins/{id}/install    (multipart field: package)
POST /api/plugins/{id}/upgrade    (multipart field: package)
POST /api/plugins/{id}/enable|disable|rollback|uninstall
GET|PUT /api/plugins/{id}/config
POST /api/plugins/{id}/menu-preview|menu-apply
POST /api/menu-items/preview|apply   (JSON: {"plugin_id":"..."})
GET  /api/audit

Every mutation requires the plugin CSRF token and an Idempotency-Key. A mutation returns an operation ID even when it completes synchronously. Failed installation and upgrade never replace the active revision. Uninstall is allowed only after disable and removes plugin files, not Core data.

Plugin package

Packages are ZIP files with manifest.json, optional detached signature.json, and declared ui/ files. A package may also include service/ files when the control plane owns the plugin process; external service packages omit backend.command and require a configured loopback service_url before enabling. SHA-256 hashes in the manifest cover every declared file. Absolute paths, traversal, duplicate entries, symlinks, undeclared hashes, oversized files, unknown manifest fields, and untrusted publishers are rejected before staging. Installation uses a per-plugin revision directory and an atomic registry JSON update.

Deliberate V1 limits

The control plane does not register Core routes, change Core migrations, run arbitrary proxy URLs, provide transparent iframe SSO, or move billing and subscription hot-path logic out of Core. A subscription manager remains a separate business plugin that consumes its own typed Core API adapter.