chore: initialize standalone business plugin repository
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
# 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
|
||||
|
||||
```sh
|
||||
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
|
||||
|
||||
```text
|
||||
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.
|
||||
Reference in New Issue
Block a user