Publishing to the Addon Registry
The phpvms addon registry is the central index of community addons. Once your addon is registered, operators can discover and install it directly from their phpvms admin UI — they never need to clone repos or run composer commands.
This guide is the short reference. The full plugin authors guide in the registry repo is the authoritative source.
Prerequisites
Before submitting:
- A public GitHub repository for your addon.
- At least one published GitHub release with a zip asset attached.
- The zip contains
module.jsonat its root (not inside a subdirectory). - The zip's
module.jsondeclares aregistry_idequal to your registry name (for example,acme/reports). See Addingregistry_idto module.json below. - All migrations under
database/migrations/follow the migration rules.
Adding registry_id to module.json
The alias field in module.json is restricted to lowercase identifiers
(letters, digits, underscores) because Laravel uses it as the namespace for
views, configs, and translations — for example view('reports::index').
Registry names use the {author}/{package} form, which is not a legal alias.
Add a separate registry_id field to module.json so the registry can match
your addon to its YAML entry:
{
"name": "Reports",
"alias": "reports",
"registry_id": "acme/reports",
"description": "KPIs, route performance, scheduled exports.",
"keywords": ["reports", "analytics"],
"priority": 0,
"providers": [
"Modules\\Reports\\Providers\\ReportsServiceProvider"
],
"files": []
}
name— display name. Free-form.alias— Laravel namespace. Lowercase, no slashes.registry_id— must equal thename:value in yourpackages/{author}/{name}.yml.
What you submit
A single YAML file at packages/{author}/{name}.yml describing your addon.
You do not submit a release: block — the registry's bot queries your
GitHub releases and adds it after merge.
If your namespace doesn't exist yet, also submit packages/{author}/meta.yml in
the same PR.
Naming rules
Registry names use the form {author}/{package}:
- Lowercase letters, digits, and hyphens only.
- Each segment is at least two characters.
- No underscores, no uppercase, no periods.
- The slug
metais reserved (it denotes namespace metadata).
Conventions (recommended, not enforced):
- The
{author}segment should match your GitHub username or organisation. This keeps ownership unambiguous and makes the source repo easy to find. - The
{package}segment should match the GitHub repository name of the addon source. So an addon hosted athttps://github.com/acme/reports-addonis best registered asacme/reports-addon.
Examples that follow the convention: acme/reports, phpvms/core-tools,
crew-tools/dispatch.
Minimal package YAML
# packages/acme/reports.yml
name: acme/reports
description:
Reports addon for phpvms — KPIs, route performance, scheduled exports.
category: reporting
license: MIT
keywords:
- reports
- analytics
- dashboard
source:
type: github-release
repository: acme/reports-addon
requirements:
php: '>=8.3'
phpvms: '>=7.0.0'
That's the entire submission. The bot resolves the release: block after merge.
Allowed category values
Pick exactly one. The current list lives in
schema/categories.yml:
accountingcommunicationscrewdev-toolsintegrationoperationspirepsreportingschedulingtemplatesuiwidgetother
To request a new category, open a separate PR adding it to that file before submitting your package YAML.
meta.yml (first-time author)
If your namespace is new (no other addons under packages/{author}/), include
packages/{author}/meta.yml in the same PR:
# packages/acme/meta.yml
name: Acme Corp
url: https://acme.example.com
maintainers:
- acme-dev
- jdoe
maintainers is a list of GitHub usernames. The first listed is treated as the
primary contact.
What CI checks at PR time
- Schema — required fields, valid
nameregex, allowed category, requirements present. - Filename matches name —
acme/reportslives atpackages/acme/reports.yml, no exceptions. - Source repo exists and is public.
- Latest release — at least one published release with a zip asset.
- Zip integrity — downloadable, contains
module.jsonat the root, no forbidden paths (.git/,.github/,tests/,node_modules/,.idea/,.vscode/,.DS_Store,Tests/). - module.json — schema-valid;
registry_idequals the registry name. - Migration lint — see Migration rules.
The validator posts a single comment summarising results. If everything passes,
the comment includes the proposed release: block.
What happens after merge
- The
release-blockworkflow opens an auto-merging bot PR appending the resolvedrelease:block to your YAML. - The
publishworkflow buildsraw/packages.jsonandraw/keywords.json, uploads them to R2, and refreshes the worker's edge cache. - Hosts polling the read API see your addon within a few minutes.
- Subsequent releases on your repo are picked up by the discovery sweep (cron every 6h, plus push triggers from the worker).
Updating your addon
Tag a new release on your GitHub repo. The discovery sweep opens an auto-merging
bot/bump-{author}-{name}-{version} PR within hours.
You do not interact with the registry repository for routine updates.
Marking an addon revoked or archived
Revocation and archival are maintainer actions, not author actions. See
revocation.md
in the registry repo for the process.