---
title: "Publish and version"
description: "Ship a plugin with a Git tag, and control exactly what every node runs."
canonical_url: "https://docs.oz0.ai/build/plugins/publish"
---
# Publish and version

> Ship a plugin with a Git tag, and control exactly what every node runs.

For: plugin authors.

Publish a plugin as a versioned Git repository that another tenant can install. Before publishing, lock its dependencies, declare its requirements and include its documentation and licence. A hub listing lets people install that same plugin by name.

## A plugin is a Git repository

Push the plugin's folder to a Git repository the edge can reach over HTTPS. The edge fetches exactly one commit, packs it and serves it to the nodes; nodes never reach Git.

Git is not the only way: `plugin pack` builds a bundle that installs from a file or an upload, and `plugin push` publishes it to an OCI registry, both locked to the bundle's digest. See [Folders, bundles and OCI](https://docs.oz0.ai/build/plugins/sources).

One repository can also hold several plugins, each in a folder with its own `oz0-plugin.yaml`; operators install one with `plugin install --path`. A private repository works too, with a token the tenant keeps as a secret. See [Install and update](https://docs.oz0.ai/build/plugins/install/sources#a-plugin-in-a-folder).

## Versions

- `version` in `oz0-plugin.yaml` is a semantic version, such as `1.4.0`. It is for people.
- Release tags are `v1.4.0`. `plugin install` without `--ref` takes the highest `vX.Y.Z` tag, compared as numbers, or the default branch when there is none.
- The commit is what actually installs. Every install is locked to one.

Bump `version`, commit, tag and push:

```bash [Terminal]
git commit -am "ops-tools 0.2.0"
git tag v0.2.0
git push --tags
```

Then install the new tag, or let `plugin update` resolve a branch again:

```bash [Terminal]
oz0 plugin install https://github.com/your-org/ops-tools --ref v0.2.0
oz0 plugin update ops-tools
```

## Lock your dependencies

- Commit `uv.lock`. The node builds the plugin's environment with `uv sync --frozen`, so it installs exactly what you locked.
- Pin packages: `pypi:name==1.2.3`, `npm:name@1.2.3`. Unpinned packages are pinned by the edge at install time, but pinning them yourself means you know what runs.

## Say what you need

```yaml [oz0-plugin.yaml]
requires:
  oz0: ">=0.4"                       # the Orchestrator Zero versions it works with
  platforms: [linux/amd64, linux/arm64, darwin/arm64]
  secrets: [GITHUB_TOKEN]            # tenant secrets its processes need
```

`platforms` lists `os/arch` pairs from `linux`, `darwin` and `windows` with `amd64` and `arm64`; empty means every platform.

## Document and license it

`README.md` at the plugin's root is its documentation: the hub's plugin page shows it whole, with its headings, tables, images from the package and Mermaid diagrams. Say what the plugin does and for whom, how to set it up (connections, secrets, settings, models), which functions read and which write, and its Limits; link longer guides on your own site. Declare the licence in the manifest and keep its text in a licence file:

```yaml [oz0-plugin.yaml]
license: MIT            # an SPDX expression; the licence's full text is in LICENSE
```

`plugin lint` and `plugin pack` fail on a README past [its limits](https://docs.oz0.ai/build/plugins/limits/documentation#the-readme-its-images-and-its-diagrams) and on a licence file that reads as another licence than `license:` declares, and `hub submit` also fails without a licence file. `oz0 plugin new` writes a README with these sections and a `LICENSE` placeholder, which counts as no licence until you replace it with the licence's text. Every limit, its code and what to do about it is on [Plugin limits](https://docs.oz0.ai/build/plugins/limits).

## Let others install it by name

A plugin listed in a [plugin index](https://docs.oz0.ai/build/plugins/indexes) installs by name and version, `plugin install hub:<name>@<version>`, pinned to the commit and content the listing names. `oz0 hub submit` writes the listing and opens its pull request on a hub; see [List a plugin](https://docs.oz0.ai/build/plugins/list-a-plugin).

`oz0 hub search` and the console's Install dialog show the first section of your `README.md`: what follows its title, up to the next heading. Say there in a sentence or two what the plugin does and for whom. Inspect keeps paragraphs, emphasis, code, lists and links to `http`, `https` and `mailto` addresses, at most 2,000 characters, and leaves out HTML, images, relative links, tables and badges. The hub's plugin page shows the whole README ([the README is the documentation](https://docs.oz0.ai/build/plugins/list-a-plugin/review#the-readme-is-the-documentation)).

Everything else a listing says (its kind, the models it asks for, what it connects to, each function's type and default) the engine computes from your code and manifest: see [Find a plugin by what it needs](https://docs.oz0.ai/build/plugins/indexes#find-a-plugin-by-what-it-needs).

## Keep it neutral and small

One plugin per concern is easier to roll out and roll back than one plugin with everything. Say its kind
(`connector`, `capability` or `policy`) and keep the house rules before you publish: see
[How to structure plugins](https://docs.oz0.ai/build/plugins/structure), and run `oz0 plugin lint --verified` to see the rules as the
hub's checks of verified plugins see them. Put your company's models, rules and services in your own plugins; the core never needs to change for them.


## Sitemap

See the full [sitemap](https://docs.oz0.ai/sitemap.md) for all pages.
