Plug-ins are loaded on Aca-Py startup based on the following parameters:
--plugin
- identifies the plug-in library to load--block-plugin
- identifies plug-ins (including built-ins) that are not to be loaded--plugin-config
- identify a configuration parameter for a plug-in--plugin-config-value
- identify a value for a plug-in configuration
The --plug-in
parameter specifies a package that is loaded by Aca-Py at runtime, and extends Aca-Py by adding support for additional protocols and message types, and/or extending the Admin API with additional endpoints.
The original plug-in design (which we will call the "old" model) explicitly indluded message_types.py
routes.py
(to add Admin API's). But functionality was added later (we'll call this the "new" model) to allow the plug-in to include a generic setup
package that could perform arbitrary initialization. The "new" model also includes support for a definition.py
file that can specify plug-in version information (major/minor plug-in version, as well as the minimum supported version (if another agent is running an older version of the plug-in)).
You can discover which plug-ins are installed in an aca-py instance by calling (in the "server" section) the GET /plugins
endpoint. (Note that this will return all loaded protocols, including the built-ins. You can call the GET /status/config
to inspect the Aca-Py configuration, which will include the configuration for the external plug-ins.)
If a setup method is provided, it will be called. If not, the message_types.py
and routes.py
will be explicitly loaded.
This would be in the package/module __init__.py
:
async def setup(context: InjectionContext):
pass
TODO I couldn't find an implementation of a custom setup
in any of the existing plug-ins, so I'm not completely sure what are the best practices for this option.
When loading a plug-in, if there is a message_types.py
available, Aca-Py will check the following attributes to initialize the protocol(s):
MESSAGE_TYPES
- identifies message types supported by the protocolCONTROLLERS
- identifies protocol controllers
If routes.py
is available, then Aca-Py will call the following functions to initialize the Admin endpoints:
register()
- registers routes for the new Admin endpointsregister_events()
- registers an events this package will listen for/respond to
If definition.py
is available, Aca-Py will read this package to determine protocol version information. An example follows (this is an example that specifies two protocol versions):
versions = [
{
"major_version": 1,
"minimum_minor_version": 0,
"current_minor_version": 0,
"path": "v1_0",
},
{
"major_version": 2,
"minimum_minor_version": 0,
"current_minor_version": 0,
"path": "v2_0",
},
]
The attributes are:
major_version
- specifies the protocol major versioncurrent_minor_version
- specifies the protocol minor versionminimum_minor_version
- specifies the minimum supported version (if a lower version is installed in another agent)path
- specifies the sub-path within the package for this version
The load sequence for a plug-in (the "Startup" class depends on how Aca-Py is running - upgrade
, provision
or start
):
sequenceDiagram
participant Startup
Note right of Startup: Configuration is loaded on startup<br/>from aca-py config params
Startup->>+ArgParse: configure
ArgParse->>settings: ["external_plugins"]
ArgParse->>settings: ["blocked_plugins"]
Startup->>+Conductor: setup()
Note right of Conductor: Each configured plug-in is validated and loaded
Conductor->>DefaultContext: build_context()
DefaultContext->>DefaultContext: load_plugins()
DefaultContext->>+PluginRegistry: register_package() (for built-in protocols)
PluginRegistry->>PluginRegistry: register_plugin() (for each sub-package)
DefaultContext->>PluginRegistry: register_plugin() (for non-protocol built-ins)
loop for each external plug-in
DefaultContext->>PluginRegistry: register_plugin()
alt if a setup method is provided
PluginRegistry->>ExternalPlugIn: has setup
else if routes and/or message_types are provided
PluginRegistry->>ExternalPlugIn: has routes
PluginRegistry->>ExternalPlugIn: has message_types
end
opt if definition is provided
PluginRegistry->>ExternalPlugIn: definition()
end
end
DefaultContext->>PluginRegistry: init_context()
loop for each external plug-in
alt if a setup method is provided
PluginRegistry->>ExternalPlugIn: setup()
else if a setup method is NOT provided
PluginRegistry->>PluginRegistry: load_protocols()
PluginRegistry->>PluginRegistry: load_protocol_version()
PluginRegistry->>ProtocolRegistry: register_message_types()
PluginRegistry->>ProtocolRegistry: register_controllers()
end
PluginRegistry->>PluginRegistry: register_protocol_events()
end
Conductor->>Conductor: load_transports()
Note right of Conductor: If the admin server is enabled, plug-in routes are added
Conductor->>AdminServer: create admin server if enabled
Startup->>Conductor: start()
Conductor->>Conductor: start_transports()
Conductor->>AdminServer: start()
Note right of Startup: the following represents an<br/>admin server api request
Startup->>AdminServer: setup_context() (called on each request)
AdminServer->>PluginRegistry: register_admin_routes()
loop for each external plug-in
PluginRegistry->>ExternalPlugIn: routes.register() (to register endpoints)
end
When developing a new plug-in:
- If you are providing a new protocol or defining message types, you should include a
definition.py
file. - If you are providing a new protocol or defining message types, you should include a
message_types.py
file. - If you are providing additional Admin endpoints, you should include a
routes.py
file. - If you are providing any other functionality, you should provide a
setup.py
file to initialize the custom functionality. No guidance is currently available for this option.
Most Aca-Py plug-ins provide support for installing the plug-in using poetry. It is recommended to include support in your package for installing using either pip or poetry, to provide maximum support for users of your plug-in.
TBD
This list was originally published in this hackmd document.
The following links may be helpful or provide additional context for the current plug-in support. (These are links to issues or pull requests that were raised during plug-in development.)
Configuration params: openwallet-foundation#1121 https://hackmd.io/ROUzENdpQ12cz3UB9qk1nA openwallet-foundation#1226
Loading plug-ins: openwallet-foundation#1086
Versioning for plug-ins: openwallet-foundation#443