๐Ÿ›ก๏ธ FLYsafe.live โ€” DAA, Custodian & Runner Architecture Strategy

Cloud detect-and-avoid, the Custodian/Runner execution model, and cross-protocol avoidance behavior โ€” MAVLink and DJI
v1.6 โ€” Adds DJI Cloud API deep-dive: DRC fly_to_point, breakpoint resume, Edge SDK scope, FlightHub 2 exclusivity TECH-002 / FSL โ€” RTM Platform
Prepared for Lindsay Mohr, AIRmarket ยท Grounded against the am-flyrtm-blender source (Flight Blender / InterUSS derivative) and FigJam board "Custodian-to-Runner Fence and Coverage Sync"

๐ŸŽฏ What this document establishes

Contents

  1. Core thesis & component responsibilities
  2. RTM Core internals โ€” the code-grounded DAA pipeline
  3. Surveillance service performance vs. coverage geometry (ASTM F3623 SDSP)
  4. Custodian & Runner โ€” parallel AMQP consumers, not a relay
  5. Autopilot avoidance paths โ€” ADS-B injection vs. Guided Mode
  6. Three-tier hybrid avoidance โ€” onboard, local injection & cloud (DJI + MAVLink)
  7. MAVLink vs. DJI โ€” automatic entry/exit and operator visibility
  8. IP boundary โ€” what's stock ArduPilot vs. what's FLYsafe's own
  9. Operator visibility layer โ€” Mission Planner Plugin & Chrome extension
  10. Runner deployment model โ€” standardized small-form-factor hardware
  11. Open questions & next steps
  12. Appendix โ€” FigJam reference diagrams (native recreations)
  13. DJI Cloud API deep-dive โ€” DRC, breakpoint resume, Edge SDK & FlightHub 2 exclusivity

1. Core Thesis & Component Responsibilities

The hybrid DAA architecture spans three components. Each has exactly one job, and none of them duplicates another's reasoning โ€” this separation is what keeps the system auditable and lets any one layer degrade without silently corrupting the others.

โ˜๏ธ
RTM Core โ€” the cloud brain
RTM
Owns flight binding, the fleet-wide picture, flight authorization, flight tracking, and all DAA reasoning.
  • Generates DAA notifications and proximity alerts
  • Computes the actual avoidance maneuver, already checked against geofences and ground risk
  • Publishes commands once โ€” never talks to the aircraft directly
๐Ÿชž
Cloud Custodian โ€” per-aircraft watchdog
Custodian
One instance per aircraft. Purely supervisory โ€” TCAS-analogous, never a relay or a decision-maker.
  • Independently receives the same messages RTM publishes
  • Verifies via real telemetry that the Runner did what the message said
  • Reports tiered DAA status back to RTM: full, degraded, no qualified surveillance
๐Ÿ›ฉ๏ธ
Autopilot Runner โ€” protocol-agnostic edge
Runner
Same Custodian, same RTM contract โ€” the implementation branches per airframe.
  • Acts on the message immediately โ€” no wait on the Custodian
  • Translates the cloud-computed maneuver into native protocol commands
  • Confirms actual execution from real telemetry, not just a command acknowledgment
Governing principle RTM decides. The Runner executes. The Custodian verifies. No component performs another's job โ€” the Custodian never decides what maneuver to fly, and the Runner never re-derives whether a conflict exists. Each layer trusts the layer above it for its inputs and is independently responsible for its own output being honest.

2. RTM Core Internals โ€” the Code-Grounded DAA Pipeline

This section is grounded directly in the am-flyrtm-blender repository (a Flight Blender / InterUSS derivative), not just the whiteboard. Four subsystems carry the pipeline from a live telemetry frame through to a delivered avoidance command.

Flight Authorization
flight_declaration_operations ยท scd_operations ยท conformance_monitoring_operations
โ†’
Flight Tracking
flight_feed_operations ยท rid_operations (ASTM F3411)
โ†’
Cloud DAA
detect_and_avoid_operations (ASTM F3442)
โ†’
notification_operations
AMQP delivery
Fig. 1 โ€” The four subsystems that carry a telemetry frame from ingestion to a published avoidance command.

Conflict detection & alert tiers (conflict_detection.py)

Analytical closest-point-of-approach (CPA) time is computed between ownship and each intruder, backed by a discrete trajectory-sampling check (_sample_min_separation) over a time horizon as a secondary verification. Separation is then mapped straight onto ASTM F3442 alert tiers:

Alert LevelTriggerMeaning
WARNINGHorizontal & vertical separation below NMAC thresholdsNear mid-air collision predicted
CAUTIONSeparation below Well-Clear thresholdsWell-Clear breach predicted
ADVISORYSeparation below 1.5ร— Well-Clear thresholdsApproaching Well-Clear
NON_ALERTSeparation above all thresholdsNo conflict

Alert lifecycle & avoidance engine

alert_lifecycle.py implements ASTM F3442 ยง8.2 alert creation/escalation/resolution and ยง10.2.3 periodic logging, with latency instrumentation at every hop (processing latency, sensor latency, alert-notify latency โ€” all measured, not assumed). Once an alert escalates, avoidance.engine's DefaultAvoidanceEngine selects a maneuver per ASTM F3442 ยง9.2, and avoidance.ground_risk filters that maneuver against geofence exclusion zones before it's ever sent anywhere โ€” the Runner never receives a maneuver that hasn't already been checked against ground risk.

How the maneuver is actually chosen โ€” candidate generation & forward-simulated scoring

This is the part that makes it rich avoidance rather than a fixed rule: DefaultAvoidanceEngine.compute_maneuver() doesn't just apply a lookup table, it generates several candidate maneuvers and predicts the outcome of each one before picking a winner.

1
Generate candidates
Descend, climb, right turn, left turn, and a combined right-turn-plus-descend โ€” each clamped to the airframe's envelope (max climb/descent rate, max turn rate) and the time actually available before CPA.
2
Forward-simulate each candidate
The intruder's projected position is reconstructed from the stored bearing/range. Each candidate's modified heading and vertical rate are re-run through the same _sample_min_separation trajectory sampler used for detection, producing a predicted post-maneuver minimum separation for that specific candidate.
3
Apply GPS-accuracy safety margin
Predicted horizontal and vertical separation are each reduced by the configured GPS accuracy figures before scoring โ€” the engine never credits a maneuver with more clearance than the position source can actually guarantee.
4
Ground-risk filter every candidate
avoidance.ground_risk.trajectory_enters_geofence checks each candidate's projected path against the active geofence set. Any candidate that would enter a geofence is penalised to near-zero in the scoring โ€” it can still be selected only if literally nothing else is better, and even then it's flagged infeasible.
5
Select & assess feasibility
The candidate with the best predicted horizontal separation wins. The engine then reports whether the chosen maneuver is actually feasible โ€” flagged insufficient_time, ground_risk_conflict, or envelope_limit when it isn't โ€” rather than silently commanding a maneuver that can't achieve well-clear.
What RTM hands off, precisely The output is not a waypoint or a lat/lon target โ€” it's a maneuver: heading change (degrees), target vertical rate (m/s), duration (seconds), the predicted resulting separation, an urgency tag, and a feasibility verdict. That maneuver shape is exactly what flows into _send_avoidance_command() and maps directly onto both the DJI DRC live-control channel and MAVLink's streamed SET_POSITION_TARGET_GLOBAL_INT loop described in ยง5 โ€” the Runner translates the maneuver into protocol-native commands, it never re-derives where to go.

