Skip to content

Deeper Dive: Aca-Py Plug-Ins

What's in a Plug-In and How does it Work?

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 (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 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.)

setup method

If a setup method is provided, it will be called. If not, the and will be explicitly loaded.

This would be in the package/module

async def setup(context: InjectionContext):

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 available, Aca-Py will check the following attributes to initialize the protocol(s):

  • MESSAGE_TYPES - identifies message types supported by the protocol
  • CONTROLLERS - identifies protocol controllers

If is available, then Aca-Py will call the following functions to initialize the Admin endpoints:

  • register() - registers routes for the new Admin endpoints
  • register_events() - registers an events this package will listen for/respond to

If 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 version
  • current_minor_version - specifies the protocol minor version
  • minimum_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

Loading Aca-Py Plug-Ins at Runtime

The load sequence for a plug-in (the "Startup" class depends on how Aca-Py is running - upgrade, provision or start):

  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
      opt if definition is provided
        PluginRegistry->>ExternalPlugIn:  definition()
      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()
        PluginRegistry->>PluginRegistry:  register_protocol_events()

      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)

Developing a New Plug-In

When developing a new plug-in:

  • If you are providing a new protocol or defining message types, you should include a file.
  • If you are providing a new protocol or defining message types, you should include a file.
  • If you are providing additional Admin endpoints, you should include a file.
  • If you are providing any other functionality, you should provide a file to initialize the custom functionality. No guidance is currently available for this option.

PIP vs Poetry Support

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.

Plug-In Demo


Aca-Py Plug-ins

This list was originally published in this hackmd document.

Maintainer Name Features Last Update Link
BCGov Redis Events Inbound/Outbound message queue Sep 2022
Hyperledger Aries Toolbox UI for ACA-py Aug 2022
Hyperledger Aries ACApy Plugin Toolbox Protocol Handlers Aug 2022
Indicio Data Transfer Specific Data import Aug 2022
Indicio Question & Answer Non-Aries Protocol Aug 2022
Indicio Acapy-plugin-pickup Fetching Messages from Mediator Aug 2022
Indicio Machine Readable GF Governance Framework Mar 2022
Indicio Cache Redis Cache for Scaleability Jul 2022
SICPA Dlab Kafka Events Event Bus Integration Aug 2022
SICPA Dlab DidComm Resolver Unversal Resolver for DIDComm Aug 2022
SICPA Dlab Universal Resolver Multi-ledger Reading Jul 2021
DDX mydata-did-protocol Oct 2022
BCGov Basic Message Storage Basic message storage (traction) Dec 2022
BCGov Multi-tenant Provider Multi-tenant Provider (traction) Dec 2022
BCGov Traction Innkeeper Innkeeper (traction) Feb 2023


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:

Loading plug-ins:

Versioning for plug-ins: