States
The supervisor records one status per installed plugin in/var/ados/state/plugin-state.json:
Removal deletes the install entry rather than moving it to a state.
Parse before install
The install flow is deliberately two-stage:- Parse: the host opens the archive in memory, validates the manifest, verifies the Ed25519 signature, and returns a summary. Nothing is written to disk.
- Install: only after the operator approves the requested
permissions does the host commit the install. The agent unpacks the
archive into
/var/ados/plugins/<id>/; the GCS records the install in the operator’s profile.
Enable and disable
Enable is the first time the plugin actually runs. For a subprocess plugin the agent generates a systemd unit (ados-plugin-<id>.service),
writes it under /etc/systemd/system/, reloads the daemon, and starts
the unit inside the shared ados-plugins.slice. The GCS host sets the
status to enabled and mounts the iframe in every contributing slot.
Disable stops the unit and unmounts the iframe. State on disk persists.
Re-enable resumes from where it left off.
Failure and the start limit
The per-plugin unit restarts on failure (Restart=on-failure,
RestartSec=2s). If it exceeds the start limit (5 starts within 60
seconds) systemd stops restarting it and the supervisor records the
plugin as failed. The plugin does not auto-recover from that; the
operator inspects the logs and decides whether to re-enable, remove, or
investigate. Auto-recovery would mask the underlying bug.
Lifecycle hooks (agent half)
Every hook is optional. The runner looks each one up on your plugin instance at the right point in the process lifecycle and awaits it if it returns a coroutine; a hook you do not define is simply skipped. The runner is one process per plugin. Each time the unit starts, the runner calls the startup hooks in a fixed order, waits for the shutdown signal, then calls the teardown hooks. It does not persist a marker across restarts, so the full sequence runs on every process start.
Most plugins only need
on_start and on_stop. The runner keeps the
process alive between on_start and on_stop until the supervisor
stops the unit.
A built-in agent plugin is a plain class (no base class to subclass)
named by the module:Class entrypoint; a file-path entrypoint loads a
class named Plugin. The ctx argument is the plugin context (see
event hooks and bus).
Lifecycle hooks (GCS half)
The GCS half exposesmount and unmount through definePlugin:
unmount runs when the host disposes the iframe (operator disabled the
plugin, navigated away, or removed it). Use it to flush in-flight state.
Remove
ados plugin remove <id> (or the GCS button) tears the plugin out:
- Stop the unit if running.
- Delete the unit file (and any declared extra-service units). Reload the daemon.
- Remove the install directory
/var/ados/plugins/<id>/. - Drop the install entry from the state file.
--keep-data additionally retains the plugin’s log file. The plugin’s
data directory under /var/ados/plugin-data/<id>/ is not deleted by
remove, so a reinstall can pick up where it left off.
Configuration changes
On the agent side, the config you receive is loaded once when the process starts. The runner reads it from disk and passes it toon_configure(ctx, config) during startup. To pick up a later edit
without a restart, read live values through ctx.config at the point of
use rather than caching the dict from on_configure.
On the GCS side the host emits a config.changed event that your plugin
subscribes to with ctx.config.onChange, so the iframe can react to an
edit while it stays mounted.
State observability
Every transition is recorded. Operators see the plugin’s history throughados plugin info <id> and ados plugin logs <id> on the agent, and on
the plugin detail page in Mission Control.