Skip to main content
MAVLink v2 includes an optional message-signing layer that stamps every outgoing frame with a 48-bit signature: the first 6 bytes of SHA-256 over a 32-byte shared secret, the frame, a link id and a timestamp. Once an ArduPilot flight controller holds a key, it rejects unsigned commands on every link except USB. A peer on the same WiFi, a compromised cloud relay or a rogue user on the local network then cannot inject arm, takeoff or parameter-write commands even if they reach the MAVLink stream. In ADOS, the signing key lives in the Mission Control browser as raw bytes in that browser’s IndexedDB. The agent never persists the key. It exposes a small REST surface so Mission Control can detect capability, push the key to the flight controller once, and clear it again.
The key never touches the agent’s disk. In the browser it is stored as raw bytes in IndexedDB, because the signature is a SHA-256 hash over the key itself and Web Crypto cannot compute that from a non-extractable key. Any script running on the Mission Control page can read it.
Mission Control releases before September 2026 signed with HMAC-SHA256 over a region that left out the start byte. No autopilot accepts those signatures, so a flight controller enrolled from one of those releases rejects every command sent over a non-USB link. Re-enroll it over USB from the current release, or clear it with the disable-on-fc route below. Keys stored by those releases are discarded, so the browser shows no key until you enroll or import one.

How it works

  1. Mission Control generates 32 random bytes using crypto.getRandomValues.
  2. Mission Control POSTs the key as hex to /api/mavlink/signing/enroll-fc over the agent’s LAN API. That API is plain HTTP authenticated by the pairing key, so the key crosses the network in clear; enroll over a link you trust, or over USB or loopback.
  3. The agent sends a SETUP_SIGNING message to the flight controller twice, 200 ms apart, then zeroizes the raw key buffer.
  4. Mission Control stores a copy of the raw bytes in IndexedDB keyed by drone id, and zeroizes its own buffer.
  5. Every outgoing MAVLink v2 frame to this drone is signed: the signed bit is set in incompat_flags, the CRC is recomputed, and a 13-byte tail is appended with a 1-byte link id, a 6-byte timestamp (10 µs units since 2015-01-01), and the first 6 bytes of SHA-256(key ‖ frame from the start byte through the CRC ‖ link id ‖ timestamp).
  6. The agent passes signed frames to the flight controller unchanged.
  7. The flight controller validates each frame and silently drops unsigned or invalid ones, except unsigned frames arriving on its USB port.

Enabling signing

A signing mistake can leave the flight controller refusing commands from your ground station. Enable, rotate or clear signing on the ground, disarmed, and read Safety first.
1

Open the signing panel

In Mission Control, select your drone, open the Setup tab, and pick MAVLink Signing under Security.
2

Check capability

The panel asks the agent which autopilot the flight controller reports in its heartbeat. ArduPilot is supported. If the panel shows “not available”, see Firmware support.
3

Click Enable signing

The browser generates a fresh 32-byte key and enrolls it with the flight controller. Enrollment usually completes in under two seconds.
4

Verify the fingerprint

The panel shows an 8-character key id (the first bytes of SHA-256 over the key). Note it if you want to check later that another browser is on the same key.

What the flight controller enforces

ArduPilot has no parameter that switches enforcement on or off. A non-zero key in its signing store is the switch: from the moment it holds one, it accepts unsigned frames only on its USB port (plus RADIO_STATUS from a telemetry radio) and drops them on every other link. Sending an all-zero key clears the store and turns signing off. The flight controller never acknowledges SETUP_SIGNING. The enroll response carries verified: true when the agent saw a frame from the flight controller signed with the new key; verified: false means unconfirmed, not failed. When a request is interrupted after a frame may have gone out, Mission Control keeps both the new key and the previous one and asks the operator which one the flight controller holds. A disable is handled the same way: Mission Control keeps the key until the operator confirms unsigned commands are accepted.
Signing can lock you out of your own drone. If every browser that holds the key gets wiped or replaced and you have not exported a copy, the flight controller rejects every command on every link except USB. Recovery: connect over USB and re-enroll, or, from the companion computer, call the disable-on-fc route to send a zero-key SETUP_SIGNING and then re-enroll from a fresh Mission Control session.

Rotating keys

Click Rotate key in the signing panel to generate a new 32-byte key and enroll it with the flight controller. The old key is replaced once the agent reports the new one sent; an interrupted rotation keeps both until you confirm which one the flight controller holds. Any other browser still holding the previous key loses its ability to sign until you re-enroll it. Rotation is a per-browser action. Each browser that needs to keep working with the rotated key has to re-enroll separately, or import the new key from a browser that already has it.

Firmware support

Support is decided from the autopilot type in the flight controller’s heartbeat. The signing key store is not exposed as parameters, so a parameter dump says nothing about it.

Agent REST surface

All routes live under /api/mavlink/signing/, are served natively by the agent’s control front, and are gated by the X-ADOS-Key header. Request-body redaction strips key_hex from structured logs. The fingerprint (key_id) is safe to log and appears in audit entries. Signing is managed from the Mission Control signing panel. For SSH-only diagnostics or lockout recovery, call these routes with curl on the companion computer (for example GET /capability to check support or POST /disable-on-fc to clear the key).

Troubleshooting

Security posture

What signing protects against:
  • Unauthorized command injection over the cloud relay path.
  • A WiFi peer on the same access point sending arm or takeoff commands to your drone.
  • A compromised agent host forging commands to the flight controller, because the agent does not hold the key.
What signing does not cover:
  • An operator with physical access to the flight controller’s UART.
  • Enrollment over an untrusted LAN. The enroll request is plain HTTP, so anyone who can observe that traffic captures the key. Enroll over a link you trust, or over USB or loopback.
  • A browser whose JavaScript execution is compromised by a same-origin script injection. The key is raw bytes in IndexedDB, so injected script can read it, copy it off the machine, and sign with it indefinitely. Rotate the key if you suspect the browser or the Mission Control origin was compromised.
  • Lost-key recovery. Keep a copy of the key fingerprint somewhere you can find it again.

Next steps

Mission Control signing

Enable, rotate and verify signing from the Setup tab.

MAVLink proxy

How the agent forwards signed frames to the flight controller.

REST API

Full API surface and authentication headers.

Safety

Checks to run before any flight.
Last modified on October 11, 2026