Skip to main content
A plugin lives across many host versions and many of its own versions. Semver gives operators a way to predict what an upgrade will and will not change, and the agent’s automatic updates rely on it.

Semver for plugins

Use MAJOR.MINOR.PATCH: The rule of thumb: an operator running an old config on a new MINOR version should see a working plugin. A new MAJOR version warrants reading the changelog.

Compatibility ranges

The manifest declares which host versions it supports:
The host parses each range as comma-separated atoms, where every atom is an operator followed by a semver: Combine atoms with a comma (logical AND): ">=0.10.0,<1.0.0". A bare semver with no operator means exact match. Pre-release and build suffixes are stripped before comparison, so 1.2.3-rc.1 compares as 1.2.3. Caret (^) and tilde (~) shorthand are not supported; write the bounds out. supported_boards is an exact board-id list. An empty list, an absent field or ["*"] means any board. To restrict the plugin, list the board ids it runs on; the ids are on Supported hardware. The compatibility check runs at install time. If the running agent version is outside ados_version, the install (or the install of a newer archive over an existing plugin) is rejected. Mission Control checks gcs_version against its own build.

ados_version vs gcs_version

ados_version matches the running ADOS Drone Agent version. gcs_version matches the running Mission Control build. They move independently, so a plugin can support a wide agent range and a narrow GCS range or the reverse. The compatibility block and its ados_version field are required on every manifest, even for a GCS-only plugin; set a wide range if the plugin does not depend on a specific agent. gcs_version is optional; omit it for an agent-only plugin.

Manual update

  1. The operator installs a newer .adosplug over the installed plugin.
  2. The host parses the archive and checks the signature, compatibility and board. A failing check rejects the update and leaves the old version running.
  3. Grants the new version still declares carry over, permissions it no longer declares are dropped, and a newly requested permission starts ungranted until the operator approves it.
  4. The host stops the running plugin, replaces the install directory, re-renders the units and starts the new version.
  5. On start the lifecycle hooks run in order (on_install, on_enable, on_configure, on_start). Read your live config through ctx.config_kv and migrate your data directory if needed.
The install methods are on Local install.

Automatic updates

Once a day the agent compares every enabled plugin with the newest version the registry publishes and does one of the following:
  • Installs it silently when the update is a patch or minor bump with an unchanged permission set and code surface, still supports the board, and is signed by the same key as the installed version. It goes through the normal install checks; prior grants are restored and the plugin is re-enabled. A refused archive leaves the old version in place.
  • Notifies the operator for a major bump, a permission change, a change to what the plugin can run (risk, declared services, spawn allowlist, binaries, payload files), a dropped board, or a different signer. The operator decides. A signer change is refused as an update; it needs a remove and a fresh install.
  • Skips it when the plugin is pinned, auto-update is off for it, or nothing newer exists.
The whole cycle is deferred while the vehicle counts as armed (unknown or stale state counts as armed), because an update stops the plugin. Control it per plugin:
To get silent updates, keep patch and minor releases additive and sign every release with the same key.

Additive vs breaking permission changes

Additive (MINOR safe):
  • Adding an optional permission.
  • Adding a slot contribution.
  • Adding a config field with a default value.
Breaking (MAJOR required):
  • Adding a required permission.
  • Removing a slot the operator depends on.
  • Renaming a config field without a migration.
  • Changing the wire format of a plugin.<id>.* event topic.
  • Changing default behavior in a way the operator would notice.
Any permission change, additive or not, turns an automatic update into a notification.

Config migration

Carry a version field in your config and migrate forward inside on_configure:

Data migration

There is no SDK migration helper. Carry a version marker in your data directory and migrate forward on start, handling skipped versions:
Forward migration is the contract; reading new data with an older plugin is best-effort.

Rollback

Installing an older version over a newer one is refused (rollback protection). To roll back:
Without --keep-data the data directory is deleted. With it, your older version reads data a newer version may have migrated, so your code has to tolerate that. Pin the plugin afterwards if auto-update would otherwise bring the newer version back.

Pre-release versions

Pre-releases use semver pre-release identifiers (1.0.0-rc.1). Treat a pre-release as older than its equivalent release, so 1.0.0 supersedes 1.0.0-rc.1. Do not ship -rc builds to operators who did not ask for them; the local-install path accepts one.

Communicating breaking changes

A MAJOR version is contract-breaking. Add a migration section to the README, optionally ship a one-shot migration that imports old state into the new format, and raise ados_version if the change needs a newer host. The host does not detect lost user data on upgrade; that responsibility is the plugin author’s.

Next steps

Local install

Install and update a packed archive on a node.

Performance and budgets

Resource caps, throttling and profiling.
Last modified on October 11, 2026