Visual reference for the MQTT broker architecture, topic namespace design, and per-category topic inventory. Read alongside the ClickUp wiki page FLYsafe_MQTT_Topic_Strategy.md.
Three broker types. Each has a distinct role and a distinct set of consumers.
Publishes surveillance feeds and weather. Topics are pushed to every tenant broker on arrival. No aircraft telemetry lives here.
All aircraft telemetry, external ingest, internal coordination, and the shared feeds re-published from the platform broker. The web app subscribes here only.
Raw ingest, Flink pipeline topics, commands, dead-letter queues. Never consumed by the web app or the MQTT broker directly.
Every topic on the tenant broker follows this pattern. Each segment has a specific purpose.
v2/… in parallel. Consumers migrate on their own schedule. Never increment without a notice period.v1/+/aircraft/… to subscribe across tenants.aircraft (own per-drone telemetry), shared (tenant-wide feeds), internal (microservice coordination — never web app), or partner (cross-tenant partnership streams). Partner topics omit the {tenant} segment entirely — the broker is already scoped to one tenant, so it is implicit in the connection. The literal keyword partner occupies segment 2, providing immediate visual disambiguation between own-drone and partner feeds without redundant path segments, followed by {partnerTenant} to identify the data source: v1/partner/sait/aircraft/SAI-003/drones/telemetry.+ here subscribes to all drones: v1/airmarket/aircraft/+/drones/telemetry.drones · oi · surveillance · infrastructure · rtm-services · inventory · internaltelemetry · networkid · c2links · events · tisb · declaration · osdEvery topic is one of three categories. The category determines who produces it, who consumes it, and whether the web app ever sees it.
v1/{tenant}/internal/#.These topics originate on the shared broker and are re-published to each tenant broker under v1/{tenant}/shared/…
| Topic (on shared broker) | Re-published as (on tenant broker) | Rate | Description |
|---|---|---|---|
| surveillance_traffic/live_mlat | v1/{t}/shared/surveillance_traffic/live_mlat | 1–5 Hz | MLAT-fused aircraft tracks. 5 Jetvision G-1090 stations — 2 clusters: CYEG area + Fort Saskatchewan, 37.2 km baseline. |
| surveillance_traffic/live_community | surveillance_traffic/live_community | 1–5 Hz | Community ADS-B — 989 global receivers. dump1090/readsb JSON format. Dedup by ICAO hex across feeds. |
| surveillance_traffic/live_regional_{region} | surveillance_traffic/live_regional_{region} | 1–5 Hz | Regional feeds: EDM (23 receivers), CAL (55 receivers), FMM (provisioned, empty on staging). |
| surveillance_traffic/live_ads | surveillance_traffic/live_ads | 1–5 Hz | Aggregated ADS-B feed. Slightly different schema — no r/t fields, extended emergency string. |
| surveillance_traffic/live_vision | surveillance_traffic/live_vision | On detect | GACM vision sensor aircraft detections. Includes emitter_device_id. Subset of ADS-B fields. |
| Topic | Rate | Description |
|---|---|---|
| surveillance_sensors/live_mlat | 30–60 s | 5/5 online. G-1090 + G-1090 UAT hardware. Note: no per-device connected or uptime fields — station health inferred from envelope online_receivers. |
| surveillance_sensors/live_vision | 30–60 s | 2/2 online. GACM-0100-000086 + 000114. Includes altitude_mm, rtt_ms, uptime per device. altitude_mm ÷ 1000 = metres. |
| surveillance_sensors/live_community | 30–60 s | 989/989 global receivers. Unique schema: has distance_miles_edm, distance_miles_fmm, distance_miles_cal per receiver — pre-computed from all three Alberta cities. |
| surveillance_sensors/live_regional_{region} | 30–60 s | EDM (23 receivers), CAL (55 receivers). Single distance_miles field from regional centre. |
| Topic | Rate | Description |
|---|---|---|
| weather/ | Varies | All weather subtopics. Full subtopic structure TBD — see Open Item OI-02. Feeds Drones — Weather tab. |
Published by FlightBinder (Flink) after normalisation. All values are canonical — correct units, decoded field names, no raw integers or sentinel values.
| Topic | Rate | App context | Key fields |
|---|---|---|---|
| drones/telemetry | 10 Hz | Drones | lat, lng, heading, agl/aglM, asl/aslM, atl/atlM, flightMode, armed, battery, groundSpeed, vertSpeed, hAcc, vAcc, utmState |
| drones/networkid | ~1 Hz | Drones | heading (degrees direct), alt_agl (m direct), pilot (full_name), callsign, missionName, missionLocation, waypoints[], trackOrigin |
| drones/c2links | 1 Hz | Drones — C2 tab | per-link: label, tech, status, latency, rssi, rfScore, isLead, uplink, downlink; mobile station: lat, lng, vehicleCallsign |
| drones/events | On event | Drones — Events | timestamp, category (Comms/Mission/DAA/Power/OI/System), severity (ok/warn/crit), message |
| drones/tisb | On change | Drones — Surv tab | cifib_status (connected/disconnected), last_update |
| oi/declaration | On change | OI's | oiPolygon[] (Leaflet format), conformance, status, maxAgl, start/end, subscribers[], utmProvider, declarationId |
| Topic | Rate | App context | Key fields |
|---|---|---|---|
| surveillance_traffic/live_ads surveillance_traffic/live_mlat surveillance_traffic/live_community surveillance_traffic/live_vision surveillance_traffic/live_regional_edmonton surveillance_traffic/live_regional_calgary surveillance_traffic/live_regional_fort_mcmurray | 1–5 Hz | Surveillance | hex, callsign (trimmed), lat, lon, alt_baro (ft), gs (kt), track, baro_rate (ft/min), isMlat (bool), isTisb, trust, rssi (dBm), seenSec |
| surveillance_sensors/live_mlat surveillance_sensors/live_vision surveillance_sensors/live_community surveillance_sensors/live_regional_edmonton surveillance_sensors/live_regional_calgary surveillance_sensors/live_regional_fort_mcmurray | 30–60 s | Surveillance | online_receivers, expected_receivers, devices[].id, devices[].lat, devices[].lon, devices[].rtt_ms, devices[].uptime |
| weather/ | Varies | Drones — Wx | conditions, tempC, windSpeedKt, windDir, gustKt, ceilingFt, metar, forecast[] |
| infrastructure/sites | 30 s | Infrastructure | site.id, site.status, site.lat/lng, backhaul, latency, uptime, component.status, component.model |
| rtm-services/platform | 5 s | RTM Services | service.name, service.status, kafkaLag, flinkJobs[], cpuPct, memPct, errorRate, processingRate |
| inventory/assets | On change | Inventory | droneId, model, serial, uasType, deploymentType, ec.adsbOut, ec.adsbIn, dockId, assignedPilot |
v1/airmarket/aircraft/+/drones/telemetry. All partner drones (any active partner, all subtopics): v1/partner/+/aircraft/+/#. All own-tenant contexts: v1/airmarket/#. Note: v1/airmarket/# does not capture partner topics by design — partner data lives under the separate v1/partner/ root, ensuring clean ACL separation between own-fleet and cross-tenant feeds.{tenant} in the partner path? Each EMQX broker instance is dedicated to a single tenant — the tenant is already implicit in the broker connection endpoint. Including {tenant} in the partner path would be redundant and forces the frontend to parse the second segment just to determine whether a message is own-fleet or partner data.partner as segment 2, in place of {tenant}. This gives the frontend an immediate, unambiguous signal at the topic level: a message on v1/tenant/… belongs to the operator's own fleet; a message on v1/partner/… comes from an approved cross-tenant feed. The keywords themselves carry the semantic distinction — no payload parsing or topic matching logic is needed to route messages to the correct map layer.{partnerTenant} segment identifies the source of each feed. A single wildcard subscription v1/partner/+/aircraft/+/# covers all active partners — new partnerships appear and disappear automatically without any frontend reconfiguration when the Partnership Controller creates or deletes a KafkaConnector.
| Topic | Rate | App context | Key fields |
|---|---|---|---|
| drones/telemetry | 10 Hz | Partner Drones | Identical schema to own-drone drones/telemetry. Carries an additional partner_tenant field injected by the KafkaConnector transform. Web app renders with distinct visual treatment — grey icon, "Partner: {B}" label. Provides live position, heading, altitude (AGL/ASL), flight mode, armed state, battery, and ground speed. Wildcard for all active partners: v1/partner/+/aircraft/+/drones/telemetry. |
| drones/networkid | ~1 Hz | Partner Drones | Identical schema to own-drone drones/networkid. Provides the receiving tenant with operator context for the partner drone: pilot full name, callsign, mission name, mission location, and active waypoints. Enables the frontend to identify which partner operator and mission plan are associated with a drone currently on the map. |
| oi/declaration | On change | Partner OIs | Identical schema to own-drone oi/declaration. Streams the partner drone's active Operational Intent: OI polygon (Leaflet format), conformance status (CONFORMING / NON_CONFORMING), max AGL, declared start and end times, subscribers, UTM provider, and declaration ID. Allows the receiving tenant to visualise the partner's OI boundary alongside the drone position and monitor conformance in real time. |
partner-{B}-to-{A}-mqtt-sink KafkaConnector in the shared-kafka namespace, reading from {B}.telemetry.enriched. The connector routes per-serial — the Kafka message key (aircraft serial) builds the MQTT topic path. Partnership must be approved via the Partnership Controller API before messages flow: creating the connector starts streaming; deleting it stops it immediately. See Cross-Tenant Partnership Architecture for the full request → approve → suspend → resume lifecycle.v1/partner/+/aircraft/+/# — publish is denied. The v1/partner/# namespace is never produced by the tenant's own Flink jobs; it is exclusively written by KafkaConnectors in shared-kafka, enforcing a clear boundary between own-fleet output and inbound partner streams.
Third-party data arriving in native formats. Flink adapter jobs normalise and republish. The web app never subscribes to these topics.
| Topic (root) | Rate | Kafka output | Notes |
|---|---|---|---|
| thing/product/{sn}/osd | 1–5 Hz | {tenant}.telemetry.raw.dji | OSD = On-Screen Display. host key = dock/GCS. sub key = aircraft (airborne only). |
| thing/product/{sn}/# | Various | {tenant}.telemetry.raw.dji | All DJI message types via one wildcard subscription. |
dronesense_dock_status before attributing OSD data to a dronedock.* and c2.* canonical fields.null. Never pass 65535 to the web app — it is not a valid signal quality value.null in rth.homePt.1 = SDR primary link. 0 = 4G primary link. Map to c2.workmode string.Microservice coordination signals. Current topics use flat unversioned names. Migration to v1/{tenant}/internal/… namespace is recommended.
| Current topic | Proposed topic | Consumer | Purpose |
|---|---|---|---|
| dronesense_dock_status/{dock_sn} | v1/{t}/internal/dock-status/{sn} | DJI Bridge adapter | Dock↔drone serial pairing registry. Required lookup before DJI OSD can be attributed to the correct aircraft. dock_sn ≠ drone_sn. |
| asset_tracking/ops_asset/{asset_id} | v1/{t}/internal/asset-tracking/ops/{id} | Asset tracker service + infra map | Operational asset positions (trucks, docks, vehicles). Dock variant (subcategory=Dock) includes on-site weather: temperature, humidity, wind_speed, rainfall — feeds wx.groundSensors[]. |
| asset_tracking/c2_monitoring/{station_id} | v1/{t}/internal/asset-tracking/c2/{id} | C2 Monitor service | Mobile C2 station position. GRS station is vehicle-mounted on chase truck — horizontal_speed > 0 when moving. Feeds C2 tab station geolocation. Must update dynamically. |
asset_tracking/ops_asset/{dock_id} payload with subcategory=Dock carries info.temperature, info.humidity, info.wind_speed, info.environment_temperature, info.rainfall. This can populate the Drones weather tab ground sensors for sites with a dock — no separate weather API needed.info.drone_in_dock == 1 confirms drone is physically docked. info.drone_charge_state.capacity_percent gives battery while docked. info.sub_device.device_online_status gives aircraft agent connectivity.info.position_state.rtk_number (29 sats), is_fixed == 2 (RTK fixed), quality == 5 (best). This is the dock's GPS quality, not the aircraft's.Used by Flink jobs and microservices only. Never exposed to the MQTT broker or the web app.
Kafka uses dots as separators (not slashes). The tenant prefix scopes all topics to the organisation.
Partition key is aircraft_serial within each topic — no per-serial topic splitting.
| Kafka Topic | Purpose | Flink consumer |
|---|---|---|
| {tenant}.telemetry.raw | Raw MAVLink from RTTP agents (ArduPilot / PX4) | FlightBinder |
| {tenant}.telemetry.raw.dji | Raw DJI Cloud API telemetry — separate ingest path from RTTP | DJI Bridge adapter |
| {tenant}.telemetry.enriched | FlightBinder canonical output — MQTT Bridge publishes this to the web app | MQTT Bridge |
| {tenant}.networkid | Raw networkid messages from aircraft agent | NetworkID processor |
| {tenant}.networkid.dd | Deduplicated / processed networkid — FlightBinder reads .dd not .raw | FlightBinder |
| Kafka Topic | Purpose | Flink consumer |
|---|---|---|
| {tenant}.flightplan | Flight plan / OI declaration data | OI Conformance Tracker |
| {tenant}.utm | UTM / DSS state messages — OI conformance changes | OI Conformance Tracker |
{tenant}.events.* topics.| Kafka Topic | Purpose | Alert Router output |
|---|---|---|
| {tenant}.events.c2 | C2 link state change events | user alerts if primary/all links lost |
| {tenant}.events.containment | Containment / geofence breach events | WARNING → ops team |
| {tenant}.events.daa | DAA conflict / breach events | CRITICAL → ops team |
| {tenant}.events.operator | Operator-facing alert events | → user/{id}/alerts directly |
| {tenant}.events.rtl | RTL (Return to Launch) triggered events | WARNING → ops team |
| {tenant}.events.utm | UTM state change events (OI conformance) | WARNING if non-conforming |
| Kafka Topic | Purpose | Consumer |
|---|---|---|
| {tenant}.dronesense | DroneSense dock status / pairing data | DJI Bridge adapter |
| {tenant}.asset.tracking.ops.asset.dock | Asset tracking — dock position + on-site weather telemetry | Asset tracker service |
| {tenant}.mqtt_bridge | MQTT bridge relay output | MQTT Bridge |
| {tenant}.mqtt_sink_dlq | Dead-letter queue for MQTT sink failures | Ops / retry service |
{serial} for per-aircraft topics — all messages for one drone processed in order by the same Flink instance. {tenant_id} for shared topics. Retention: raw ingest 7 d · enriched 24 h · events 30 d · DLQ 14 d.A dedicated per-drone topic streams aircraft within the DAA evaluation rings to both the web app and the aircraft agent. Critical path for DAA services — each consumer gets the same message, no topic duplication.
| Field | Type | Description |
|---|---|---|
| ts | int ms | Evaluation timestamp (Unix ms) |
| droneId | string | Drone serial — self-describing alongside topic path |
| evalRadius / swcRadius / nmacRadius | float m | Ring radii in metres — tenant/drone-configurable, not hardcoded |
| evalCount / swcCount / nmacCount | int | Target count per ring — for UI badge display |
| targets[].hex | string | ICAO hex — primary target ID |
| targets[].callsign | string | Trimmed ATC callsign |
| targets[].lat / .lng | float deg | Target position |
| targets[].alt_baro / .alt_geom | float ft | Barometric and geometric altitude |
| targets[].gs / .track / .baro_rate | kt / deg / ft/min | Speed, heading, vertical rate |
| targets[].distM / .distNm | float | Distance from drone — primary DAA field |
| targets[].bearing | float deg | Bearing from drone to target (true north) |
| targets[].closureRate | float m/s | Closure rate — positive = approaching |
| targets[].ring | enum | EVAL / SWC / NMAC |
| targets[].isMlat / .isTisb / .trust / .feedSource | mixed | Source provenance and trust level |
Events and alerts serve different audiences and travel different pipelines. A single event may trigger zero, one, or many alerts depending on configurable routing rules.
| Event | Alert? | Severity | Notify |
|---|---|---|---|
| DAA | |||
| utmState == EMERGENCY | YES | CRITICAL | All users |
| DAA NMAC breach | YES | CRITICAL | Ops team |
| DAA SWC breach | YES | WARNING | Ops team |
| Target enters eval ring | no | — | Event only — no alert |
| C2 links | |||
| All C2 links lost | YES | CRITICAL | Ops + pilot |
| Primary link lost | YES | WARNING | Ops team |
| Link restored | no | — | Event only |
| Power | |||
| Battery < 15% | YES | WARNING | Ops team |
| Battery < 5% | YES | CRITICAL | All users |
| Battery at 30% | no | — | Event only — logged |
| OI / Mission | |||
| OI non-conforming | YES | WARNING | Ops team |
| Mission complete | YES | INFO | Ops team |
| Flight mode change | no | — | Event only — logged |
| Topic | Purpose | Who subscribes |
|---|---|---|
| v1/{tenant}/aircraft/{serial}/alerts | All alerts for this aircraft — DAA, C2, battery, OI, mission | Web app (per monitored drone) · Aircraft agent (own serial only) |
| v1/{tenant}/aircraft/{serial}/alerts/{severity} | Filtered by severity: critical / warning / info | Selective subscribers — automated response systems, AI agent |
| v1/{tenant}/shared/alerts/broadcast | Tenant-wide system alerts not tied to a single aircraft (platform outage, service degradation) | Web app — always subscribed regardless of which drone is in focus |
v1/airmarket/aircraft/+/alerts — single subscription covers all aircraft. Client filters by serial to route each alert to the correct drone panel.
| Field | Type | Description |
|---|---|---|
| ts | int ms | Unix timestamp (ms) when event occurred |
| droneId | string | Aircraft serial number |
| tenantId | string | Tenant identifier |
| category | enum | Comms · Mission · DAA · Power · OI · System |
| severity | enum | ok · warn · crit |
| eventType | string | Machine-readable identifier (snake_case) |
| message | string | Human-readable event description |
| data | object | Category-specific detail fields — see examples below |
| Field | Type | Description |
|---|---|---|
| ts | int ms | Unix timestamp (ms) when alert was issued |
| alertId | string | Unique alert identifier — prefixed ID or UUID |
| droneId | string|null | Aircraft serial — null for broadcast alerts |
| tenantId | string | Tenant identifier |
| severity | enum | critical · warning · info |
| category | enum | DAA · C2 · Power · OI · System |
| title | string | Short display title for UI notification |
| message | string | Full alert message for display |
| sourceEvent | string | eventType that triggered this alert |
| requiresAck | bool | Whether user acknowledgement is required |
| ackedAt | int ms|null | Acknowledgement timestamp — null if pending |
| ackedBy | string|null | User ID who acknowledged — null if pending |
Ready-to-publish payloads for each event and alert category. Use these in MQTT Explorer, a test harness, or the EMQX dashboard to simulate flight conditions during development.
SIMTL70 · Tenant: airmarket · Topic prefix: v1/airmarket/aircraft/SIMTL70/ — replace ts values with the current Unix epoch in milliseconds when publishing.v1/airmarket/aircraft/SIMTL70/drones/events{
"ts": 1718467200000,
"droneId": "SIMTL70",
"tenantId": "airmarket",
"category": "Comms",
"severity": "warn",
"eventType": "link_primary_lost",
"message": "Primary C2 link lost — SDR offline",
"data": {
"linkLabel": "SDR",
"tech": "SDR",
"previousStatus": "active",
"currentStatus": "offline",
"backupActive": true,
"backupTech": "4G"
}
}
{
"ts": 1718467320000,
"droneId": "SIMTL70",
"tenantId": "airmarket",
"category": "DAA",
"severity": "crit",
"eventType": "daa_nmac_breach",
"message": "NMAC breach — C172 within 152m",
"data": {
"targetHex": "C00F12",
"targetCallsign": "CNC172",
"distM": 152.4,
"closureRate": 14.7,
"ring": "NMAC",
"targetAltFt": 2340,
"droneAglM": 91.5
}
}
{
"ts": 1718467440000,
"droneId": "SIMTL70",
"tenantId": "airmarket",
"category": "Power",
"severity": "warn",
"eventType": "battery_low",
"message": "Battery at 13% — return recommended",
"data": {
"batteryPct": 13,
"threshold": 15,
"estimatedFlightTimeSec": 210,
"voltageV": 21.6,
"currentA": 18.3
}
}
{
"ts": 1718467560000,
"droneId": "SIMTL70",
"tenantId": "airmarket",
"category": "OI",
"severity": "warn",
"eventType": "oi_non_conforming",
"message": "Aircraft outside OI boundary",
"data": {
"declarationId": "oi-2024-06-15-001",
"conformance": "NON_CONFORMING",
"deviationM": 47.2,
"maxAglM": 120,
"currentAglM": 134.5,
"missionName": "Pipeline Survey North"
}
}
{
"ts": 1718468100000,
"droneId": "SIMTL70",
"tenantId": "airmarket",
"category": "Mission",
"severity": "ok",
"eventType": "mission_complete",
"message": "Mission completed successfully",
"data": {
"missionName": "Pipeline Survey North",
"missionId": "msn-2024-06-15-003",
"durationSec": 1740,
"waypointsTotal": 12,
"waypointsCompleted": 12,
"pilotName": "Jordan Ellis"
}
}
{
"ts": 1718467680000,
"droneId": "SIMTL70",
"tenantId": "airmarket",
"category": "System",
"severity": "ok",
"eventType": "flight_mode_change",
"message": "Flight mode changed to AUTO",
"data": {
"previousMode": "LOITER",
"currentMode": "AUTO",
"armed": true,
"flightMode": "AUTO"
}
}
v1/airmarket/aircraft/SIMTL70/alerts{
"ts": 1718467320000,
"alertId": "alrt_daa_7f3a9c",
"droneId": "SIMTL70",
"tenantId": "airmarket",
"severity": "critical",
"category": "DAA",
"title": "NMAC Breach",
"message": "C172 (C00F12) within 152m — immediate action required",
"sourceEvent": "daa_nmac_breach",
"requiresAck": true,
"ackedAt": null,
"ackedBy": null
}
{
"ts": 1718467200000,
"alertId": "alrt_c2_2b8f1e",
"droneId": "SIMTL70",
"tenantId": "airmarket",
"severity": "critical",
"category": "C2",
"title": "All C2 Links Lost",
"message": "All C2 links offline — aircraft in failsafe",
"sourceEvent": "link_all_lost",
"requiresAck": true,
"ackedAt": null,
"ackedBy": null
}
{
"ts": 1718467440000,
"alertId": "alrt_pwr_9d4c5a",
"droneId": "SIMTL70",
"tenantId": "airmarket",
"severity": "warning",
"category": "Power",
"title": "Low Battery",
"message": "Battery at 13% — return to home recommended",
"sourceEvent": "battery_low",
"requiresAck": false,
"ackedAt": null,
"ackedBy": null
}
{
"ts": 1718467800000,
"alertId": "alrt_sys_3e2a1f",
"droneId": null,
"tenantId": "airmarket",
"severity": "warning",
"category": "System",
"title": "Platform Degraded",
"message": "FlightBinder latency elevated — telemetry may be delayed",
"sourceEvent": "flink_job_lag_high",
"requiresAck": false,
"ackedAt": null,
"ackedBy": null
}
v1/airmarket/aircraft/SIMTL70/drones/events to verify the Events tab renders. Publish alert payloads to v1/airmarket/aircraft/SIMTL70/alerts to verify the alert panel. The Alert Router normally produces alerts automatically from events — in manual testing, publish both independently. Use MQTT Explorer or: mosquitto_pub -h emqx-mqtt.airmarket.stage.flysafe.live -t "v1/airmarket/aircraft/SIMTL70/alerts" -m '{...}'The current FLYsafe RTM app subscribes to a set of legacy flat topics with no tenant prefix. The strategic design introduces a structured namespace (v1/{tenant}/…), a separate cross-tenant partnership stream (v1/partner/…), and several net-new topic types. This section defines the full porting path for frontend developers and the parallel-publish strategy that makes the migration zero-downtime.
v1/{tenant}/aircraft/{serial}/…. The tenant ID is explicit in the path — it removes ambiguity in ACL rules and future-proofs cross-tenant federation without changing the per-tenant broker model.networkid/{serial} becomes v1/{tenant}/aircraft/{serial}/drones/networkid. The context segment (drones/, oi/) maps 1-to-1 with the RTM UI navigation context — no per-serial subscription needed; one wildcard covers all drones.v1/partner/{partnerTenant}/aircraft/{serial}/… root carries approved cross-tenant drone feeds. It is a separate namespace from v1/{tenant}/… — purely additive, no legacy migration required. One frontend wildcard (v1/partner/+/aircraft/+/#) subscribes to all active partners at once.| Legacy topic (v1 app) | Strategic topic (v2 app) | Priority | Notes |
|---|---|---|---|
| Own-drone telemetry — v1/{tenant}/aircraft/{serial}/… | |||
| networkid/{serial} | …/drones/networkid | P1 | Primary normalised source — heading, AGL, pilot name, callsign, mission name, waypoints. Most critical topic to port. Wildcard: v1/{tenant}/aircraft/+/drones/networkid. |
| flightplan/{serial} | …/oi/declaration | P1 | OI polygon (Leaflet format), conformance state (CONFORMING / NON_CONFORMING), max AGL, start/end times, declarationId. Renamed from flightplan to reflect the operational intent concept. Wildcard: v1/{tenant}/aircraft/+/oi/declaration. |
| TISB Stream/{serial} | …/drones/tisb | P2 | TIS-B CIFIB connection status. On-change only. Normalised topic name; payload schema unchanged. |
| Shared feeds — v1/{tenant}/shared/… | |||
| surveillance_traffic/{feed} | …/shared/surveillance_traffic/{feed} | P2 | Payload and feed names (live_ads, live_mlat, live_community, live_regional_*) are unchanged. Subscription path update only — add the v1/{tenant}/shared/ prefix. Middleware Surveillance Fusion Job publishes to both old and new paths during the transition window. |
| External ingest — unchanged at broker root (middleware-internal, not subscribed by web app) | |||
| thing/product/{sn}/osd | v1/{tenant}/aircraft/{sn}/drones/telemetry (produced by DJI Bridge adapter) |
P3 | DJI publishes to the root-level thing/product/ path — this does not change. The DJI Bridge adapter normalises OSD payloads into the canonical drones/telemetry schema. The web app never subscribes to thing/product/… directly — it always consumed normalised output. |
| Partnership streams — v1/partner/{partnerTenant}/aircraft/{serial}/… — net new, no legacy equivalent | |||
| — no legacy topic — | v1/partner/{B}/aircraft/{sn}/drones/telemetry | NEW | Partner drone live position, heading, altitude, flight mode, battery. Produced by a KafkaConnector in shared-kafka on partnership approval. Wildcard for all active partners: v1/partner/+/aircraft/+/drones/telemetry. |
| — no legacy topic — | v1/partner/{B}/aircraft/{sn}/drones/networkid | NEW | Partner drone mission context — pilot name, callsign, mission name, waypoints. Allows the receiving tenant to identify who is flying and under what mission plan for each partner drone on the map. |
| — no legacy topic — | v1/partner/{B}/aircraft/{sn}/oi/declaration | NEW | Partner drone's active Operational Intent — OI polygon, conformance status, max AGL, start/end times, declarationId. Renders the partner's OI boundary alongside their drone position and monitors conformance in real time. |
| Topic | Context | What it enables |
|---|---|---|
| v1/{t}/aircraft/{sn}/drones/telemetry | Drones | Canonical 10 Hz drone position direct from FlightBinder — lat, lng, heading, AGL, ASL, flight mode, armed state, battery, ground speed. Primary own-fleet map feed. |
| v1/{t}/aircraft/{sn}/drones/c2links | Drones — C2 tab | Per-link C2 telemetry — SDR/4G label, latency, RSSI, RF score, primary/backup designation, mobile station lat/lng. Powers the C2 tab and chase-truck map pin. |
| v1/{t}/aircraft/{sn}/drones/events | Drones — Events | Typed event stream (Comms / Mission / DAA / Power / OI / System). Powers the Events tab; feeds the Alert Router for downstream alerting. |
| v1/{t}/aircraft/{sn}/drones/proximate_aircraft | Drones — DAA | DAA proximity rings — all air traffic within EVAL / SWC / NMAC radii, with distance, bearing, and closure rate per target. Powers the DAA map overlay. |
| v1/{t}/aircraft/{sn}/alerts | Alerts | Per-drone alert topic. Frontend subscribes to all active drones via wildcard: v1/{t}/aircraft/+/alerts. Routes by serial to the correct drone panel. |
| v1/{t}/shared/alerts/broadcast | Alerts | Platform-wide broadcast alerts not tied to one aircraft — service degradation, planned maintenance. Always subscribed regardless of which drone is in focus. |
| v1/partner/+/aircraft/+/# | Partner | All cross-tenant partnership streams. A single wildcard covers all active partners — drone positions, mission context, and OI boundaries start flowing the moment a partnership is approved, with no frontend reconfiguration. |
v1/partner/…, a completely separate root from v1/{tenant}/…. There is no legacy topic to cut over from. To enable partnership support the frontend adds three wildcard subscriptions: v1/partner/+/aircraft/+/drones/telemetry, …/drones/networkid, and …/oi/declaration. No existing subscription changes. Partner data begins flowing the moment the Partnership Controller approves a request and creates the KafkaConnector — the wildcard picks it up automatically.{tenant} with your tenant ID (e.g. airmarket).v1/{tenant}/aircraft/+/drones/networkid — single wildcard covers all drones, no per-serial subscription neededv1/{tenant}/aircraft/+/oi/declaration — renamed to reflect the operational intent concept; schema extended with conformance statev1/{tenant}/shared/surveillance_traffic/{feed} — payload and feed names unchanged; prefix onlyv1/{tenant}/aircraft/+/drones/tisb — normalised name; same on-change behaviourdroneId fieldpartner_tenant payload fieldv1/{tenant}/… path. No existing consumer breaks. Partnership topics under v1/partner/… begin publishing immediately when the first KafkaConnector is created — they have no legacy path to maintain.v1/{tenant}/… own-drone topics and adds v1/partner/… partnership subscriptions. Both app versions run in parallel — v1 on legacy paths, v2 on the strategic structure — without interfering with each other.v1/partner/… topics are a live production capability — they are never decommissioned.Unresolved items and confirmed resolutions.
| ID | Item / Resolution | Status | Owner |
|---|---|---|---|
| OI-01 | surveillance_traffic/ structure NOT changing. Parent topic with sub-topics per service. live_ads has reduced field set vs live_community — missing r, t, mlat[], rssi. Fusion Job handles both schemas. | ✅ Resolved | Middleware |
| OI-02 | Topic is weather/. Subtopic structure and payload TBD — to be provided by weather service team. | ⏳ Partial | Weather team |
| OI-03 | dronesense_dock_status produced by FLYsafe microservices AFTER OSD messages arrive. DJI Bridge must queue OSD until pairing available — do not drop. | ✅ Resolved | DroneSense |
| OI-04 | Asset IDs are human-labelled in microservice config. Stable per deployment. | ✅ Resolved | Asset tracker |
| OI-05 | GRS = Ground Radio Station. Stable per vehicle per deployment. | ✅ Resolved | C2 team |
| OI-06 | Internal topic migration to v1/{tenant}/internal/ namespace. Parallel publish approach confirmed. Timeline TBD. | ⏳ In progress | Middleware |
| OI-07 | DLQ monitoring — developer monitors during development. AI agent monitoring under consideration for production. | ⏳ In progress | Middleware / Ops |
| OI-08 | Partnership connector source topic: prefer {partnerTenant}.telemetry.enriched over .raw — enriched is already canonical RTTP schema, web app receives correct units and decoded fields with no additional transform. Confirm Tenant B's enriched topic is published into the shared-kafka namespace before the connector is built. | ⏳ Open | Tenant B middleware |