New plugin index

As various topics have mentioned, the 2.0 release will bring with it an overhaul of the plugin framework. Two components of this are a new Avogadro-plugin API, and a new way for plugins to specify metadata for Avogadro (in TOML files). These have been previously discussed, and are pretty much implemented.

The third and final component of the plugin framework is the broader infrastructure that provides plugins to users. This is currently handled by a combination of the Avogadro/plugins GitHub repository and the plugin index at https://avogadro.cc/plugins.json. Since the infrastructure is designed to work with the current plugins and the current metadata format, it needs adjusting, which I’ve been working on doing.

This post is to:

  1. summarize the current state of affairs;
  2. summarize the work done and decisions made so far;
  3. pose a few remaining questions.

Hopefully this means those interested are kept abreast of the development progress. Anyone is welcome to contribute opinions and perspectives if they like!

Current state

The infrastructure is currently set up as follows:

  • The Avogadro/plugins repository currently comprises a register of plugins, called repositories.txt, and a Python script, cachedetails.py
  • The register contains a list of plugin repository URLs and nothing more.
  • A plugin author can apply for their plugin to be made available to Avogadro users in the plugin downloader dialog by requesting that the plugin’s repo is added to the register.
  • The script is responsible for processing the register, fetching information from each plugin’s source, and compiling an index file, plugins.json, from that information.
  • The generated index is uploaded to https://avogadro.cc/plugins.json; this file at this URL is then the actual index, and is accessed by Avogadro to get information on the available plugins.
  • When installing a plugin, Avogadro does not download the plugin’s code from some central repository that the project maintains, it downloads the code directly from the plugin’s own repo – all Avogadro gets from the index is some metadata and the URL for the source repo.
  • The information in the index includes the plugin’s name and version, the type of plugin, and the date and time of the last update.
  • Which version of a plugin to use is not specified in the register. Avogadro simply downloads the latest source code of the main or master branch, unless the plugin uses GitHub’s Releases feature, in which case Avogadro downloads the most recent release instead.
  • To collect the metadata for the index, the script combines information from each plugin’s metadata file (which until now was called plugin.json) as well as the GitHub repo’s metadata. If the same thing is specified differently in several places it picks one to use preferentially: for example, the plugin name, authors, and description are taken preferentially from plugin.json, but the version number is taken preferentially from the release name (but only if there is a release, of course).
  • The different sources of metadata, and correct metadata not being enforced, leads to problems like the the PyPept plugin being listed in the plugin downloader with the name commands and the same description as the actual commands plugin
  • The index is generated by the script, but the script is run manually, and the upload of the index file to https://avogadro.cc/plugins.json is also manual.
  • When a new version of a plugin is created, the plugin index becomes outdated and inaccurate. After making a new version, plugin authors have to request that the index is regenerated. The plugin register is not changed.
  • If a plugin does not use GH releases, Avogadro always fetches the latest code, so any changes to the code of the plugin are automatically given to anyone who subsequently downloads the plugin, even if the index hasn’t been updated. If the version number doesn’t get updated, different users can have different versions of the same plugin while each still has the same version number.

Changes and decisions already made

Updates to all this (register, script, and index) are necessary anyway for the new API and metadata format, but various aspects of the above are also unsatisfactory.

The current state of work on the Avogadro/plugins repository can be seen in my branch for the purpose at GitHub - matterhorn103/plugins at api2.

Some general principles

  • The old register and script are to be retained for now.
  • Information that is necessary for Avogadro to handle plugins properly prior to download is to be stored in the plugin index, which means plugin authors must specify it either in repositories.toml or the plugin’s metadata TOML, and it is now mandatory to provide it. This way, Avogadro does not have to inspect the plugin’s code prior to download
    • This applies to the download/installation mechanisms and to the main area of the plugin downloader dialog; things like the README of a plugin are not intended
  • In future, we might want to allow sources other than GitHub; as such, plugin metadata should be, as far as possible, independent from the structure or metadata of the Git(Hub) repo itself.
  • All pieces of information put into the index should come from a single source, never selected from amongst multiple possibilities. Conflicts and inconsistencies are thus avoided. From now on, for example, the plugin’s description is optional, but if one is provided it must be specified in the plugin’s metadata TOML, and is never taken from the GH repo description any more.
  • The code of plugins made available to Avogadro users via the Avogadro infrastructure should never change without the knowledge and approval of the Avogadro team.