3. Surveillance Service Performance vs. Coverage Geometry (ASTM F3623 SDSP)

Correction record surveillance_monitoring_operations was initially assumed to compute RF-propagation coverage volumes. It does not. It implements the seven ASTM F3623 SDSP service performance metrics via SurveillanceMetricCalculator: heartbeat rate, heartbeat delivery probability, track update probability, per-sensor MTTR, auto-recovery time, MTBF (with/without auto recovery), and failure notifications. This is about whether the surveillance service is performing to spec over time โ€” not where it can physically see.
The coverage-zone concept is standards-aligned, not invented The standard's own HealthMessage dataclass already carries a field: machine_readable_file_of_estimated_coverage: str โ€” currently an unused placeholder in the repo. ASTM F3623 itself anticipates exactly the RF-propagation coverage model under discussion. Building it fills a real, existing hook rather than bolting on an unrequired feature.
ConceptWhat it measuresStatus
Seven SDSP metrics (SurveillanceMetricCalculator)Service reliability over time โ€” is the sensor delivering heartbeats/tracks at the required rateImplemented
Sensor health (SurveillanceSensor, SurveillanceSensorHealth)Operational / degraded / outage status per registered sensorImplemented
Coverage Propagation Model (new)Spatial geometry โ€” where the sensor can physically seePlanned โ€” fills HealthMessage.machine_readable_file_of_estimated_coverage

These are two distinct, legitimate axes of SDSP health per the standard โ€” reliability and geometry โ€” not competing designs. Both feed the same HealthMessage published up through the notification path.

4. Custodian & Runner โ€” Parallel AMQP Consumers, Not a Relay

Fundamental design change (this revision) Previously modeled: RTM publishes the avoidance command to the Custodian, which relays it to the Runner. Revised: the Custodian is no longer a delivery hop. RTM publishes once; the Custodian and the Runner each independently subscribe to the same message stream and receive every message in parallel. The Runner acts the moment it receives its copy โ€” it does not wait on the Custodian. The Custodian, having received the identical message, independently verifies via the aircraft's real telemetry that the Runner actually did what the message said, and reports that confirmation (or a mismatch) back to RTM as the tiered DAA status.

This mirrors TCAS-style monitoring rather than gating: the safety-critical path (RTM โ†’ Runner) has no extra hop that can add latency or become a silent single point of failure, and the Custodian becomes a true independent check rather than a relay that could itself fail to pass something along.

RTM Core
avoidance commands ยท proximity alerts ยท fences ยท coverage
โ†“ publish once
AMQP Fanout / Topic Exchange (per flight declaration)
NOT a single competing-consumer queue
โ†“ copy of every message
Custodian's own bound queue
โ†“
Cloud Custodian
independent watchdog โ€” never a relay
โ†“ copy of every message
Runner's own bound queue
โ†“
Autopilot Runner
acts immediately โ€” no wait on Custodian
โ†“
Executes maneuver / fence upload on aircraft
Runner's real telemetry and status flow back to the Custodian, which compares "what the message said" against "what actually happened" and reports tiered DAA status (full / degraded / no qualified surveillance) to RTM.
Fig. 2 โ€” Custodian and Runner as parallel, independent AMQP consumers off a shared exchange.

What RTM actually publishes, and over which channel

Grounded in notification_helpers.py: the avoidance command and the proximity alert are not delivered identically.

MessageChannelPriority
Avoidance command (_send_avoidance_command)AMQP only โ€” flight declaration's own queuecritical
Proximity alert, active or resolved (_send_proximity_notification)AMQP and MQTT topic daa_alertsinfo / warning / critical by alert level
{ "command_type": "avoidance_maneuver", "alert_id": "<uuid>", "timestamp": "<iso8601>", "maneuver": { ... } }
Implementation requirement โ€” flag to Maykon & Raman A standard AMQP queue is competing-consumer: if the Custodian and Runner both bind to the same queue, messages split between them rather than both receiving every message. To get both a full copy of every message, RTM must publish to a fanout or topic exchange, with the Custodian and Runner each binding their own queue to it. This is a real RabbitMQ/AMQP topology change, not just a diagram relabel.

5. Autopilot Avoidance Paths โ€” ADS-B Injection vs. Guided Mode

On the MAVLink side, the Runner has two mechanisms available to actually cause an avoidance maneuver. Which one runs is an operator-configurable deployment setting โ€” FLYsafe's default deployment uses Guided Mode.

Path A โ€” ADS-B Injection AlternativePath B โ€” Guided Mode FLYsafe Default
MechanismRunner synthesizes an ADSB_VEHICLE message from intruder position and injects it into the MAVLink stream as a sensor contact.Runner receives RTM's already-computed maneuver and streams SET_POSITION_TARGET_GLOBAL_INT at heartbeat rate โ€” not the one-shot DO_REPOSITION.
Who decides the maneuverArduPilot's own AP_Avoidance library, using its tuned AVD_* parameters (action, warn/fail distances).RTM โ€” the maneuver was already checked against ground risk and geofences before the Runner ever sees it.
Runner's role after the triggerEnds at injection. No further command sent. Lower runner control, lower latency.Continuous โ€” keeps re-asserting the setpoint, watches live telemetry (GLOBAL_POSITION_INT, TERRAIN_REPORT) to confirm actual execution, commands climb-back once RTM signals the conflict is clear.
Known riskDepends entirely on ArduPilot's own tuning being correct for the deployment.GUIDED setpoints outside a loaded fence can be silently rejected โ€” COMMAND_ACK alone is not trustworthy; telemetry confirmation is mandatory.
Design note The choice between Path A and Path B is an operator configuration setting per ArduPilot deployment. FLYsafe's default is Guided Mode, for deterministic, verifiable outcomes where RTM's own risk-checked maneuver is guaranteed to actually execute. ADS-B injection remains available as a lighter-weight alternative that relies on ArduPilot's own tuning for continuous general traffic awareness.

