Publishing your plugin
Once you’ve deployed your Plugin Setup contract, you will be able to publish your plugin into Aragon’s plugin registry so that any Aragon DAO can install it.
How to publish a new plugin
Publishing a plugin to Aragon OSx involves a few key steps to ensure your plugin is properly registered and accessible to DAOs.
Make sure your plugin is deployed in a supported network
Make sure your Plugin Setup contract is deployed on a supported network. You can find the list of supported networks here. You will need the address of your Plugin Setup contract in order to publish your plugin to the protocol.
Publishing your plugin to Aragon OSx
Every plugin in Aragon can have multiple versions. When publishing a plugin to the Aragon protocol, we create a PluginRepo for each plugin, which will contain the plugin’s versions.
To publish a plugin, use Aragon’s PluginRepoFactory contract, which creates new PluginRepo instances for you and assigns them an ENS subdomain.
Call pluginRepo.createPluginRepoWithFirstVersion() to create the first version of your plugin, as described here.
This adds the new PluginRepo address to the PluginRepoRegistry, which tracks all available plugins in the protocol.
You can find the PluginRepoFactory contract addresses for each network here.
To build and deploy your plugin, you can look at Aragon’s template for OSx plugins.
Versioning schema
Aragon OSx has an on-chain versioning system built-in, which distinguishes between releases and builds.
-
Releases contain a major set of features, ABI and storage layout that is incompatible with older releases. You cannot upgrade to a different release; instead, install a new plugin instance with the desired release and uninstall the old one.
-
Builds are minor or patch versions within a release. They are intended for compatible upgrades (e.g., adding features or fixing bugs without breaking changes).
Builds are particularly important for UUPSUpgradeable plugins, whereas a non-upgradeable plugin will work off of only releases.
Given a version tag RELEASE.BUILD, we can infer that:
-
A
RELEASEis published when applying major, breaking changes:-
The
Pluginimplementation contract such as the-
change or removal of storage variables
-
removal of external functions
-
change of external function headers
-
-
-
A
BUILDis published when we apply forward compatible changes not affecting the interaction with other contracts:-
The
Pluginimplementation contract such as the-
addition of
-
storage variables
-
external functions
-
-
change of
-
external function bodies
-
-
addition, change, or removal of
-
internal functions
-
constants
-
immutables
-
events
-
errors
-
-
-
The
PluginSetupcontract such as-
addition, change, or removal of
-
input parameters
-
helper contracts
-
requested permissions
-
-
-
The release and build
metadataURIs such as the-
change of
-
the plugin setup ABI
-
the plugin UI components
-
the plugin description
-
-
-
Plugin Metadata Specification
The plugin metadata is needed so that the App can interact with plugins and their plugin setup:
-
Handling the plugin lifecycle (installation, upgrade, uninstallation)
-
It allows the frontend to render the expected input fields in order to setup the plugin (e.g., the list of initial members of the Multisig plugin)
Currently, two kinds of metadata exist:
-
Release metadata
-
Build metadata
Release Metadata
The release metadata is a JSON file pinned on IPFS with its IPFS CID published for each release in the PluginRepo(see also the section about versioning).
The intention is to provide an appealing overview of each release’s functionality.
It can be updated with each call to createVersion() in IPluginRepo by the repo maintainer.
It can be replaced at any time with updateReleaseMetadata() in IPluginRepo by the repo maintainer.
The release-metadata.json file consists of the following entries:
| Key | Type | Description |
|---|---|---|
name |
|
Name of the plugin (e.g. |
description |
|
Description of the plugin release and its functionality. |
images |
UNSPECIFIED |
Optional. Contains a series of images advertising the plugin’s functionality. |
Build Metadata
The build metadata is a JSON file pinned on IPFS, with its IPFS CID published once per build in the PluginRepo (see also the section on versioning).
The intention is to inform users about the changes introduced in this build compared to the previous one, and to provide instructions to the App frontend on how to interact with the plugin’s setup and implementation contracts.
A build can be published only once by calling createVersion() in IPluginRepo by the repo maintainer.
| Key | Type | Description |
|---|---|---|
ui |
UNSPECIFIED |
A specially formatted object containing instructions for the App frontend on how to render the plugin’s UI. |
change |
|
Description of the code and UI changes compared to the previous build within the same release. |
pluginSetup |
|
Optional. Contains configuration details for the plugin setup, not images. |
Each build metadata contains the following fields:
-
one
"prepareInstallation"object -
one
"prepareUninstallation"object -
0 to N
"prepareUpdate"objects enumerated from 1 to N+1
Each "prepare…" object contains:
| Key | Type | Description |
|---|---|---|
description |
|
Describes what this setup step does and what input it requires. |
inputs |
|
A description of the inputs required for this setup step following the Solidity JSON ABI format enriched with an additional |
By following the Solidity JSON ABI format for inputs, we adhere to an established standard with support for complex types (tuples, arrays, nested structures) and enable future extensibility—such as human-readable description fields.
Example
{
"ui": {},
"change": "- The ability to create a proposal now depends on the membership status of the current instead of the snapshot block.\n- Added a check ensuring that the initial member list cannot overflow.",
"pluginSetup": {
"prepareInstallation": {
"description": "The information required for the installation.",
"inputs": [
{
"internalType": "address[]",
"name": "members",
"type": "address[]",
"description": "The addresses of the initial members to be added."
},
{
"components": [
{
"internalType": "bool",
"name": "onlyListed",
"type": "bool",
"description": "Whether only listed addresses can create a proposal or not."
},
{
"internalType": "uint16",
"name": "minApprovals",
"type": "uint16",
"description": "The minimal number of approvals required for a proposal to pass."
}
],
"internalType": "struct Multisig.MultisigSettings",
"name": "multisigSettings",
"type": "tuple",
"description": "The initial multisig settings."
}
],
"prepareUpdate": {
"1": {
"description": "No input is required for the update.",
"inputs": []
}
},
"prepareUninstallation": {
"description": "No input is required for the uninstallation.",
"inputs": []
}
}
}
}