Files
HiveGrid-Wireframes/hivegrid-cloud/docs/plugins/versioning.md
T

136 lines
6.5 KiB
Markdown

# Plugin Versioning + Compatibility Policy
The plugin runtime supports three independent version axes. This
document defines the rules each axis follows and how they compose into
the compatibility checks the host enforces at install time.
## Axes
| Axis | Scope | Policy |
| ----------------------- | ----------------------------------------------------- | ----------------------------------------------- |
| Manifest schema version | `schemaVersion` field on every plugin.toml | Integer-bumped on breaking schema changes |
| Plugin SDK version | `@toolctrl/plugin-sdk` npm package | NPM SemVer |
| Host runtime version | The host binary running the plugin | NPM SemVer (toolctrl-boilerplate `package.json`) |
Each plugin pins:
```toml
schemaVersion = 1 # required
minHostVersion = "1.0.0" # optional, inclusive lower host bound
maxHostVersion = "1.9.9" # optional, inclusive upper host bound
```
## SemVer policy for plugin authors
| Change | Bump |
| ----------------------------------------------------- | ------------- |
| Add a new hook subscription | minor |
| Drop a hook subscription | major |
| Add a new declared capability | minor |
| Tighten an `allowedHosts` / `keyPattern` scope | major |
| Loosen an `allowedHosts` / `keyPattern` scope | minor |
| Change `entry` filename | major |
| Logic-only change inside the wasm | patch |
| Re-sign with a new key (`signedBy` change) | minor (audit) |
Side-by-side updates are not implemented in the foundation. The
`Plugin` table has a unique `(tenantId, pluginId)` constraint, and
`installPlugin` rejects a duplicate `pluginId` for the tenant with
`already_installed`. To move a tenant to a new plugin version today,
operators must disable/remove the existing install path first, then
install the replacement.
## SemVer policy for the host
| Change | Bump |
| ----------------------------------------------------- | ------------- |
| Add a capability to the closed enum | minor |
| Remove a capability from the closed enum | major |
| Add a new optional manifest field | minor |
| Make an optional manifest field required | major |
| Bump `schemaVersion` (breaking manifest change) | major |
| Reduce plugin fuel limit | major |
| Increase plugin fuel limit | minor |
| Rework hook payload shape (breaking) | major |
## Compatibility matrix template (reserved)
Reserved for a future boot-time compatibility sweep. The foundation
does not evaluate an installed plugin set against this table on worker
boot. Current enforcement is per install: the manifest must parse, the
`schemaVersion` must be accepted, and optional `minHostVersion` /
`maxHostVersion` constraints must include the running host version.
| Host version | Manifest `schemaVersion` accepted | Plugin SDK majors known to work | Status |
| ------------ | --------------------------------- | ------------------------------- | ---------- |
| `1.x` | `1` | `0.x` | current |
| `2.x` | `1`, `2` | `0.x`, `1.x` | planned |
When a host crosses a major boundary it must:
1. Bump the rejected `schemaVersion` set in the runtime.
2. Update this table.
3. Send a deprecation banner to every plugin author 2 majors in
advance (the marketplace surface owns the comms — backlog).
## Install-time check
`PluginLifecycleService.installPlugin` validates:
1. Manifest parses with the current Zod schema.
2. `schemaVersion` is in the host's accepted set.
3. Production runtime operations are explicitly enabled in production
with `PLUGIN_RUNTIME_PRODUCTION_ENABLED=true`.
4. Optional `minHostVersion` / `maxHostVersion` include the running
host version. Bounds are inclusive; omitting both means "any host".
5. `signaturePublicKey` matches `manifest.signedBy`.
6. `manifest.signedBy` is present in `PLUGIN_TRUSTED_PUBLIC_KEYS`.
7. The pre-positioned disk binary verifies against the submitted
`wasmSha256`, manifest signature, manifest signing key, and trusted
key set.
8. The submitted `wasmSha256` matches `manifest.wasmSha256`.
9. No plugin with the same `(tenantId, pluginId)` is already installed.
10. The tenant has not exceeded `PLUGIN_MAX_INSTALLS_PER_TENANT`.
Foundation: any of these failing rejects the install with a
`PluginLifecycleError`.
## Boot-time check (reserved)
The boot-time compatibility matrix remains reserved. If implemented,
worker boot would:
1. Read every installed `Plugin` row.
2. Compare against the running host version.
3. Mark out-of-range plugins `incompatible_host_version` and skip them
on hook fan-out until the operator updates the host or the plugin.
The foundation does not enforce this yet; plugins remain `enabled`
across host upgrades. Host-version constraints are enforced when a
plugin is installed, not during worker boot.
## Upgrade guidance for plugin authors
- **Don't ship a patch that changes capability declarations.** Patches
must be wasm-only; manifest changes are at least a minor.
- **Re-sign on every release** — the signature covers `wasmSha256` so
any binary change requires re-signing anyway.
- **Test against the host's matching SDK major** before publishing.
The host SDK ships in the same monorepo and is pinned to the host
release.
## Deviations from SemVer
Two cases break strict SemVer:
1. **Security re-signs**: changing `signedBy` (e.g. publisher key
rotation) bumps minor + records an audit-event flag so operators
notice. The change is non-functional, but operators must re-trust
the new key.
2. **Capability-scope tightening**: tightening an `allowedHosts`
list is a *user-visible* break (some calls now deny) even though
the runtime API is identical. Major bump required.
Both are tracked through the audit-log so a tenant admin can correlate
upgrades with denial-row spikes.