6. Three-Tier Hybrid Avoidance โ€” Onboard, Local Injection & Cloud

ยง5 describes the two MAVLink-side mechanisms as an either/or operator setting. In practice they stack โ€” along with a hardware floor below both of them โ€” into a three-tier fallback ladder that runs from "always on, no intelligence" up to "richest available, requires the cloud." This reframes the Runner's role: it isn't only a translator for RTM's commands, it's also a local sensor-aggregation and injection layer that keeps functioning when RTM isn't reachable at all.

TierDJIMAVLink / ArduPilotRequires RTM link?Who decides
1 โ€” Hardware floor Native onboard vision/infrared obstacle detection. Universal, always on, independent of Runner or RTM. Optional Only present if the airframe has physical proximity sensors (Lidar, sonar, RealSense) wired to ArduPilot's own PRX parameters. No Flight-controller firmware itself โ€” physical-object stopgap only, no traffic awareness
2 โ€” Local sensor injection Runner aggregates whatever's wired to the companion computer (1090/978 ADS-B receiver, camera, acoustic sensor, etc.) and synthesizes a sensor-contact injection feeding the onboard avoidance logic locally. Same mechanism โ€” Runner synthesizes an ADSB_VEHICLE message from aggregated sensor data and injects it into the MAVLink stream; AP_Avoidance reacts via its tuned AVD_* parameters. This is ยง5's "Path A." No โ€” this is the no-connectivity fallback Onboard avoidance logic (DJI) / AP_Avoidance (ArduPilot), reacting to Runner-fed sensor data
3 โ€” Cloud DAA RTM's forward-simulated, geofence-checked maneuver from ยง2, delivered over the DRC channel (DJI) or streamed Guided Mode setpoints (MAVLink โ€” ยง5's "Path B"). Definitive when available. Yes RTM (DefaultAvoidanceEngine)
Key platform difference DJI's Tier 1 is universal hardware present on every airframe by default. MAVLink's Tier 1 is airframe-dependent and optional โ€” it simply doesn't exist unless someone wired proximity sensors to that specific frame. Tiers 2 and 3 are architecturally identical across both platforms: the Runner is the common hybrid injection point regardless of protocol, aggregating local sensors when alone and executing RTM's maneuver when connected.

Geofence distribution โ€” the same fence set reaches every tier

"Rich avoidance" in ยง2 means every candidate maneuver RTM considers is already checked against active geofences before it's ever published. That check is only the cloud-side half of the picture. The same fence set is distributed in parallel to more than just RTM's own filter:

Active geofence set (per flight declaration)
โ†“ pushed simultaneously
RTM's ground-risk filter
checks every Tier-3 candidate โ€” ยง2
Cloud Custodian
holds fences for its own verification
Autopilot Runner
holds fences for its own logic
ArduPilot onboard fence
native FENCE_TYPE, ArduPilot-only
Fig. 3 โ€” The same fence set reaches RTM, the Custodian, the Runner, and (for ArduPilot) the autopilot itself, at the same time.

The reason to push the fence set all the way onto the autopilot, and not only hold it cloud-side, is Tier 2. A Tier-2 local-injection maneuver never touches RTM โ€” there's no cloud ground-risk filter in that loop by definition, since it's the fallback for exactly the case where RTM isn't reachable. If the onboard fence is loaded natively on the ArduPilot flight controller, the airframe itself refuses to fly into excluded airspace regardless of which tier triggered the maneuver โ€” ground-risk enforcement becomes a property of the aircraft, not just a property of the cloud reasoning. DJI and Custodian/Runner-held copies serve the equivalent purpose one layer up: the Custodian can flag a Runner action that ignored a fence as a verification failure even without an onboard native fence to fall back on.

Status vs. this architecture โ€” see ยง11 The onboard-fence half of this diagram is the intended design, not yet the current state. Per the existing gap already tracked in ยง11: no MISSION_TYPE_FENCE upload protocol code exists today, and the reference airframe's own parameters show FENCE_TOTAL -103 โ€” no fence points loaded. RTM's own ground-risk filter (ยง2) and the Custodian/Runner-held copies are real today; the autopilot-native leg of the fence-distribution diagram above is the piece that still needs to be built.

7. MAVLink vs. DJI โ€” Automatic Entry/Exit and Operator Visibility

Both protocol implementations follow the same shape: automatic entry, automatic exit โ€” no manual confirmation gate on either end by default. This matches the precedent already set by UAvionix Casia, which today automatically pushes ArduPilot back into AUTO once its onboard DAA logic clears, relying on operator training rather than a manual resume step.

StageMAVLink / ArduPilotDJI / Cloud API DRC Channel
Automatic entryRunner commands GUIDED mode directly โ€” no operator confirmation required.Cloud sends cloud_control_auth_request; RC authorizes; aircraft reports is_cloud_control_auth = true.
Command deliveryStreamed SET_POSITION_TARGET_GLOBAL_INT at heartbeat rate โ€” deterministic descent and hold.Joystick-style near-real-time commands over the DRC live-control MQTT channel.
Mission handlingGUIDED holds the setpoint continuously while the Runner keeps re-asserting it.Waypoint mission is canceled while cloud control is active โ€” native DJI behavior, not FLYsafe-specific.
Operator visibilityRunner sends STATUSTEXT directly into the Mission Planner messages panel โ€” "entering DAA mode."FlightHub 2 control-status and mode flags only โ€” Gap no free-text HUD equivalent today.
Automatic exitOnce RTM signals the conflict cleared, Runner commands mode back to AUTO and sends a second STATUSTEXT.Runner automatically releases DRC authority.
Mission resumptionResumes AUTO mission directly.DJI Dock's native "Resume Flight From Breakpoint" resumes rather than restarting โ€” Caveat hardware-gated (confirmed M30/30T, Mavic 3E/3T/3M only) and DJI's own dock explicitly warns against auto-resuming after an obstacle-triggered stop. See ยง13.
Manual overrideOptional configurable fallback โ€” manual resume gate for operators who want tighter control. Not the default.Not modeled โ€” automatic release is DJI-native behavior.
Known asymmetry MAVLink has a free-text STATUSTEXT message bus straight into the GCS HUD. DJI has no equivalent today โ€” the operator relies entirely on FlightHub 2's own UI state. Operator training therefore differs by platform: "watch Mission Planner's message log" vs. "watch FlightHub 2's control-status indicator." This asymmetry is closed at the tooling layer, not the protocol layer โ€” see ยง9.

8. IP Boundary โ€” What's Stock ArduPilot vs. What's FLYsafe's Own

Worth being precise about this, since it's the actual differentiation story.