Changes so far

  • Everything has been updated to reflect the changes to the API and plugin metadata file (which is now contained in either pyproject.toml or avogadro.toml).
  • The register for new (>=2.0) plugins is in a repositories.toml file.
  • There is a new script, generate_index.py, for generating the new index.
  • Plugin authors are required to specify more information in the register than before:
    • The plugin name
      • The name is the key of the plugin’s table in the TOML – this is a convenient way to prevent two plugins ever having the same name
    • The URL of the git repository of the plugin’s source
      • This is basically the same as before, but because it’s separate to the name, it allows the name of the plugin to differ from the name of the GH repository (e.g. I’d like the xtb plugin to just be called xtb within Avogadro, but the repo is called avo_xtb to distinguish it from the actual xtb program)
    • An optional path to the plugin relative to the top of the git repo
      • This allows for a project to make an Avogadro plugin for the project and keep it with the rest of the project’s code, but not have to have it at top level. This would allow the PyPept plugin to be merged into the parent PyPept project, for example. Would also have meant I wouldn’t have had to split easyxtb and avo_xtb across two separate repos.
    • The plugin metadata file being used
      • Makes the script much simpler if it’s known in advance
    • The type of the plugin
      • This could be removed, but is the only source of the information and might be useful for the plugin downloader to have without having to work it out
    • A specific git commit
  • The script combines information for each plugin from repositories.toml, the plugin’s metadata file, and the GitHub metadata of the repository.
    name, version, authors, and license are now mandatory keys in the plugin metadata file.
  • The script now validates some aspects of the metadata it collects e.g. that version numbers are strings rather than floats. If the script errors due to bad metadata, the responsible plugin won’t be accepted into the index.
  • GitHub releases are no longer a source of information. However, if releases are in use for the plugin’s repository, the script will check that the commit indicated in repositories.toml corresponds to a release, and that the version numbers match. This is to prevent the code that users install in Avogadro for version x being different from the code in the plugin’s repo released as version x, which would be deceiving. This will result in a warning, not an error, so as not to prevent the “project provides an Avogadro plugin in a subdirectory rather than a separate repo” setup.
  • A plugin will always be downloaded by Avogadro by reference to a specific commit, not to a release. This is because releases can be deleted or changed, so the release might not have the same code in future, whereas a commit hash is essentially unique and unchangeable.
  • Plugins are in future to be made available to users by changing repositories.toml via a pull request, which the Avogadro team will merge. A mixture of manual and automatic vetting of plugins will likely be applied. Importantly, updates to existing plugins are also released via the same pull request mechanism.
  • Index generation and upload remains manual for now but will likely be automated as a GitHub action that is triggered by the merge of a PR. Since in future all changes to plugins will require a PR, this will work nicely to keep the index always up-to-date.

The resulting index

All this affords an index file that looks like this (indentation added to make it more readable here – the version online wouldn’t have it):

