Update a plugin
For: tenant administrators and plugin authors.
Update the installed version and review changes of ownership or dependencies.
Update
oz0 plugin update github-tools
update resolves the plugin's ref again, in the same folder and with the same secret, and rolls out what it points to now, through its quality gate when it has evals. With --ref main, that is the latest commit on main; with a tag, the same commit unless the tag moved. To move to a new release, install it again with the new --ref.
A plugin installed from a plugin index (hub:<name>) remembers its listing: update resolves it again in the same index and moves to the newest active version listed there, with every check of an install (the listed commit or digest, the content, the repository's ID). It never moves back: when the newest active version is not newer than the installed one, as when the installed one was withdrawn since, the plugin stays and update says which version the index lists.
plugin list and the Plugins page say when the index lists a newer version (0.2.0 listed), from the copy of the index the cluster has; index refresh fetches a newer copy. Installing the plugin again from Git or a bundle, by hand, drops the listing.
A node restarts its runtime once the new version is ready. If the new version fails to build or start, the node keeps the previous one running and reports the failure in plugin list.
Updates from new hands
A plugin can change hands: its author sells it, hands it to a company, or transfers its repository to another account. That is normal, and it is also how a trusted plugin starts to ship someone else's code to everyone who updates it. So an install remembers who it came from, and an update never takes code from new hands without a tenant admin saying yes.
- From a plugin index, the install keeps the listed version's custody: the publisher, the repository and its owner, the date it came into those hands, and the index's review of the change when it links one. Each listed version carries a custody epoch that goes up by one at every change of hands.
From Git
The install records the repository's ID and owner as the forge reports them: GitHub (and GitHub Enterprise), GitLab, and Gitea, which covers Forgejo and Codeberg. The edge asks the forge, with the token of a private repository, so the token never leaves the edge. github.com, gitlab.com and codeberg.org are known; for a self-hosted forge, name it with oz0 plugin install <url> --forge github|gitlab|gitea.
Without a forge that reports IDs and owners (a host the engine does not know and no --forge, --forge none, or a forge that did not answer at the first install), neither a transfer nor a repository deleted and created again is noticed: an update comes from the same URL, so the owner in it never changes, and that owner is recorded for display only. Name the forge of a self-hosted GitHub Enterprise, GitLab, Gitea or Forgejo with --forge to get the protection. --forge none is remembered: later updates do not ask the forge either.
An update whose version is in other hands (another epoch, publisher, repository or owner) changes nothing yet:
oz0 plugin update github-tools
# Change request cr-5f1c...: plugin_custody of github-tools, pending
# The plugin would come from new hands. Installed from:
# epoch: 1
# publisher: io.github.alice
# - owner: alice
# The update would take it from:
# + epoch: 2
# publisher: io.github.alice
# + owner: acme-labs
# + review: https://...
oz0 change approve cr-5f1c... --delta sha256:...
It files a change request of kind plugin_custody that shows who the plugin came from and who it would come from. Approving it runs the update again, exactly as it was shown: the same custody and the same commit.
A tenant whose review-widening rule is second_admin needs an admin other than the one who asked; --approve-egress never approves a change of hands. A plugin that another plugin needs, and that the install updates first, waits the same way, and a rollout to canaries starts only after the approval.
In the console, the Update button says the update waits, and the request is on the Changes tab of Approvals.
A Git repository whose forge now reports another ID was deleted and created again, maybe by someone else, and its update is refused; so is an update when the forge does not answer with the ID it reported before. A repository transferred to another owner keeps its ID, and its update waits for a change request like any other change of hands. Installing from another URL by hand is your own new choice and asks nothing.
plugin list (UPDATE column) and the Plugins page say new hands before anyone updates, and the tenant's admins get a plugin.custody notification when a refresh of the index finds it.
Plugins installed before custody was recorded keep working. One installed from an index compares against the custody the index lists for its installed version, so its next update into new hands waits like any other, and following a move always does; when that version is no longer listed and the listing has changed hands since its first epoch, the update asks too.
One installed from Git records its forge's answer at its next update, without asking.
A plugin that moved
A plugin can also move to another name in an index, such as from a person's namespace to their company's. Both listings must say so: the old one names the new one, and the new one names the old one. The plugin's name after the slash stays the same.
A plain plugin update of a moved plugin changes nothing and says where it went. Follow it when you mean to:
oz0 plugin update github-tools --follow-move
Following a move is a change of hands too: it waits for a plugin_custody change request, which shows the old listing and publisher beside the new ones. Once approved, the tenant's plugin is the same plugin under its new listing: its grants of other plugins, settings, secrets, tool policy, tool snapshots and rollout history all stay. Installing the new listing by its name instead is refused, as any listing that would replace another listing's plugin is; plugin list shows moved / <new name>, and the Plugins page has a Follow button.
A move stated on one side only is never shown or followed. Read the change request every time: following should not become a habit.
An update whose agents' contracts break the installed version's, such as an agent that now takes JSON instead of text, is refused unless it is a new major version or you add --allow-breaking; see Upgrades keep their contracts. The Install dialog in the console has the same choice.
Updates and the plugins that use them
Installing or updating a plugin is checked against every installed plugin whose uses: names it, their installed
versions and the candidates waiting for their gate or rollout:
- the new version must be in the range each one declares (
uses.github.version: ">=1.1, <2"); - it must still have every function, agent and skill each one is granted of it.
Otherwise the update is refused with the list, as a removal with dependents is:
oz0 plugin update github
error: not allowed in the current state: plugin github 2.0.0 would break 2 plugin(s) that use it:
uses pr-reviewer: takes github >=1.1, <2, not 2.0.0 [owner team:reviews]
uses triage: is granted oz0_github.comment, agent oz0-github, which 2.0.0 lacks [owner user:ada]
Update them to versions that take it first, or update it anyway with --force --reason <why>
The list shows at most 20 plugins and how many more; oz0 plugin dependents github lists them all. plugin preview
and the install screen show the same list before you install, and the Plugins page's Update opens it with
Update anyway, which asks for the reason.
- Forced:
--force --reason <why>(at most 500 characters; the CLI takes$OZ0_REASONwhen--reasonis left out) installs it anyway. The audit log keeps the reason with the list asplugin.update.forced. A forced update without a reason is refused. The plugins it went past keep their grants, but their calls of what the version lacks fail until they move. - Optional users never refuse it: a plugin whose entry is
optional: truegoes on without what the version lacks, and the install says so in a warning. - Functions known only on a node: a version without a measured
tools.listing.jsonhas functions the install cannot see, and it cannot wait for a node to start the tools. The install says which functions are checked later; when the first node lists the version's tools, a function that a dependent is granted and the listing lacks is a warning on both plugins' pages and in the audit log (plugin.uses.functions_gone). Measure the listing (plugin testwrites it) so the install checks it at once. - Gates and rollouts check again. A plugin installed while the version waited for its quality gate or rolled out
may not take it: the gate stops instead of passing, a waiting rollout ends and drops its candidate
(
rollout.refused), and a running rollout rolls back at its end, each with the list as its reason. The plugins a forced update went past pass; any other stops it. - Nothing solves ranges across the tree, and there is no lockfile beyond the tenant's: its installed plugins, each locked to a commit or digest, are the lock. Exact combinations of versions are what sets carry.