โš™๏ธ
Stock ArduPilot โ€” not FLYsafe IP
Off-the-shelf
Available to any ArduPilot operator, unmodified.
  • AP_Avoidance reacting to injected ADSB_VEHICLE messages
  • Native DAA_* parameter set (DAA_AVD_ACTION, DAA_AVD_ALERT, DAA_AVD_ALT, DAA_MARGIN_FENCE)
  • Native fence support โ€” FENCE_TYPE, FENCE_TOTAL, mission-protocol polygon upload
๐Ÿ’ก
FLYsafe's actual IP
Differentiation
Everything that decides what to feed the autopilot and confirms it worked.
  • Translating an AMQP avoidance command into the right ArduPilot-native action
  • Deciding when to push a fence vs. inject synthetic traffic vs. command deterministic Guided descent
  • The verification loop reading real telemetry back, since ArduPilot can silently reject a fence-violating command with no error

The honest boundary: ArduPilot is the muscle โ€” dumb and reactive, doing exactly what any operator's ArduPilot does. FLYsafe's differentiation is the orchestration layer above it: cloud DAA reasoning, delivery-and-trust verification through the Custodian, and the Runner's translation-and-confirmation logic that turns a cloud-level "avoid this" into a reliable, provably-executed action on a stock autopilot โ€” without modifying the autopilot itself. That's a strength: it works on any ArduPilot airframe without custom firmware.

9. Operator Visibility Layer โ€” Mission Planner Plugin & Chrome Extension

The same RTM message and status stream branches out to two thin, surface-specific injection points โ€” closing the DJI visibility gap identified in ยง7.

Mission Planner PluginFLYsafe.live RTM Chrome Extension
StatusAlready builtNew, planned
HostMission Planner desktop GCS (MAVLink)Any browser-based GCS โ€” FlightHub 2 today, extensible to Skydio or other web consoles
MechanismNative plugin injecting STATUSTEXT-style messages directly into Mission Planner's messages panelBrowser extension augmenting the web page UI directly โ€” not a DJI API integration
What it surfacesDAA mode entry, DAA mode exit, avoidance maneuver statusDAA messages, status changes, and control-state updates as an in-page overlay or banner
Why it mattersOperator's existing tool, no new installFills the DJI visibility gap โ€” no native STATUSTEXT equivalent exists in FlightHub 2 itself
Design principle One shared RTM message and status stream, two lightweight surface-specific plugins. Because the Chrome extension augments the page rather than integrating with any single vendor's API, the same pattern extends to any future web-based ground control platform without rework.

10. Runner Deployment Model โ€” Standardized Small-Form-Factor Hardware

ยง3 and ยง9 describe the Autopilot Runner as software living inside the operator's existing GCS session โ€” the Mission Planner Plugin, the Chrome GCS Plugin. That covers the attended case, where a human is at a laptop running Mission Planner or a browser for the duration of the flight. It does not cover unattended, dock-based operations โ€” a DJI Dock cycling missions with nobody at a console, or a fixed MAVLink installation with no GCS laptop physically present. The intent going forward is to standardize the Runner's deployment for exactly that case onto a small, dedicated piece of onboard/dock hardware, so the Runner becomes a physical unit that ships once and works the same way regardless of airframe or dock vendor.