[
  {
    "name": "pypkg-demo",
    "version": "6.0.2",
    "authors": [
      {
        "name": "Amedeo Avogadro",
        "email": "[email protected]"
      }
    ],
    "license": "BSD-3-Clause",
    "description": "An example Python package plugin for Avogadro 2 to demonstrate the revamped plugin API in v2.0 onwards",
    "minimum-avogadro-version": "1.103",
    "feature-types": [
      "electrostatic-models",
      "energy-models",
      "file-formats",
      "input-generators",
      "menu-commands"
    ],
    "git": "https://github.com/matterhorn103/avo-plugin-demo.git",
    "path": "pypkg-demo",
    "metadata": "pyproject.toml",
    "commit": "dbbc4d49396ac2b7746abb0bec0af6e577d8c0c5",
    "plugin-type": "pypkg",
    "last-update": "2026-02-16T02:18:19Z",
    "commit-timestamp": "2026-02-16T02:18:14Z",
    "has-release": false
  },
  {
    "name": "avogenerators",
    "version": "2.0",
    "authors": [
      {
        "name": "Allison Vacanti",
        "email": "[email protected]"
      },
      {
        "name": "Kyle Lutz",
        "email": "[email protected]"
      },
      {
        "name": "Marcus D. Hanwell",
        "email": "[email protected]"
      },
      {
        "name": "Geoffrey Hutchison",
        "email": "[email protected]"
      },
      {
        "name": "Eric Berquist",
        "email": "[email protected]"
      },
      {
        "name": "Shiv Upadhyay",
        "email": "[email protected]"
      },
      {
        "name": "Amanda E. Dumi",
        "email": "[email protected]"
      },
      {
        "name": "Adrea Snow",
        "email": "[email protected]"
      },
      {
        "name": "Christian Clauss",
        "email": "[email protected]"
      },
      {
        "name": "Ty Balduf",
        "email": "[email protected]"
      },
      {
        "name": "Javier Cerezo",
        "email": "[email protected]"
      },
      {
        "name": "Matthew Milner",
        "email": "[email protected]"
      }
    ],
    "license": "BSD-3-Clause",
    "description": "Scripts for generating input files for computational chemistry packages",
    "minimum-avogadro-version": "1.103",
    "feature-types": [
      "input-generators"
    ],
    "git": "https://github.com/OpenChemistry/avogenerators.git",
    "metadata": "pyproject.toml",
    "commit": "9843e35d4009040847dab2f49cd108a1c5391e0a",
    "plugin-type": "pypkg",
    "last-update": "2026-02-21T19:59:00Z",
    "commit-timestamp": "2026-02-21T19:58:56Z",
    "has-release": false
  }
]

If a plugin has GH releases, the index would additionally contain release-tag and release-version.

@ghutchis Are you happy with this? Would you like to see any other information included in the index, or is this enough for the plugin downloader to do everything it needs to do without downloading the whole plugin’s code?

Remaining questions

  • Does the new index contain all necessary information?
  • Where should the new index be put?
    • The intention is to put it at a different URL e.g. https://avogadro.cc/plugins2.json, so that the old index can remain online and people using Avogadro versions <2.0 can still get plugins the same way they do now
  • Might it be an idea to have a copy of the index in the plugins repo as well as on the website, and have the plugin downloader in Avogadro check both in case one or the other is unavailable? In case the Avogadro code moves to GitLab, for example, or for any times when the website is down.
  • Should the plugins repo move to the OpenChemistry organization to sit alongside the other Avogadro repos? (The same question could apply to the documentation/website, which is at Avogadro/two.avogadro.cc.)
  • When should the old infrastructure be retired? (Note that if any plugins that don’t use GitHub releases choose to update for the new API, those plugins will stop working with Avogadro <2.0, because the code downloaded is always the latest for release-less plugin repos.)

All feedback welcome!

I’d probably prefer a path to the zip / tarball, e.g. Downloading source code archives - GitHub Docs

This has two advantages - the C++ code doesn’t have to figure out the ZIP path itself, but also GitLab and other hosting services have “download ZIP from a commit” with slightly different path schemes.

It might be nice to have some sort of “popularity” indication, e.g. stars, download count, etc. if not at first then eventually.

The website is hosted by GitHub (i.e., Microsoft) and cached by CloudFlare. Moreover, it’s a set of static files. So if the website is down, bad things are happening across the internet.

I’d leave it up, statically for the foreseeable future. If you check avogadro2 package versions - Repology you’ll see plenty of users with older versions. Someone just reported a bug from 1.99.

I think most of the plugins in the old index use releases?

OK, that’s fine, I can add a src key. That’ll mean also having a sha256 key so that the integrity of the download can be checked. And I’ll retain the git key since sometimes Avogadro makes use of Git to do the downloads now.

Agreed, and that shouldn’t be too hard to add.

If you think it’s unnecessary then that’s fine, was only a thought. Am I imagining it though or were there certificate issues a couple of times?

It’s an 8:7 split, but the bundled ones (avogenerators, molecules, crystals, i18n) represent half of the 8 with releases.

