- An agent half: a subprocess on the node, written in Python or Rust.
- A GCS half: a TypeScript bundle that Mission Control runs in a sandboxed iframe.
agent.runtime: python (the default) or agent.runtime: rust. A Python half uses the ados.sdk package; a Rust half links the ados-sdk crate. Both speak the same length-prefixed msgpack wire to the agent’s plugin host. The GCS half uses @altnautica/plugin-sdk whatever language the agent half is written in.
When to ship two halves
Ship two halves when the plugin needs both:- Hardware or system access on the node.
- An operator-facing UI in Mission Control.
Lifecycle
Both halves install from one.adosplug archive, and the operator approves both halves’ permissions in one grid. The agent half runs under the agent’s supervisor as its own sandboxed unit; the unit and sandbox names are on Agent runtime and sandbox. The GCS half mounts whenever a slot it contributes to renders in Mission Control.
The halves start and stop independently. A GCS half can mount while its agent half is stopped, so render an offline state until state arrives.
Cross-half communication
The halves never call each other. The agent half publishes events on its own topics; the plugin host keeps the latest event per topic in the plugin’s state sidecar, and Mission Control reads that sidecar.plugin.<id>. prefix from the agent half:
plugin.<id>. prefix needs no capability. Reserved, never publishable: vehicle., mavlink., mission., safety., agent., swarm., gps., vision., telemetry., and any plugin. topic outside your own plugin.<id>. prefix or your declared shared topics. Other topics need event.publish. The full rules are on Agent event bus.
The GCS half reads these events through ctx.events.subscribe; it never opens a network connection of its own.
Shared manifest
One manifest with anagent block and a gcs block. The field reference is Manifest.
id, name, version, license, risk) are flat. Permissions are - id: <capability> entries. Both halves share one id and version, so they form one install row.
Version compatibility between halves
Both halves ship in the same archive, so they always carry the same version. What can drift between releases is the shape of the data they exchange:- Event payloads published by the agent and read by the GCS. Put a
schema_versionfield in each payload and branch on it in the GCS. - Stored data written by one half and read by the other. Carry a
data_versionin each record. See State and storage.
Driver code in the agent half
Driver code lives in the agent half. In Python, subclass one of the driver base classes (CameraDriver, GimbalDriver, LidarDriver, GpsDriver, EscDriver, PayloadActuatorDriver); in Rust, implement the matching trait. The facade is ctx.peripheral_manager:
register_*_driver calls return not_implemented. A driver plugin opens and runs its own device from its lifecycle hooks. See Driver layer.
Worked example: hybrid plugin skeleton
agent/plugin.py, the runner loads the file directly and puts its directory on sys.path. Import sibling modules by name, not with a relative import:
mount call; the manifest decides which slot each panel renders in:
Failure modes
Next steps
State and storage
Where each half keeps data and how it survives updates.
Agent event bus
Topics, publish and subscribe rules, and shared topics.