Deployment direction Standardize the Autopilot Runner onto a small-form-factor single-board computer (SBC) that mounts onboard the aircraft or inside the dock unit and connects to the autopilot (or the dock's own network) over a standard RJ45 Ethernet connection. One hardware SKU, one Runner software image, deployed identically whether the airframe underneath is a DJI Dock-managed drone or a MAVLink/ArduPilot airframe โ€” only the protocol-adapter layer inside the Runner differs (ยง3), not the box it runs on.

Why standard Ethernet, and why now

Wiring an SBC to the autopilot (or to the dock's internal network switch) over RJ45/Ethernet rather than USB, serial, or a vendor-specific breakout cable keeps the hardware side of the Runner drone-agnostic: Ethernet is already the physical layer DJI Dock installations use internally, and it's a standard, shielded, long-run-tolerant connector that any MAVLink companion-computer carrier board can expose as well. A single cable class means the same mounting kit and cable run works whether the target is a dock-side network port or an onboard companion-computer Ethernet header โ€” no per-airframe adapter cables to stock, qualify, or lose in the field. Where the candidate hardware also supports Power-over-Ethernet (see below), the same single cable carries both data and power, which materially simplifies onboard wiring compared to running a separate power rail alongside a data cable.

Candidate hardware

Two small-form-factor SBCs are under evaluation as the standard Runner hardware unit. Neither has been bench-tested or committed to yet โ€” this is a candidate list, not a finalized BOM.

๐Ÿ–ฅ๏ธ
Radxa ZERO 3E
Candidate 1
Compact SBC, Rockchip RK3566 quad-core Cortex-A55 (up to 1.6 GHz), Mali-G52 GPU, LPDDR4 RAM, boots from microSD.
  • Gigabit Ethernet with PoE support โ€” one cable for data and power
  • USB 3.0 Type-C (host) + USB 2.0 Type-C (OTG, power/data)
  • Micro HDMI out; 22-pin MIPI CSI camera connector
  • H.264/H.265 decode to 4K@60fps โ€” headroom for onboard video/evidence capture if needed later
๐Ÿ”Œ
Orange Pi Zero 3
Candidate 2
Compact SBC, Allwinner H618 quad-core Cortex-A53, 1/1.5/2/4GB LPDDR4 RAM options, boots from microSD (16MB onboard SPI flash) โ€” a lower-cost or higher-availability alternative to evaluate alongside the Radxa unit.
  • Gigabit Ethernet (RJ45) โ€” no PoE; powered separately via USB-C (5V/2–3A)
  • WiFi 5 + Bluetooth 5.0 on-board (not needed for the wired deployment model, but available)
  • USB 2.0 port; Micro HDMI out; GPIO expansion headers
  • Manufacturer product page โ†’
PoE is not universal across candidates. Candidate 1 (Radxa ZERO 3E) can take power and data over the same Ethernet cable. Candidate 2 (Orange Pi Zero 3) has Gigabit Ethernet but draws power separately over USB-C โ€” a single-cable PoE install is not an option with this board as-is. This is a real factor in the hardware decision, not just a spec-sheet footnote: it changes the wiring harness and power-budget story for an onboard or dock-mounted install.
Autopilot (MAVLink) or Dock internal network (DJI)
โ†• RJ45 / Ethernet โ€” PoE where supported: one cable, power + data
Autopilot Runner
standardized SBC โ€” same hardware SKU regardless of airframe
โ†• uplink (dock network / cellular / WiFi, deployment-dependent)
RTM Core & Cloud Custodian APIs
Fig. 4 โ€” Standardized Runner hardware sits between the autopilot/dock network and the cloud, over the same Ethernet physical layer regardless of which airframe or dock vendor is underneath it.
Open items before this is more than a candidate list

11. Open Questions & Next Steps

1
AMQP fanout/topic exchange topology
Custodian and Runner each need their own bound queue off a fanout or topic exchange โ€” this is real infrastructure work for Maykon and Raman, not just a diagram change. Confirm exchange type and per-flight-declaration queue naming convention.
2
Maneuver translation โ€” RTM's heading/rate output vs. what each protocol actually needs
RTM's DefaultAvoidanceEngine outputs a heading change and a target vertical rate, held for a duration โ€” a rate command, not a position. Neither execution path can consume that directly: MAVLink Guided Mode needs a projected lat/lon/alt target (streamed via SET_POSITION_TARGET_GLOBAL_INT), and DJI's DRC channel needs joystick-style stick-equivalent commands. Someone has to own the translation from "heading delta + vertical rate + duration" into each protocol's native input. Open design question: does RTM itself compute and publish the projected lat/lon (so the maneuver message already carries a position target), or does the Runner own that projection locally from RTM's heading/rate output? Whoever owns it needs to be decided before the Runner-side execution work (below) can be built against a stable contract. Flag as unresolved โ€” needs a decision, not just implementation.
3
GUIDED setpoint loop in aircraftagent.py โ€” note: this codebase is actively in flux
Dead-code maneuver functions (mavlink_do_reposition, mavlink_nav_loiter_time, mavlink_nav_loiter_turns, mavlink_set_mode) exist but are one-shot position commands, not a streamed rate/setpoint loop, and none of them are called anywhere today. Once the translation question above is settled, need a real heartbeat-maintained SET_POSITION_TARGET_GLOBAL_INT loop, telemetry-based confirmation against silent fence rejection, and an AMQP consumer for command_type == "avoidance_maneuver". This snapshot reflects the runner repo at time of writing โ€” expect it to move.
4
Exclusion-zone fence upload
No MISSION_TYPE_FENCE protocol code exists yet. FENCE_TYPE 4 is set in drones/dellvostro/mav.parm but FENCE_TOTAL -103 โ€” no fence points loaded. This is the concrete implementation gap behind the geofence-distribution model in ยง6: the architecture calls for the same fence set to reach the onboard autopilot for ArduPilot airframes, but the upload path itself doesn't exist in code yet.
5
Avoidance-event feedback to RTM/Custodian
Pattern exists for RTL (send_rtl_to_rtm()) โ€” needs an equivalent for avoidance-command-issued / acked / rejected and fence-upload-status, feeding the Custodian's verification loop described in ยง4.
6
DJI DRC channel implementation
Build the descent-and-hold command over the DRC live-control channel, mirroring the MAVLink guided-mode behavior; confirm DJI Dock's Resume Flight From Breakpoint behaves as expected for our mission-resumption case.
7
Chrome extension scoping
Define the DOM/API hook points on FlightHub 2 for the in-page overlay; confirm distribution path (Chrome Web Store vs. enterprise extension) alongside the existing Chrome GCS Plugin work already underway for telemetry ingestion.

12. Appendix โ€” FigJam Reference Diagrams (Native Recreations)

The six diagrams below are recreated directly from the "FLS - Architecture Sept 2026" FigJam board (source: figma.com/board/gO1lwj33cuJpnsw0fdSI7U), box for box and label for label, so the board's full detail is permanently part of this document rather than a link or a screenshot that can drift out of sync. Each diagram links back to its exact spot on the live board and notes which section of this document it backs up.

1 โ€” FSL Solutions Components โ†” ยง1

FLYsafe.live RTM
RTM Core โ€” flight binding, fleet-wide picture
โ†“
DAA Proximity Engine โ€” detects conflicts and near misses from cooperative traffic
โ†“
Exclusion Zone Source โ€” towers, no-fly polygons
โ†“
Surveillance Coverage Engine โ€” RF propagation model, feeds HealthMessage coverage field
Cloud Custodian
Custodian โ€” supervisory layer
โ†“
Verification Manager โ€” compares message received against Runner's real telemetry, confirms the anticipated action happened
โ†“
Tiered DAA Status โ€” full, degraded, no qualified surveillance
Autopilot Runner
Runner Core โ€” credential & session management, protocol translation, acts immediately on message, no wait on Custodian
โ†“
MAVLink Runner โ€” ArduPilot support
โ†“
DJI Runner โ€” FlightHub 2 and DRC channel support
Aircraft
MAVLink Autopilot โ€” ArduPilot
โ†“
DJI Autopilot โ€” DJI flight controller
Cross-lane connections (7)
  • RTM โ†’ Custodian & Runner: copy of every message (parallel AMQP consumers โ€” ยง4)
  • Custodian โ†’ RTM: DAA performance tier
  • Runner โ†’ Custodian: status and confirmation telemetry
  • Runner โ†’ MAVLink Autopilot: fence upload, ADSB_VEHICLE injection, guided setpoint
  • Runner โ†’ DJI Autopilot: DRC channel commands, traffic injection
  • MAVLink Autopilot โ†’ Runner: confirmed fence state, telemetry
  • DJI Autopilot โ†’ Runner: confirmed state, telemetry

2 โ€” FSL End-to-End Workflow โ†” ยง2

Flight Authorization
flight_declaration_operations + scd_operations + conformance_monitoring_operations
FlightDeclaration โ€” operational intent, volumes
โ†“
custom_volume_generation โ€” operational and contingency volumes
โ†“
deconfliction_engine and protocol โ€” strategic deconfliction vs. other declared flights
โ†“
scd_operations / dss_scd_helper โ€” DSS submission and federation, ASTM F3548
โ†“
conformance_monitoring_operations โ€” state machine: accepted, activated, nonconforming, contingent, withdrawn
Flight Tracking
flight_feed_operations + rid_operations
FlightObservation โ€” telemetry ingestion
โ†“
rid_telemetry_helper โ€” Remote ID telemetry normalization
โ†“
rid_operations โ€” ASTM F3411 network Remote ID compliance
โ†“
flight_stream_helper โ€” live feed distribution
Cloud DAA
detect_and_avoid_operations, ASTM F3442
aircraft_state โ€” ownship and intruder state, track_cache
โ†“
conflict_detection โ€” analytical CPA time, discrete trajectory sampling, alert level: NON_ALERT / ADVISORY / CAUTION / WARNING
โ†“
alert_lifecycle โ€” alert creation, escalation, resolution, periodic logging (ยง8.2, ยง10.2.3)
โ†“
avoidance.engine โ€” DefaultAvoidanceEngine, maneuver selection (ยง9.2 โ€” see ยง2 candidate-generation detail)
โ†“ selected maneuver
avoidance.ground_risk โ€” geofence-aware maneuver filtering
โ†“ maneuver dict / active or resolved alert
notification_helpers โ€” builds AMQP and MQTT payloads
โ†“
daa_notifications โ€” DAAConformanceNotification wrapper
Surveillance Service Performance
surveillance_monitoring_operations, ASTM F3623 SDSP
SurveillanceSensor / SurveillanceSensorHealth โ€” registered sensors: operational, degraded, outage
โ†“
SurveillanceMetricCalculator โ€” 7 SDSP metrics: heartbeat rate, heartbeat delivery probability, track update probability, MTTR, auto-recovery time, MTBF, failure notifications
โ†“
HealthMessage โ€” current_status, scheduled_degradations, machine_readable_file_of_estimated_coverage
โ†“ 7 SDSP performance metrics
Coverage Propagation Model โ€” New RF-propagation-based estimated coverage geometry; fills the existing but unimplemented coverage field in HealthMessage
Supporting Sources
geo_fence_operations โ€” exclusion zone geometry
โ†“
notification_operations โ€” NotificationFactory, AMQP delivery
Cross-lane connections (8)
  • Flight Authorization โ†’ Cloud DAA: conformance state
  • Flight Authorization โ†’ Cloud DAA: contingency volume
  • Flight Tracking โ†’ Cloud DAA: live telemetry
  • Supporting Sources (geo_fence_operations) โ†’ Cloud DAA: exclusion geometry
  • Supporting Sources โ†’ onboard/downstream consumers: exclusion geometry, direct (feeds the ยง6 geofence-distribution model)
  • Cloud DAA โ†’ notification_operations: command_type: avoidance_maneuver, critical, AMQP only
  • Cloud DAA โ†’ notification_operations: proximity alert, active or resolved, AMQP + MQTT topic daa_alerts
  • Surveillance Service Performance โ†’ notification_operations: surveillance service status plus coverage

3 โ€” Autopilot DAA Implementation Paths โ†” ยง5

Path A โ€” ADS-B Injection
delegated to ArduPilot native avoidance
Runner synthesizes ADSB_VEHICLE message from intruder position
โ†“
Injected into MAVLink stream as a sensor contact
โ†“
ArduPilot AP_Avoidance evaluates threat using AVD_* parameters โ€” action, warn/fail distances
โ†“
ArduPilot decides & executes maneuver internally โ€” climb, descend, horizontal, or report-only
โ†“
Runner role ends at injection โ€” no further command sent, lower Runner control, lower latency
Path B โ€” Guided Mode Deterministic Command
FLYsafe default deployment
Runner receives RTM's computed maneuver โ€” exact descent altitude and hold position
โ†“
Runner streams SET_POSITION_TARGET_GLOBAL_INT at heartbeat rate โ€” not one-shot DO_REPOSITION
โ†“
ArduPilot GUIDED mode holds the setpoint continuously while Runner keeps re-asserting it
โ†“
Runner watches live telemetry (GLOBAL_POSITION_INT, TERRAIN_REPORT) to confirm actual execution
โ†“
Risk flagged in code review: GUIDED setpoints outside a loaded fence can be silently rejected โ€” COMMAND_ACK alone is not trustworthy
โ†“
Runner commands climb-back to original altitude once RTM signals the conflict cleared

4 โ€” Autopilot Avoidance Implementation Similarities โ†” ยง7

ArduPilot โ€” MAVLink
default is Guided Mode
Automatic entry โ€” Runner commands GUIDED mode, no operator confirmation required
โ†“
Runner streams SET_POSITION_TARGET_GLOBAL_INT at heartbeat rate โ€” deterministic descent and hold
โ†“
Runner sends STATUSTEXT to Mission Planner's messages panel โ€” "entering DAA mode"
โ†“
Runner watches live telemetry (GLOBAL_POSITION_INT, TERRAIN_REPORT) โ€” confirms actual execution, doesn't trust COMMAND_ACK alone
โ†“
Automatic exit โ€” once RTM signals conflict cleared, Runner commands mode back to AUTO
โ†“
Runner sends second STATUSTEXT โ€” "DAA mode cleared, resuming mission"
โ†“
Optional configurable fallback โ€” manual resume gate for operators wanting tighter control, not the default
DJI โ€” Cloud API DRC Channel
default is automatic seize & release
Automatic entry โ€” cloud sends cloud_control_auth_request, RC authorizes, aircraft reports is_cloud_control_auth = true
โ†“
FlightHub continuously seizes & maintains flight control during the event โ€” matches DJI-native live-control behavior
โ†“
Runner issues descent-and-hold via the DRC live-control MQTT channel โ€” joystick-style, near-real-time
โ†“
Waypoint mission is canceled while cloud control is active โ€” DJI-native behavior, not FLYsafe-specific
โ†“
Operator visibility โ€” FlightHub 2 control-status and mode flags only, no STATUSTEXT-equivalent free-text HUD message today
โ†“
Automatic exit โ€” once RTM signals conflict cleared, Runner releases DRC authority
โ†“
Resume Flight From Breakpoint โ€” DJI Dock native feature, cleanly resumes rather than restarting

5 โ€” GUI Plugins โ†” ยง9

Mission Planner Plugin
existing, native desktop GCS integration
Already built โ€” Mission Planner Plugin, MAVLink
โ†“
Injects STATUSTEXT-style messages directly into Mission Planner's messages panel
โ†“
Surfaces DAA mode entry, DAA mode exit, avoidance maneuver status
FLYsafe.live RTM Chrome Extension
new, planned
Browser extension โ€” not a DJI API integration, augments the web page UI directly
โ†“
Targets any browser-based GCS โ€” FlightHub 2 today, extensible to Skydio or other web consoles
โ†“
Injects DAA messages, status changes, and control-state updates as an in-page overlay or banner
โ†“
Fills the DJI visibility gap โ€” no native STATUSTEXT equivalent exists in FlightHub 2 itself

6 โ€” Custodian vs Runner Responsibility Update โ†” ยง4, Fig. 2

This board diagram documents the same fanout/topic-exchange redesign already fully diagrammed as Fig. 2 in ยง4 โ€” RTM Core, the AMQP fanout exchange, Custodian's and Runner's own bound queues, verification, and tiered DAA status reporting back to RTM โ€” down to the same two callouts captured there: Custodian previously relayed the command to the Runner as a middle hop; now Custodian and Runner are parallel, independent subscribers to the same exchange, and AMQP queues are competing-consumer by default, so both must bind their own queue to a fanout or topic exchange to each receive every message. Not redrawn a second time here to avoid duplicating Fig. 2 pixel-for-pixel โ€” see ยง4 directly.

13. DJI Cloud API Deep-Dive โ€” DRC, Breakpoint Resume, Edge SDK & FlightHub 2 Exclusivity

This section corrects and expands the DJI framing used earlier in this document (ยง5โ€“ยง7), based on a direct read of DJI's Cloud API and Edge SDK documentation. The short version: DJI avoidance execution is not limited to raw joystick-style stick input as originally framed, and the "put compute on the dock to skip the cloud" idea does not hold up against DJI's actual SDK boundaries. Everything below is grounded in DJI's own public docs (dji-sdk/Cloud-API-Doc, dji-sdk/Edge-SDK, and DJI's developer/enterprise-insights sites) โ€” flagged where something is a named feature versus a confirmed field-level schema.

13.1 โ€” DRC has a fly_to_point method, not just joystick input

DJI's live flight control feature set, reached only after seizing DRC control authority, includes flight direction control, gimbal rotation control, and one-key taking off and flyto โ€” a single-point "fly to" command sitting alongside pure directional stick control, not a replacement for the whole DRC concept. The fly_to_point method's payload carries a points array, and each point includes latitude, longitude, and height โ€” so altitude is an explicit, settable field, not inferred. DJI's docs describe this height as the target point height using the ellipsoidal height, WGS84 model, with the aircraft hovering at the point by default once it arrives. A second, separate altitude reference is also available: relative altitude to the takeoff point (the dock), where the aircraft ascends to a specific height before flying to the target point.

Two altitude reference frames, not one fly_to_point lets you set either a WGS84 ellipsoidal absolute height or a dock-relative (AGL-style) height. Given the NRCan HRDEM/MRDEM AGL/MSL compliance work already established elsewhere in this project, whichever field the Runner populates for a DJI avoidance maneuver needs to be deliberate, not defaulted โ€” the two are not interchangeable and mixing them up would silently corrupt containment math.

The command is not fire-and-forget: a companion fly_to_point_stop method exists, and DJI's documentation notes the target point can be updated mid-flight without leaving the flyto/DRC process. There's also a minimum-altitude safety floor built into the aircraft itself โ€” a 20-meter minimum flight altitude safety mechanism, so the aircraft will first rise to 20 meters if commanded below that relative to the takeoff point.

13.2 โ€” Wayline breakpoint resume is real, but hardware-gated and explicitly discouraged after an obstacle event

DJI's wayline management system does support pausing and resuming a mission, including resuming flight from a breakpoint: the dock provides breakpoint information to the drone, the drone flies back to that point, and continues the wayline mission from there. The flighttask_resume-style call includes an optional field specifically for resuming from a specified breakpoint. This makes a clean DRC-interrupt-then-resume recovery model plausible: Runner enters DRC, executes fly_to_point as the avoidance maneuver, exits DRC, then calls wayline resume with the breakpoint reference.

CaveatWhat DJI's docs actually say
Hardware gateBreakpoint resume is confirmed supported on Matrice 30/30T and Mavic 3 Enterprise/3T/3M. Not a guaranteed capability across every DJI airframe.
Obstacle-triggered stopA real DJI dock error code (321517) reads: "obstacle detected; task stopped; to ensure flight safety, do not resume task from breakpoint." DJI itself builds in a safety guard against blind breakpoint-resume specifically after an obstacle-triggered interruption โ€” which is exactly our avoidance scenario.
Design implication Given DJI's own 321517 guidance, the Runner should not treat "exit DRC, call resume-from-breakpoint" as a safe default recovery path immediately after an avoidance maneuver without an explicit re-validation step (fresh containment/traffic check) first. This is a real behavioral difference from the MAVLink side, where GUIDED-to-AUTO resumption has no equivalent DJI-native warning attached to it.

13.3 โ€” Can the DRC authority request happen without a human physically approving it?

For a docked, unmanned deployment (our case), yes โ€” functionally. DJI's documented sequence is device-to-device: the web/cloud side sends cloud_control_auth_request, and the "Pilot" side (DJI Pilot 2, running as the dock's own software agent, not a human holding a physical remote) agrees and reports back is_cloud_control_auth = true. There is no human-in-the-loop approval dialog inherent to a docked aircraft the way there would be for a handheld RC. Unconfirmed DJI's docs don't spell out "zero human involvement" in plain language for the dock case โ€” this is an inference from the architecture and sequence diagram, not an explicit guarantee, and should be sanity-checked against DJI support before being treated as load-bearing.

