Skip to main content
Hardware drivers sometimes need a closed-source library to talk to the device: a camera SDK, a LiDAR decompressor, a mount-control library. A plugin can include these binaries, but the manifest must declare the inclusion so operators see it in the install review.

Declare the vendor binary

The agent half sets contains_vendor_binary: true and provides vendor_attribution. The two go together: the schema rejects one without the other. Both fields arrived in schema version 2; set schema_version: 2 or later to mark the manifest generation. vendor_attribution is a list, so a plugin can attribute more than one upstream:
There is no per-binary path or sha256 field. The archive’s SIGNATURE covers every entry’s hash, so the vendor binary is protected by the same signature as the rest of the archive. license is the only required attribution field; the rest are optional provenance (name, source_url, source_offer_url, upstream_repo, upstream_version, commit_sha, notice). For a GPL-compatible upstream, set at least one of source_url or source_offer_url plus a version pin (upstream_version or commit_sha) so the install review can show a real source offer. ados plugin lint reports VND001 when contains_vendor_binary is set, as a reminder to check the attribution. See Linting.

Trust signal in the install review

When contains_vendor_binary is true, the install review in Mission Control shows the vendor attribution and labels the plugin as carrying a vendor binary. The operator can still install.

Loading a library in-process

The agent unpacks the archive under /var/ados/plugins/<plugin-id>/. Resolve the library relative to your plugin module and load it with ctypes:
The library runs inside the plugin’s sandbox and cannot exceed the plugin’s permissions. A vendor library that opens a USB camera still needs the plugin to declare hardware.usb.uvc. Nothing checks that an in-process .so matches the board’s architecture. If your plugin runs on more than one, ship one subfolder per architecture and pick at runtime:

Running vendor code as a separate process

If the vendor code must run as its own process, the plugin needs the process.spawn capability (risk high) plus a subprocess_spawn allowlist. Entries are binary basenames, not paths. Any ctx.process.spawn(...) whose basename is not on the list is refused, and the manifest fails validation if subprocess_spawn is set without process.spawn in permissions.
Each basename resolves to <install_dir>/vendor/<basename>. The archive is extracted verbatim, so ship spawned binaries at top-level vendor/<basename> in the archive; they land at /var/ados/plugins/<plugin-id>/vendor/<basename>. Shell metacharacters and path traversal in a basename are refused.

Per-architecture binaries

A Rust agent half declares its compiled binaries per architecture in agent.binaries, a map of {<name>: {<arch>-<os>: <path in the archive>}}. The supported keys are aarch64-linux, x86_64-linux and aarch64-macos. bin:<name> in agent.entrypoint or a service command resolves through the table on the installing host.
A bin: entrypoint requires agent.runtime: rust and a matching binaries entry. When the table has no entry for the node’s <arch>-<os>, install is refused with incompatible: no binary for <arch-os>. Every manifest field is in the manifest reference.

License note

A plugin that wraps a closed-source vendor library stays under the license it declares for its own source files. The install review flags the vendor binary separately. See Legal and licensing for how the SDK licences interact with your own.

Next steps

Manifest reference

The full agent block.

Local install

Pack, sign and install the archive.

Legal and licensing

Licences and vendor binaries.
Last modified on October 11, 2026