If we say that the old index is officially frozen and will never be regenerated using the script, we can manually edit plugins.json going forward, and we could just set the zipball_url key of those 7 to point not to HEAD but to the current commit of the pre-2.0 version. That’d be problem solved I believe.

1 Like

I’ll have to check - some of the download code assumes the filenames have a particular pattern. But otherwise this sounds like the right idea.

I haven’t created tags / releases, but the following plugins have updates for the packages:

  • avogadro-cclib
  • avogadro-ase
  • avogadro-rdkit
  • avogadro-genice

I’ll see if I can get an update to TurtleMol since that’s a great way to generate assemblies / solvate. But I think we’ve got an initial set for testing the download capabilities soon.

Okay, here’s a draft of the new “Manage Packages…” window.

This is very much a work in progress. I’m inclined to merge the “Installed” and “Available” into one column “Version” to give more space to “Description” .. and add some header pieces to the README browser (e.g., authors, etc.)

  • Install from Directory… let’s you pick a local directory with a package and it creates a symlink (making it easier to develop)
    • I probably want it to also accept a .tar.gz or .zip and check it for a package
  • I’m picking somewhat arbitrary icons from the Qt icon theme (essentially the Linux desktop icon themes) SUGGESTIONS WELCOME
  • What other columns should be visible?
  • What other data should be available for filtering?
  • What kids of data should show up on the “preview” side when you select a package?

I welcome feedback, ideas, critiques…

1 Like

Looks, and sounds, great!

It’d be nice to have a column for “Features” or “Functionality” that shows “Input Generators”, “Menu Commands” etc., that can also be filtered on.

Okay, now that there’s an actual plugin index, I made some progress:

I’m leaning towards icons for features:

  • :atom_symbol: for input generators (e.g., quantum chemistry??)
  • E for energy-models (??)
  • :high_voltage: for electrostatics
  • :page_facing_up: for file formats
  • ☰ for menu commands

I’m not a huge fan of any of these, but it was easier to use emoji than icons at the moment. Ideas are definitely welcome

I’m also going to tweak the “readme browser” to add a few features to the top, e.g. version, authors, stars, etc. If you’ve got other ideas, please let me know.

How about using the icon of the AutoOpt Tool?

1 Like

What I discovered is that using icons is kinda a pain. The current behavior puts together the icons for whatever features are declared (e.g., all of them for the demo).

When I did that with icons, it was hard to get them to scale well, whereas for emoji it works great.

I guess we could include Font Awesome?

I want to come back to the plugin manager window now that we have ~17 plugins (and more to come):

I’m wondering if the list should be organized into “folders” based on features - with of course some plugins showing up in multiple places:

  • Commands
    • xtb
    • crest
    • turtlemol
  • File Formats
    • XSF
    • cclib
    • MDTraj hd5
  • Energy Models
    • ANI
    • AIMNET
    • etc.

I think this will help because I can easily see with 25-30 plugins (at least) even the search / filter interface will become overloaded.

What’s the practical difference between that and applying a filter?

I think it might help in discoverability? I guess I’m worried some people might not think to filter.

But it’s probably something we can put off until later. As a quick count, we currently have 17 plugins. I can think of 4-5 more energy models, commands, electrostatics, etc. .… it’s probably still okay as a list for now and we revisit organization if we get to 50-60.

I’ve got a build of the latest code in front of me, and I can’t see how you filter, only sort?

You filter with the search field. But that’s kinda why I suggested adding category “folders”

I’m not sure if I’m taking you too literally with the “folders” concept, but I think that would really greatly reduce discoverability. It then becomes a matter of users trying to figure out what sort of thing they’re looking for, and that seems like it’d just raise the barrier to use.

I think there should probably be sort options, e.g. sort by popularity (number of downloads), sort by date added/updated, sort by category, that sort of thing.

This could be done in addition to folders, but would be a more familiar interface for users I think.

How about just a drop-down to choose, labelled Category? With the default set to “All”. Best of both worlds.

It already works like that :slight_smile:

1 Like

Looks like it’s perfect already then!

I do like this, it feels like the right idea.

We don’t currently have the number of downloads, but yes, you can sort columns.