13.4 โ€” DJI Edge SDK: real, dock-based, but scoped to video/AI, not flight control

DJI Dock 2 does support integration of an edge computing module, and DJI ships a real product for it: Edge SDK, described consistently across every source as an edge computing development kit for DJI Dock, allowing real-time video recognition and other AI processes for aircraft-transmitted data streams on the local system. DJI frames its whole Cloud API around a device-edge-cloud layered architecture similar to IoT โ€” but the "edge" tier in DJI's own model is about local data/video processing, not a flight-command bypass.

No flight-command surface found in Edge SDK Nothing in DJI's Edge SDK documentation or repository describes a method for requesting or holding flight control authority locally at the dock. The only two paths that can actually command the aircraft to move are (1) Cloud API's DRC channel โ€” inherently cloud-mediated by DJI's own design, even though the dock sits on our local network, or (2) the older Onboard SDK/Payload SDK line, which requires a companion computer physically mounted on the aircraft, not the dock, and is not DJI's supported direction for Enterprise dock deployments today.

Conclusion: putting our own compute on the dock does not get us a local bypass around the cloud-mediated DRC authority handshake for flight control. Edge SDK is a legitimate, useful product for local perception/AI workloads at the dock, but it is not an alternate flight-control channel.

13.5 โ€” DJI Cloud API self-hosting, and why it can't run alongside FlightHub 2

