136 lines
6.5 KiB
Markdown
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.
|