[[agent]]
id = "event.publish"
label = "Publish events on the plugin event bus"
description = "Lets the plugin emit events on topics it has been granted access to. Other plugins and host services that subscribe to the same topic will receive the payload."
category = "data_network"
risk = "low"
risk_reason = "Bus messages are sandboxed and do not affect flight."
enforced = true
[[agent]]
id = "event.subscribe"
label = "Receive events from the plugin event bus"
description = "Lets the plugin subscribe to topics it has been granted access to. The plugin sees payloads from other plugins and from host services that publish on those topics."
category = "data_network"
risk = "low"
risk_reason = "Read-only on a sandboxed in-process bus."
enforced = true
[[agent]]
id = "mavlink.read"
label = "Read MAVLink messages from the flight controller"
description = "Lets the plugin observe the live MAVLink message stream, including telemetry, mode changes, and status text. Useful for analytics, logging, and dashboards."
category = "flight_control"
risk = "low"
risk_reason = "Read-only on the MAVLink stream."
enforced = true
[[agent]]
id = "mavlink.write"
label = "Send MAVLink commands to the flight controller"
description = "Lets the plugin inject MAVLink commands to the autopilot, including mode changes, arming, and parameter writes. An untrusted plugin with this capability can take control of the aircraft."
category = "flight_control"
risk = "high"
risk_reason = "Can change flight mode, arm motors, and alter parameters in flight."
enforced = true
[[agent]]
id = "msp.read"
label = "Read MSP messages from the flight controller"
description = "Lets the plugin observe the raw MSP byte stream from a Betaflight / iNav / KISS flight controller, so it can decode telemetry, sensor, and status replies. Read-only on the MSP stream; the sibling of mavlink.read for an FC that speaks MSP instead of MAVLink."
category = "flight_control"
risk = "low"
risk_reason = "Read-only on the MSP stream."
enforced = true
[[agent]]
id = "msp.write"
label = "Send MSP commands to the flight controller"
description = "Lets the plugin write raw MSP commands to a Betaflight / iNav / KISS flight controller, including MSP_SET_RAW_RC (the RC channel values that fly the aircraft in a rate mode). An untrusted plugin with this capability can command the aircraft, exactly like mavlink.write on a MAVLink FC."
category = "flight_control"
risk = "high"
risk_reason = "Can drive the RC channels and arm the aircraft on an MSP flight controller."
enforced = true
[[agent]]
id = "mavlink.component.camera"
label = "Act as a MAVLink camera component"
description = "Lets the plugin register itself as a MAVLink camera component and respond to camera-related MAVLink messages from the GCS and the autopilot."
category = "flight_control"
risk = "medium"
risk_reason = "Owns a MAVLink component id; can interfere with the real camera if mis-configured."
enforced = false
[[agent]]
id = "mavlink.component.gimbal"
label = "Act as a MAVLink gimbal component"
description = "Lets the plugin register itself as a MAVLink gimbal manager or device and handle gimbal control messages. Required by gimbal driver plugins."
category = "flight_control"
risk = "medium"
risk_reason = "Owns a MAVLink component id and can move the gimbal."
enforced = false
[[agent]]
id = "mavlink.component.payload"
label = "Act as a MAVLink payload component"
description = "Lets the plugin register itself as a MAVLink payload component for actuators, droppers, and other mission-specific hardware."
category = "flight_control"
risk = "medium"
risk_reason = "Owns a MAVLink component id and can trigger payload actuators."
enforced = false
[[agent]]
id = "mavlink.component.peripheral"
label = "Act as a generic MAVLink peripheral component"
description = "Lets the plugin claim a MAVLink component id for a peripheral that does not fit the camera, gimbal, or payload categories. Used by sensor bridges and experimental devices."
category = "flight_control"
risk = "medium"
risk_reason = "Owns a MAVLink component id on the same bus as the autopilot."
enforced = false
[[agent]]
id = "mavlink.component.vio"
label = "Act as a MAVLink visual-inertial-odometry source"
description = "Lets the plugin register as a VIO source that publishes pose estimates over MAVLink. The autopilot can fuse these directly into the EKF, so a faulty source can cause unsafe flight behavior."
category = "flight_control"
risk = "high"
risk_reason = "Pose estimates feed the EKF and influence position control."
enforced = true
[[agent]]
id = "estimator.pose.inject"
label = "Inject pose estimates into the EKF"
description = "Lets the plugin push pose samples (position, attitude, or both) directly into the autopilot state estimator. A bad pose stream produces unsafe position commands and fly-aways."
category = "flight_control"
risk = "high"
risk_reason = "Directly biases the EKF; a malicious or buggy plugin can crash the aircraft."
enforced = true
[[agent]]
id = "telemetry.read"
label = "Read telemetry streams from the agent"
description = "Lets the plugin read the same telemetry feed the GCS consumes (battery, GPS, attitude, mode). Useful for logging and analytics."
category = "data_network"
risk = "low"
risk_reason = "Read-only on existing telemetry topics."
enforced = true
[[agent]]
id = "sensor.stream.read"
label = "Read the sensor bus (IMU, GNSS, triggers, clock)"
description = "Lets the plugin subscribe to the agent's sensor bus: timestamped IMU batches, raw GNSS receiver bytes, camera trigger records, barometer, magnetometer and clock samples, plus the clock state file. Used by capture and odometry plugins that need every sample on the host timebase."
category = "data_network"
risk = "medium"
risk_reason = "High-rate raw sensor data, including the receiver's position stream. Enforced by the generated unit's mount namespace: without this grant the sensor bus socket and the clock file are not bound into the plugin's hidden run directory, so a connect fails with ENOENT."
enforced = true
[[agent]]
id = "capture.storage.write"
label = "Write capture sessions to the capture storage volume"
description = "Lets the plugin read the capture-storage state and write capture session directories under the agent's capture root, and raise the capture-active marker that switches the flight controller to its capture sensor rates. The agent's storage janitor still owns the budget, admission and eviction."
category = "data_network"
risk = "medium"
risk_reason = "Writes large files to the capture volume and changes the flight controller's stream rates while a session records. Enforced by the generated unit's mount namespace: without this grant the capture root, the capture-active directory and the storage state are not bound into the plugin's sandbox."
enforced = true
[[agent]]
id = "telemetry.extend"
label = "Publish new telemetry fields to the GCS"
description = "Lets the plugin add new fields to the telemetry stream that ships to the GCS. Used by sensor plugins that surface custom readings on the dashboard."
category = "data_network"
risk = "low"
risk_reason = "Adds fields; does not alter existing ones."
enforced = true
[[agent]]
id = "sensor.camera.register"
label = "Register a camera as a system sensor"
description = "Lets the plugin declare a camera device the rest of the agent and the GCS can address (live preview, recording, mission usage)."
category = "hardware"
risk = "medium"
risk_reason = "Camera streams may be recorded and uploaded; sensor identity matters for safety."
enforced = false
[[agent]]
id = "sensor.depth.register"
label = "Register a depth sensor as a system sensor"
description = "Lets the plugin declare a depth-camera or stereo sensor. Downstream services can consume depth maps for obstacle avoidance and SLAM."
category = "hardware"
risk = "medium"
risk_reason = "Depth data may feed obstacle avoidance; faulty data can influence avoidance decisions."
enforced = false
[[agent]]
id = "sensor.lidar.register"
label = "Register a LiDAR as a system sensor"
description = "Lets the plugin declare a LiDAR device. Downstream services can consume point clouds for mapping and obstacle avoidance."
category = "hardware"
risk = "medium"
risk_reason = "Point clouds may feed obstacle avoidance; faulty data can influence avoidance decisions."
enforced = false
[[agent]]
id = "sensor.imu.register"
label = "Register an auxiliary IMU as a system sensor"
description = "Lets the plugin declare a secondary IMU device the rest of the agent can address. Often paired with VIO and odometry workflows."
category = "hardware"
risk = "medium"
risk_reason = "IMU output may feed state estimation downstream."
enforced = false
[[agent]]
id = "sensor.payload.register"
label = "Register a payload device as a system sensor"
description = "Lets the plugin declare a payload device (sprayer, dropper, sampler) so missions and the GCS can address it."
category = "hardware"
risk = "medium"
risk_reason = "Payload identity drives mission actions; mis-registration can trigger the wrong actuator."
enforced = false
[[agent]]
id = "hardware.uart"
label = "Open UART serial ports on the host"
description = "Lets the plugin open and exchange data over UART serial ports. Required by GPS, range-finder, and modem drivers."
category = "hardware"
risk = "medium"
risk_reason = "Serial ports may be shared with the autopilot or critical peripherals. Enforced by the generated unit's cgroup device policy: without this grant the plugin's unit carries PrivateDevices=yes and no DeviceAllow for char-ttyUSB / char-ttyACM / char-tty, so an open() of a serial node fails with EPERM."
enforced = true
[[agent]]
id = "hardware.i2c"
label = "Read and write I2C devices on the host"
description = "Lets the plugin talk to I2C devices on the SBC's I2C buses. Used by sensor drivers and small peripherals."
category = "hardware"
risk = "medium"
risk_reason = "I2C buses are shared; a misbehaving plugin can stall the bus for other devices. Enforced by the generated unit's cgroup device policy: without this grant no DeviceAllow for char-i2c is emitted, so /dev/i2c-* is unreachable."
enforced = true
[[agent]]
id = "hardware.spi"
label = "Read and write SPI devices on the host"
description = "Lets the plugin talk to SPI devices on the SBC's SPI buses. Used by displays and high-rate sensor drivers."
category = "hardware"
risk = "medium"
risk_reason = "SPI buses are shared with on-board peripherals like the front-panel display. Enforced by the generated unit's cgroup device policy: without this grant no DeviceAllow for char-spidev is emitted, so /dev/spidev* is unreachable."
enforced = true
[[agent]]
id = "hardware.gpio"
label = "Read and toggle GPIO pins on the host"
description = "Lets the plugin read input pins and drive output pins on the SBC. Used by switches, LEDs, and discrete peripheral control."
category = "hardware"
risk = "medium"
risk_reason = "GPIO outputs can drive external hardware; mis-driven pins can damage attached devices. Enforced by the generated unit's cgroup device policy: without this grant no DeviceAllow for char-gpiochip is emitted, so /dev/gpiochip* is unreachable."
enforced = true
[[agent]]
id = "hardware.gpio_out"
label = "Drive GPIO output lines (status buzzer or LED)"
description = "Lets the plugin drive a host GPIO output line high or low and play a bounded buzzer or LED beep pattern through the agent's GPIO-output service. Output-only; it does not read input pins."
category = "hardware"
risk = "high"
risk_reason = "Drives physical output hardware; a mis-driven pin can energize or damage an attached device."
enforced = true
[[agent]]
id = "hardware.usb"
label = "Claim raw USB devices on the host"
description = "Lets the plugin attach to and exchange bulk transfers with USB devices. Required by vendor SDK drivers that talk to specific USB peripherals."
category = "hardware"
risk = "medium"
risk_reason = "Claiming a USB device prevents other software from using it. Enforced by the generated unit's cgroup device policy: without this grant no DeviceAllow for char-usb_device is emitted, so a raw /dev/bus/usb transfer is unreachable."
enforced = true
[[agent]]
id = "hardware.usb.uvc"
label = "Read frames from USB UVC cameras"
description = "Lets the plugin capture video frames from USB Video Class cameras. Required by webcam and machine-vision drivers."
category = "hardware"
risk = "medium"
risk_reason = "Video frames may be recorded, transmitted, or processed off-vehicle. Enforced by the generated unit's cgroup device policy: without this grant no DeviceAllow for char-video4linux is emitted, so /dev/video* is unreachable."
enforced = true
[[agent]]
id = "hardware.camera.csi"
label = "Read frames from MIPI CSI cameras"
description = "Lets the plugin capture video frames from CSI-attached cameras on the SBC's camera connector. Required by native camera drivers."
category = "hardware"
risk = "medium"
risk_reason = "Video frames may be recorded, transmitted, or processed off-vehicle. Enforced by the generated unit's cgroup device policy: without this grant no DeviceAllow for char-video4linux or char-dri is emitted, so the CSI capture nodes are unreachable."
enforced = true
[[agent]]
id = "video.source.set"
label = "Configure the agent's video stream sources"
description = "Lets the plugin declare the camera and stream sources the agent's video pipeline should serve (one primary plus optional secondary legs), so a smart-camera or pod driver can auto-configure its own feeds without an operator hand-typing them. Applying a new source list restarts the video pipeline."
category = "data_network"
risk = "medium"
risk_reason = "Reconfigures which video sources the vehicle serves and restarts the video pipeline; a wrong source list can black out the operator's video."
enforced = true
[[agent]]
id = "video.whep.relay"
label = "Relay live video playback sessions"
description = "Lets the plugin open and close WebRTC playback (WHEP) sessions on the agent's live camera streams on behalf of a viewer it serves: it hands the agent a viewer's SDP offer for a camera and relays the answer back. The agent only plays out streams it already publishes; the plugin cannot change them or reach the media server directly."
category = "data_network"
risk = "medium"
risk_reason = "Gives the plugin the node's live camera video to hand to its own viewers, wherever it can reach them."
enforced = true
[[agent]]
id = "sensors.rig.write"
label = "Store the sensor rig calibration"
description = "Lets the plugin store the rig document (sensor roles, intrinsics and extrinsics) the agent validates and the sensor hub fuses with, through the same validation the operator's rig write runs. An invalid document is refused and the stored one kept."
category = "hardware"
risk = "medium"
risk_reason = "A wrong but valid calibration skews every fused sensor sample and capture until it is corrected."
enforced = true
[[agent]]
id = "camera.control.write"
label = "Set camera exposure and gain"
description = "Lets the plugin set the exposure time, gain and auto-exposure mode of a capture camera (one configured for capture, which the vision engine streams from), and read back the values the camera actually took. Other cameras, including the video pipeline's own, are refused; simulated and network cameras answer that nothing was applied."
category = "hardware"
risk = "medium"
risk_reason = "Changes the image every consumer of that camera sees (the operator's video, detection, tracking and capture); a wrong exposure can wash out or black out the picture until it is set back."
enforced = true
[[agent]]
id = "mission.read"
label = "Read mission plans loaded on the agent"
description = "Lets the plugin read the active mission, waypoints, fences, and rally points stored on the agent."
category = "flight_control"
risk = "low"
risk_reason = "Read-only on mission data."
enforced = true
[[agent]]
id = "mission.write"
label = "Upload or modify mission plans on the agent"
description = "Lets the plugin upload new missions, edit waypoints, or change fence and rally definitions. Used by mission-planner and pattern-generator plugins."
category = "flight_control"
risk = "high"
risk_reason = "Mission changes drive autonomous flight paths."
enforced = true
[[agent]]
id = "network.outbound"
label = "Open outbound network connections"
description = "Lets the plugin make outbound TCP, UDP, and HTTP connections from the agent host, and find services on the local network over mDNS through the agent. Required by plugins that talk to external services."
category = "data_network"
risk = "medium"
risk_reason = "Exfiltration risk; outbound traffic can carry telemetry off the aircraft. Enforced by the generated unit's socket policy: without this grant the unit carries RestrictAddressFamilies=AF_UNIX and IPAddressDeny=any, so socket(AF_INET) fails with EAFNOSUPPORT. The mdns.browse host method is gated on it at dispatch."
enforced = true
[[agent]]
id = "filesystem.host"
label = "Read and write files on the host filesystem"
description = "Lets the plugin read and write files outside its own data directory. Required by plugins that ingest logs, maps, or other host-provided files."
category = "compute_process"
risk = "medium"
risk_reason = "Broad host filesystem access; mis-use can corrupt agent state. Enforced by the generated unit's mount namespace: without this grant the unit carries InaccessiblePaths for the operator data roots and ProtectHome=yes, so a read outside the plugin's own tree fails with EACCES."
enforced = true
[[agent]]
id = "recording.write"
label = "Write recordings into the agent recording store"
description = "Lets the plugin save video, telemetry, or analysis outputs into the agent's recording store so the GCS and post-flight tools can find them."
category = "data_network"
risk = "low"
risk_reason = "Writes are sandboxed to the recording directory."
enforced = true
[[agent]]
id = "process.spawn"
label = "Spawn subprocesses on the agent host"
description = "Lets the plugin launch helper subprocesses listed in its manifest's spawn allowlist. Required to ship vendor-binary drivers and language runtimes outside the Python plugin host."
category = "compute_process"
risk = "high"
risk_reason = "Subprocesses run outside the plugin host sandbox and with the plugin's privileges."
enforced = true
[[agent]]
id = "vision.frame.read"
label = "Read camera frames from the vision engine"
description = "Lets the plugin subscribe to normalized camera frames the vision engine publishes (resolution and pixel format set by the engine). Frames are shared read-only; the plugin does not open the camera itself."
category = "compute_process"
risk = "medium"
risk_reason = "Frames may contain sensitive imagery; a frame consumer can process it off the normal recording path."
enforced = true
[[agent]]
id = "vision.model.register"
label = "Register an inference model with the vision engine"
description = "Lets the plugin register an inference model (and its input format and output classes) so the vision engine can load and run it, or so the plugin can run it against shared frames. Registered models appear to detection consumers."
category = "compute_process"
risk = "medium"
risk_reason = "A model can consume the shared accelerator and CPU; a heavy or faulty model can starve other vision consumers."
enforced = true
[[agent]]
id = "vision.model.read"
label = "Read the plugin's resolved model delivery status"
description = "Lets the plugin read the resolution of its own declared models: for each model the state (resolved, needs-model, verify-failed), the resolved on-disk path and runtime when resolved, and the reason otherwise. This is how a plugin finds where its delivered model was cached so it can load it. Reads only this plugin's own resolution; it does not read frames, other plugins' models, or command the vehicle."
category = "compute_process"
risk = "low"
risk_reason = "Read-only access to the plugin's own model-delivery metadata; no frames, no other plugin's data, no vehicle command."
enforced = true
[[agent]]
id = "vision.detection.publish"
label = "Publish detections to the vision engine"
description = "Lets the plugin publish detection results (bounding boxes, class labels, confidence, track ids) that other plugins, overlays, and mission logic can consume. Detections are derived data; they do not command the vehicle."
category = "data_network"
risk = "low"
risk_reason = "Detections are derived data on a sandboxed topic and do not affect flight."
enforced = true
[[agent]]
id = "vision.offload.advertise"
label = "Advertise a perception offload link"
description = "Lets the plugin report the perception offload link it holds (whether a compute node is paired, whether the bearer to it is good enough, its address and the model it runs). The node's perception tier and status read it. The agent counts the node as paired only when the advertised address answers, and only one plugin owns the link at a time."
category = "compute_process"
risk = "medium"
risk_reason = "The advertised link changes the perception tier the node reports and where its detections come from."
enforced = true
[[agent]]
id = "vision.detection.subscribe"
label = "Subscribe to detections from the vision engine"
description = "Lets the plugin receive the detection batches the vision engine publishes (bounding boxes, class labels, confidence, and the tracker's stable track id and lock state on the locked object). Detections are derived data; subscribing does not command the vehicle or open the camera."
category = "data_network"
risk = "low"
risk_reason = "Detections are derived data on a sandboxed topic and do not affect flight."
enforced = true
[[agent]]
id = "vision.track.designate"
label = "Set the vision engine's follow target"
description = "Lets the plugin choose which object the vision engine's single-object tracker locks onto (the follow target), by naming the box to track. This selects a target for downstream follow / framing logic; designation alone does not command the vehicle."
category = "flight_control"
risk = "medium"
risk_reason = "Choosing the follow target steers downstream follow/framing behaviour, though designation alone does not command flight."
enforced = true
[[agent]]
id = "button.subscribe"
label = "Subscribe to onboard button press events"
description = "Lets the plugin receive press events (short, long, chord) from the host's front-panel buttons. Used by node plugins that drive an on-device workflow from physical buttons. Read-only; it does not remap or consume the buttons from other consumers."
category = "hardware"
risk = "medium"
risk_reason = "Button events drive plugin actions; an input source should be reviewed like any other."
enforced = true
[[agent]]
id = "flight.guided_setpoint"
label = "Emit guided-mode setpoints"
description = "Lets the plugin command the flight controller with guided-mode position or velocity setpoints (and the guided-mode entry and exit handshake) through a scoped sender, instead of raw MAVLink writes. The setpoint stream is the only flight-command surface this capability grants."
category = "flight_control"
risk = "high"
risk_reason = "Commands vehicle motion in guided mode; a faulty stream can fly the aircraft."
enforced = true
[[agent]]
id = "flight.rate_setpoint"
label = "Emit attitude/rate setpoints"
description = "Lets a plugin command the flight controller with SET_ATTITUDE_TARGET setpoints (an attitude quaternion and/or body rates, plus collective thrust) through a scoped sender, for GUIDED-mode vehicles that accept attitude/rate offboard control. This is a second flight-command surface alongside guided-mode position/velocity setpoints; the setpoint stream is the only flight-command surface this capability grants."
category = "flight_control"
risk = "critical"
risk_reason = "Directly commands aircraft attitude/body rates and thrust at tick rate; a faulty stream can fly the aircraft outside guided-mode limits."
enforced = true
[[agent]]
id = "mavlink.tunnel"
label = "Send and receive MAVLink TUNNEL payloads"
description = "Lets the plugin exchange application payloads over MAVLink TUNNEL frames (a private payload_type) on the existing link, without the full MAVLink write surface. Used for plugin-to-plugin or node-to-node application data carried inside the MAVLink stream."
category = "flight_control"
risk = "high"
risk_reason = "Shares the flight-control link; a misbehaving tunnel can congest MAVLink traffic."
enforced = true
[[agent]]
id = "radio.aux_stream"
label = "Open and use an auxiliary radio stream"
description = "Lets the plugin open a dedicated auxiliary stream on the radio link (a separate radio-port from the data and control planes) managed by the radio service, and read or write application frames on it. Used by plugins that need an isolated low-rate channel between nodes."
category = "data_network"
risk = "high"
risk_reason = "Consumes radio airtime on the shared adapter and owns an application channel between nodes."
enforced = true
[[agent]]
id = "display.oled.page"
label = "Contribute a page to the onboard display"
description = "Lets the plugin register a page (its rendered content and touch or hit zones) on the host's onboard display, so a plugin can present status without recompiling the display service. The host owns page switching and arbitration between contributed pages."
category = "hardware"
risk = "low"
risk_reason = "Draws to the local display only; no effect on flight or the network."
enforced = true
[[agent]]
id = "mcp.expose"
label = "Expose the plugin's tools to the AI-control (MCP) surface"
description = "Lets the plugin publish its declared tools, resources, and prompts to an MCP client, and lets the host route a tool invocation into the plugin. Exposure alone grants no host authority: each tool acts only through the plugin's OTHER granted capabilities, so a tool can never do anything the plugin could not already do."
category = "data_network"
risk = "medium"
risk_reason = "Publishes a callable interface to an AI client; the effect of any call is still bounded by the plugin's other capabilities and the MCP token's scope."
enforced = true
[[agent]]
id = "hardware.gpu"
label = "Use the GPU or neural accelerator"
description = "Lets the plugin open the host's GPU and neural-accelerator device nodes (DRM render nodes, Mali, RKNPU, NVIDIA) for inference or rendering. Without it the plugin runs in a private /dev with no accelerator."
category = "hardware"
risk = "medium"
risk_reason = "Direct device access to a shared accelerator; a runaway workload can starve the vision engine. Enforced by the generated unit's cgroup device policy (DeviceAllow for the accelerator nodes plus the video and render groups)."
enforced = true
[[agent]]
id = "network.listen"
label = "Accept inbound network connections"
description = "Lets a declared plugin service bind the TCP ports it lists in its manifest (listen_ports) so other nodes on the network can reach it, and advertise those ports on the local network over mDNS through the agent. Every other port stays closed to the plugin, and the plugin's main process never listens."
category = "data_network"
risk = "high"
risk_reason = "Opens a network entry point into a plugin process on the agent host. Enforced by the generated unit: every plugin unit carries SocketBindDeny=any, and only a granted service unit gets SocketBindAllow for its declared ports. The mdns.advertise host method is gated on it at dispatch and publishes only a declared port."
enforced = true
[[agent]]
id = "cloud.publish"
label = "Publish data to the cloud relay"
description = "Lets the plugin publish small messages (at most 64 KiB each) on its own cloud stream through the agent's cloud relay, so a signed-in operator can receive them off the local network. Publishing detections onto the shared detection topic additionally needs vision.detection.publish."
category = "data_network"
risk = "medium"
risk_reason = "Sends plugin data off the aircraft to the cloud under the device's identity."
enforced = true
[[agent]]
id = "cloud.records"
label = "Store records in the operator's cloud account"
description = "Lets the plugin write small JSON records (at most 64 KiB each) into its own collection in the operator's cloud account, where the plugin's GCS half can read them back."
category = "data_network"
risk = "medium"
risk_reason = "Persists plugin data off the aircraft under the device's identity; confined to the plugin's own namespace."
enforced = true
[[agent]]
id = "node.info.read"
label = "Read this node's hardware and role facts"
description = "Lets the plugin read what kind of node it runs on: the node profile, the board and whether it has a neural accelerator, the ground-station role, whether the camera pipeline is up, and the main video stream's size and frame rate. Read-only facts; no telemetry, imagery, credentials or configuration values."
category = "hardware"
risk = "low"
risk_reason = "Read-only node identity facts; no effect on flight, no imagery and no secrets."
enforced = true
[[gcs]]
id = "ui.slot.fc-tab"
label = "Add a tab to the flight controller panel"
description = "Lets the plugin add a tab to the per-drone flight controller area. The tab renders inside a sandboxed iframe with no host DOM access."
category = "ui_slot"
risk = "low"
risk_reason = "Sandboxed iframe; no host DOM access."
[[gcs]]
id = "ui.slot.hardware-tab"
label = "Add a tab to the hardware page"
description = "Lets the plugin add a tab on the Hardware page so the operator can inspect or configure devices the plugin manages."
category = "ui_slot"
risk = "low"
risk_reason = "Sandboxed iframe inside the Hardware page."
[[gcs]]
id = "ui.slot.mission-template"
label = "Register a mission template"
description = "Lets the plugin add a new entry to the mission template picker so the operator can start missions with the plugin's parameters."
category = "ui_slot"
risk = "low"
risk_reason = "Template metadata only; missions still need approval."
[[gcs]]
id = "ui.slot.map-overlay"
label = "Draw an overlay on the map"
description = "Lets the plugin draw geometry on the map view (polygons, markers, heatmaps). The overlay is rendered in a sandboxed canvas."
category = "ui_slot"
risk = "low"
risk_reason = "Visual layer only; no flight-control effect."
[[gcs]]
id = "ui.slot.video-overlay"
label = "Draw an overlay on the live video"
description = "Lets the plugin draw overlays on top of the live video feed (detection boxes, telemetry HUDs). The overlay is rendered above the player."
category = "ui_slot"
risk = "low"
risk_reason = "Visual layer above the video element."
[[gcs]]
id = "ui.slot.notification-channel"
label = "Show notifications in the GCS"
description = "Lets the plugin push notifications into the GCS notification center so it can surface alerts and status changes to the operator."
category = "ui_slot"
risk = "low"
risk_reason = "Toasts only; each is labelled with the plugin id and rate-limited per plugin."
[[gcs]]
id = "ui.slot.settings-section"
label = "Add a section to the Settings page"
description = "Lets the plugin add its own section to the GCS Settings page so the operator can configure the plugin from a single place."
category = "ui_slot"
risk = "low"
risk_reason = "Sandboxed iframe inside the Settings page."
[[gcs]]
id = "ui.slot.flight-skill"
label = "Contribute a flight skill to the cockpit"
description = "Lets the plugin register a flight skill that appears in the cockpit Skill Bar, can be bound to a hotkey or gamepad button, and activated by the operator. Activation flips the plugin's per-drone config; the cockpit's arm and confirmation gates still apply."
category = "ui_slot"
risk = "medium"
risk_reason = "A bindable skill triggers plugin behavior; the plugin still needs its own flight-control capabilities to act, and the cockpit's arm/confirm gates still apply."
[[gcs]]
id = "ui.slot.node-detail-tab"
label = "Add a tab to the node detail panel"
description = "Lets the plugin add a per-node tab inside the node detail panel on any node profile (drone, ground station, compute). The tab is scoped to the currently selected node."
category = "ui_slot"
risk = "low"
risk_reason = "Sandboxed iframe scoped to one node."
[[gcs]]
id = "ui.slot.cockpit-panel"
label = "Add a panel to the cockpit quick settings"
description = "Lets the plugin add a panel to the in-flight cockpit quick-settings surface, scoped to the currently selected drone."
category = "ui_slot"
risk = "low"
risk_reason = "Sandboxed iframe scoped to the cockpit of one drone."
[[gcs]]
id = "ui.slot.cockpit-widget"
label = "Add a widget to the cockpit HUD"
description = "Lets the plugin place a small widget in one of the cockpit's corner zones over the live video, scoped to the drone the cockpit is flying. The widget renders inside a sandboxed iframe with no host DOM access."
category = "ui_slot"
risk = "low"
risk_reason = "Sandboxed iframe in a cockpit corner zone; display only."
[[gcs]]
id = "vision.track.designate"
label = "Choose the follow target from the GCS"
description = "Lets the plugin ask the GCS to set the vision engine's follow target on the drone it is bound to, by naming the detection box to track. Designation selects a target for downstream follow and framing logic; it does not command the vehicle."
category = "flight_control"
risk = "medium"
risk_reason = "Choosing the follow target steers downstream follow/framing behaviour, though designation alone does not command flight."
[[gcs]]
id = "telemetry.subscribe"
label = "Read live telemetry from drones"
description = "Lets the plugin subscribe to live telemetry streams (attitude, GPS, battery, mode) for the drones the operator has paired with the GCS."
category = "data_network"
risk = "low"
risk_reason = "Read-only on the telemetry stream."
[[gcs]]
id = "command.send"
label = "Send commands to drones from the GCS"
description = "Lets the plugin ask the GCS to send takeoff, land or return-to-launch to the drone it is bound to, or retarget that drone's follow tracker. The operator confirms every one of these in a prompt that names the drone. Arming, mode changes and every other command are refused. The same grant lets the plugin write its own per-drone configuration without a prompt."
category = "flight_control"
risk = "medium"
risk_reason = "Each confirmed command takes effect immediately during a flight."
[[gcs]]
id = "recording.write"
label = "Save recordings to the GCS library"
description = "Lets the plugin write recordings (video, telemetry CSV, analysis output) into the GCS recording library so the operator can replay them later."
category = "data_network"
risk = "low"
risk_reason = "Writes are scoped to the recording library."
[[gcs]]
id = "mission.read"
label = "Read mission plans loaded in the GCS"
description = "Lets the plugin read the active mission, waypoints, fences, and rally points that the operator has loaded into the GCS planner."
category = "flight_control"
risk = "low"
risk_reason = "Read-only on mission data."
[[gcs]]
id = "mission.write"
label = "Edit mission plans in the GCS"
description = "Lets the plugin upload, edit, or replace missions in the GCS planner. Used by pattern generators and mission-template plugins."
category = "flight_control"
risk = "high"
risk_reason = "Mission changes drive autonomous flight paths."
[[gcs]]
id = "cloud.read"
label = "Read fleet data from the cloud"
description = "Lets the plugin read shared fleet data from the cloud backend (drone status rows, paired devices, fleet roster)."
category = "data_network"
risk = "low"
risk_reason = "Read-only on the cloud rows the user is authorised for."
[[gcs]]
id = "perception.read"
label = "Read perception state in the GCS"
description = "Lets the plugin read the drone's perception state: where detection runs (on the drone or offloaded to a compute node), the bound compute node, and the live session health (feed freshness, throughput). Read-only on derived data; it does not open or read the camera."
category = "data_network"
risk = "low"
risk_reason = "Read-only on derived detection metadata; no camera access."
[[gcs]]
id = "perception.subscribe"
label = "Subscribe to live detections in the GCS"
description = "Lets the plugin subscribe to the live detection stream (bounding boxes, classes, track ids) for the drones the operator has paired, the same detections the cockpit already renders. Read-only; the detections are derived data, not the raw camera feed."
category = "data_network"
risk = "low"
risk_reason = "Read-only on the derived detection stream; no camera access."
[[gcs]]
id = "mcp.expose"
label = "Expose the plugin's GCS tools to the AI-control (MCP) surface"
description = "Lets the plugin publish its declared GCS-half tools, resources, and prompts to an MCP client. A GCS-only tool routes through the GCS bridge (command / mission), so its effect is still bounded by the plugin's other GCS capabilities and the MCP token's scope."
category = "data_network"
risk = "medium"
risk_reason = "Publishes a callable interface to an AI client; effect is bounded by the plugin's other capabilities and the token scope."
[[gcs]]
id = "event.publish"
label = "Publish events on the GCS plugin event bus"
description = "Lets the plugin emit events on the in-memory GCS event bus other installed plugin panels subscribe to. The bus never leaves the browser tab; it carries no host authority of its own."
category = "data_network"
risk = "low"
risk_reason = "In-tab message bus between plugin panels; no host or vehicle effect."
[[gcs]]
id = "event.subscribe"
label = "Receive events from the GCS plugin event bus"
description = "Lets the plugin subscribe to topics on the in-memory GCS event bus and see payloads other installed plugin panels publish."
category = "data_network"
risk = "low"
risk_reason = "Read-only on an in-tab message bus."
[[gcs]]
id = "ui.slot.node-agent-page"
label = "Add a page to a node's Agent sidebar"
description = "Lets the plugin add a page to the sectioned Agent sidebar of a node's detail panel, next to the built-in companion and configuration pages. The page is scoped to the selected node."
category = "ui_slot"
risk = "low"
risk_reason = "Scoped to one node's detail panel; the page acts only through the plugin's other capabilities."
[[gcs]]
id = "ui.slot.node-surface"
label = "Add a top-level tab to a node's detail panel"
description = "Lets the plugin add a top-level surface to the node detail tab strip for the node profiles it declares (for example a workstation overview). The surface is scoped to the selected node."
category = "ui_slot"
risk = "low"
risk_reason = "Scoped to one node's detail panel; the surface acts only through the plugin's other capabilities."
[[gcs]]
id = "cloud.records"
label = "Store the plugin's own records in the cloud"
description = "Lets the plugin list, read, write and delete records in its own cloud collection for the signed-in operator (for example job history). Records are keyed by plugin, so a plugin can never read another plugin's records. Each record is capped at 64 KiB and each plugin at 5000 records."
category = "data_network"
risk = "low"
risk_reason = "Confined to the plugin's own namespace for the signed-in operator."
[[method]]
id = "event.publish"
open = true
[[method]]
id = "event.subscribe"
open = true
[[method]]
id = "ping"
open = true
[[method]]
id = "tool.invoke"
required_cap = "mcp.expose"
[[method]]
id = "telemetry.subscribe"
required_cap = "telemetry.read"
[[method]]
id = "telemetry.extend"
required_cap = "telemetry.extend"
[[method]]
id = "mission.read"
required_cap = "mission.read"
[[method]]
id = "mission.write"
required_cap = "mission.write"
[[method]]
id = "recording.start"
required_cap = "recording.write"
[[method]]
id = "recording.stop"
required_cap = "recording.write"
[[method]]
id = "mavlink.subscribe"
required_cap = "mavlink.read"
[[method]]
id = "mavlink.send"
required_cap = "mavlink.write"
[[method]]
id = "msp.subscribe"
required_cap = "msp.read"
[[method]]
id = "msp.send"
required_cap = "msp.write"
[[method]]
id = "mavlink.tunnel.send"
required_cap = "mavlink.tunnel"
[[method]]
id = "mavlink.register_component"
inline = true
[[method]]
id = "peripheral.register_driver"
inline = true
[[method]]
id = "peripheral.unregister_driver"
inline = true
[[method]]
id = "camera.claim"
required_cap = "sensor.camera.register"
[[method]]
id = "camera.release"
required_cap = "sensor.camera.register"
[[method]]
id = "camera.get_frame"
required_cap = "sensor.camera.register"
[[method]]
id = "video.source.set"
required_cap = "video.source.set"
[[method]]
id = "video.whep.offer"
required_cap = "video.whep.relay"
[[method]]
id = "video.whep.close"
required_cap = "video.whep.relay"
[[method]]
id = "sensors.rig.write"
required_cap = "sensors.rig.write"
[[method]]
id = "camera.control.set"
required_cap = "camera.control.write"
[[method]]
id = "config.get"
open = true
[[method]]
id = "config.set"
open = true
[[method]]
id = "process.spawn"
required_cap = "process.spawn"
[[method]]
id = "vision.subscribe_frames"
required_cap = "vision.frame.read"
[[method]]
id = "vision.register_model"
required_cap = "vision.model.register"
[[method]]
id = "vision.read_model"
required_cap = "vision.model.read"
[[method]]
id = "vision.infer"
required_cap = "vision.model.register"
[[method]]
id = "vision.publish_detection"
required_cap = "vision.detection.publish"
[[method]]
id = "vision.subscribe_detections"
required_cap = "vision.detection.subscribe"
[[method]]
id = "vision.designate_track"
required_cap = "vision.track.designate"
[[method]]
id = "display.page.set"
required_cap = "display.oled.page"
[[method]]
id = "display.zone.subscribe"
required_cap = "display.oled.page"
[[method]]
id = "button.subscribe"
required_cap = "button.subscribe"
[[method]]
id = "gpio.output.set"
required_cap = "hardware.gpio_out"
[[method]]
id = "gpio.buzzer.beep"
required_cap = "hardware.gpio_out"
[[method]]
id = "flight.guided_setpoint.send"
required_cap = "flight.guided_setpoint"
[[method]]
id = "flight.rate_setpoint.send"
required_cap = "flight.rate_setpoint"
[[method]]
id = "radio.aux_stream.open"
required_cap = "radio.aux_stream"
[[method]]
id = "radio.aux_stream.close"
required_cap = "radio.aux_stream"
[[method]]
id = "radio.aux_stream.send"
required_cap = "radio.aux_stream"
[[method]]
id = "radio.aux_stream.subscribe"
required_cap = "radio.aux_stream"
[[method]]
id = "cloud.publish"
required_cap = "cloud.publish"
[[method]]
id = "cloud.records.put"
required_cap = "cloud.records"
[[method]]
id = "offload.advertise"
required_cap = "vision.offload.advertise"
[[method]]
id = "node.info"
required_cap = "node.info.read"
[[method]]
id = "mdns.advertise"
required_cap = "network.listen"
[[method]]
id = "mdns.browse"
required_cap = "network.outbound"