Confirming the deployment model: yes, this is a self-hosted server we stand up ourselves (Java backend + EMQX broker are the typical reference stack), and it can run in our own Kubernetes cluster. DJI Cloud API can adapt to any network as long as the DJI Pilot 2 or DJI Dock is allowed to access the third-party platform server, over standard MQTT/HTTPS/WebSocket protocols.

Exclusive, not additive: one cloud binding per dock A dock connects to one cloud platform at a time. You can either use FlightHub 2 or your own Cloud API-based solution โ€” using both simultaneously to control the same dock is not supported, and DJI's own product architecture confirms the aircraft connects indirectly to the cloud through a single dock-to-cloud gateway relationship. Whichever platform is bound owns everything at once: telemetry, missions, and DRC control authority. There is no split where FlightHub 2 runs routine missions while our Cloud API separately injects avoidance overrides on the same dock.

One adjacent, non-real-time bridge is worth noting: FlightHub Sync 2.0 (the successor to the old Cloud Interconnect Beta) adds real telemetry forwarding โ€” real-time drone telemetry data can now be forwarded to external systems via MQTT โ€” plus an MQTT Bridge supporting forwarding of the dock's MQTT information to third-party systems as "a collaborative task solution." This means a FlightHub 2-bound dock can still stream telemetry to us. It does not change the control conclusion below.

13.6 โ€” Does FlightHub 2's own OpenAPI expose a live avoidance-command endpoint?

No, checked and re-checked across three separate research passes. FlightHub 2 has grown its own separate OpenAPI (distinct from the raw dock-to-cloud Cloud API), now expanded to 300-plus endpoints in the on-premises version, covering livestream management, wayline/route management, custom flight zones (create/read/update/delete), device management, and โ€” via FlightHub Sync 2.0 โ€” telemetry forwarding out to third parties. Across every source found, FlightHub 2's "FlyTo" and "Live Flight Controls" capability is described purely as a human-facing UI feature inside its browser-based Virtual Cockpit โ€” a person clicking a FlyTo destination on the map, or using keyboard/mouse to fly the aircraft โ€” never as a REST/MQTT method a third-party system can call programmatically. One DJI source states this outright: "This function is also available through the Cloud API for developers" โ€” explicitly marking FlyTo-as-callable-API as a Cloud API/DRC thing, not a FlightHub 2 OpenAPI thing.

Conclusion: the real-time avoidance command surface lives only behind the raw dock-to-cloud DRC channel, which requires our own Cloud API server to hold the seized control authority. FlightHub 2's OpenAPI, however large it grows, does not appear to expose that same capability to third parties.

13.7 โ€” Fallback option: a Chrome extension driving FlightHub 2's Virtual Cockpit

Raised as a fallback: could a browser extension synthesize input into FlightHub 2's Virtual Cockpit UI to mimic an operator issuing a FlyTo command? Technically plausible โ€” Virtual Cockpit is browser-based, and DJI's own FAQ confirms third-party controllers work today by mapping joystick buttons to keyboard keys, meaning the UI already accepts synthesized keyboard input as a control path.

ConcernWhy it matters
Unsupported automation surfaceThis automates a human-facing manual-override UI that DJI never documented as an integration point. Any DOM/UI change on DJI's side silently breaks it, with no deprecation notice the way a documented API would get.
Requires a live rendered sessionNeeds an actual browser tab actively running Virtual Cockpit and connected per aircraft โ€” not a clean headless service the way our other Runner/Custodian agents work.
Overlaps existing planned workThis is conceptually the same territory as the Chrome GCS Plugin for DJI FlightHub 2 already in this architecture (ยง9) โ€” an extension question, not a new component question.
Recommended framing Treat the Chrome-extension-driven Virtual Cockpit approach as a Tier-3, last-resort fallback โ€” specifically for the scenario where our own Cloud API binding to the dock is unavailable โ€” not as a primary avoidance mechanism. The DRC channel through our own self-hosted Cloud API server remains the clean, documented, machine-callable path and should stay the default design.

13.8 โ€” FlightHub 2 vs. DJI Cloud API: which layer is which

Worth stating plainly since it's easy to conflate: DJI Cloud API is the protocol layer โ€” the MQTT topics, methods, and schemas (fly_to_point, flighttask_resume, the DRC channel) that any third-party developer implements against to build their own cloud service talking directly to a DJI Dock. DJI FlightHub 2 is DJI's own first-party application built on that same dock-to-cloud layer โ€” their reference fleet-management product. FLYsafe is not building on top of FlightHub 2 as a platform; it implements the same Cloud API that FlightHub 2 implements, connecting directly to the dock. The two are alternate implementations of the same interface, not a required stack โ€” and per ยง13.5, they cannot both be bound to the same dock at once.

13.9 โ€” Can DJI FlightHub 2 itself be extended with a plugin/SDK to host our DAA logic?

No โ€” checked directly, and this closes off the idea cleanly. FlightHub 2 has no plugin, app-store, or SDK-extension model that allows third-party code to run inside it. Every DJI source describes its OpenAPI the same way: "designed to enable developers to interact with third-party cloud platforms and DJI FlightHub 2" โ€” the integration direction is FlightHub 2 exposing data and accepting configuration out to external systems, not external code being hosted inside FlightHub 2's own runtime. There is no marketplace or "install this module" pattern anywhere in DJI's developer documentation, for DAA or anything else.

The relationship can only ever be API-to-API This is the same limitation any third-party platform has with FlightHub 2 โ€” DroneSense included. We can call FlightHub 2's OpenAPI or FlightHub Sync from the outside, or FlightHub 2 can call/forward to us, but we cannot author code that runs inside FlightHub 2 itself. Combined with ยง13.5โ€“ยง13.8, this leaves exactly two real places DJI avoidance logic can live: our own self-hosted Cloud API server (functionally FLYsafe's own equivalent of FlightHub 2, holding the dock's cloud binding and DRC authority directly), or the Tier-3 Chrome-extension fallback bolted onto FlightHub 2's Virtual Cockpit from the outside (ยง13.7). There is no third, "extend FlightHub 2 internally" path.

13.10 โ€” Does an On-Premises FlightHub 2 deployment change the control-authority answer?

No โ€” On-Premises is a hosting/data-residency choice, not a different control architecture. It's the same DJI product: DJI is explicit that FlightHub 2 On-Premises delivers the same capabilities as the public cloud version, deployable on private cloud, physical servers, or DJI's pre-packaged AIO appliance instead of DJI-managed AWS, while continuing to work toward full feature parity between the two editions.

What changes vs. what doesn't Changes: where the FlightHub 2 server physically lives and who touches the data โ€” telemetry, imagery, and flight logs stay on infrastructure we control, up to fully air-gapped if required. Does not change: it is still the FlightHub 2 application โ€” DJI's product, not ours โ€” holding the dock's exclusive cloud binding (ยง13.5), still exposing the same OpenAPI oriented at external systems interacting with FlightHub 2 rather than code hosted inside it (ยง13.6/ยง13.9), and still with no documented DRC-style live flight-command endpoint on that OpenAPI. The "custom development" language attached to On-Premises refers to the same OpenAPI plus embeddable frontend components (Flight Routes Editor, Virtual Cockpit, Project/Map can be embedded into another app shell) โ€” not a new machine-callable control surface.

Conclusion: On-Premises may be worth adopting on its own data-sovereignty merits, but it doesn't create a path for the Runner to inject avoidance maneuvers into FlightHub 2. Whether public cloud or On-Premises, FlightHub 2 owning the dock's binding excludes our own Cloud API server from holding DRC authority at the same time (ยง13.5) โ€” the hosting model doesn't change which side owns control.