Purpose. This page exists so any developer can answer “what table is this field in, and why does it exist” without reading four separate documents. It is built from two sources: the design (Aircraft_Custodian_dTwin_Strategy.html v1.13, and the deployed rtm-tenant-schema.sql / aadms-schema.sql) and the current build (fsl-rtm-map-mu.html, the RTM Map mockup).
Why it also lists gaps. The design and the mockup were built at different times and now model some things differently — not just missing fields, but incompatible data models in a couple of places. §4 lists every place they disagree so we can decide, as a team, which one is right before more work gets built on top of either.
① Database Landscape
Two physically separate database contexts are involved. Every drone-facing table/view lives in a tenant's own database; AADMS is a single central database that reads across all tenants.
Database / Instance
Schema
Object
Type
Purpose
rtm-app-db one per tenant — airmarket, sait, sandbox
drones
dlb_drones
Table
Raw import from the FLYsafe.software DLB API. Written only by the Import process.
drones
v_dlb_drones
View
Renames the two display identifiers on dlb_drones for operator-facing reporting.
drones
drones
Table
The Custodian dTwin record — RTM configuration, OI/automation settings, conspicuity detail, and live telemetry. The core operational table.
drones
daa_rules
Table
One row per Detect-and-Avoid rule per drone. New in v1.13 — backs the mockup's daa_rules[] panel.
drones
v_dtwin
View
Joins drones ⋈ dlb_drones. The one view every consumer (Custodian API, frontend, MCP) should read from.
aadms_db one central instance
aadms
drones
Materialized View
Cross-tenant callsign → (tenant_id, serial_number) lookup for ADS-B routing. Built with postgres_fdw over every tenant's drones.drones.
Keycloak external IdP, one realm per tenant
—
drone-as-user identity
External
Auth-only identity for the drone. ID is <tenant>-<serial> — computed, not a stored column or FK.
flights_binding location/owner not detailed in this doc
—
flights_binding
External / not detailed here
Stores the Flight Volume / Contingency Volume / Submitted Volume geometry for current and upcoming flights (v1.13). Replaces the never-built drones.oi_geometry. Its own column-level schema isn't covered by this doc — see its owning reference.
② Entity Relationship Diagram
③ Table Structure Reference
Every column that is actually stored, grouped the same way the schema itself groups them. Views are described separately below — they don't store anything, so their columns aren't repeated.
drones.dlb_drones — raw DLB import (table)
Written only by the Import process. Column names match the DLB API verbatim — never renamed, never written by RTM.
Column
Type
Why it's there
guid
TEXT NOT NULL (PK)
DLB's drone UUID. The join key every other table/view uses to reach DLB identity.
serial_number
TEXT NOT NULL
OEM serial. Part of the natural key used to link to drones.drones.
name
TEXT
Drone display name from DLB.
brand, model, drone_type, color
TEXT
Hardware identity, straight from DLB.
status
TEXT
DLB airworthiness status (e.g. Airworthy, Grounded).
identification_number
TEXT
TC registration. Exposed as TC_Registration in v_dtwin.
inventory_number
TEXT
Operator callsign. Exposed as Callsign in v_dtwin — the ADS-B routing key.
When this row was last upserted by the Import process.
drones.drones — Custodian dTwin state (table)
PK (tenant_id, serial_number). Three write owners: the Import process (identity, first insert only), the Custodian APIs (config, from the RTM Map UI or MCP agents), and the GCS Plugin path (live telemetry, via the Custodian).
Core identity
Column
Type
Why it's there
tenant_id
TEXT NOT NULL
Which tenant owns this row. Half of the primary key.
serial_number
TEXT NOT NULL
Other half of the primary key. Matches dlb_drones.serial_number.
dlb_guid
TEXT NOT NULL (FK, UQ)
Links 1:1 back to dlb_drones.guid for all DLB-sourced identity.
Custodian / adapter configuration
adapter_type
TEXT NOT NULL
Which GCS adapter the Custodian talks to: mavlink | dji_fh2 | dronesense. Set via Custodian API.
adapter_config
JSONB NOT NULL
Adapter-specific connection settings (host/port for MAVLink, workspace ID for DJI FH2, etc).
publish_rate_hz
INT NOT NULL
How often the Custodian publishes telemetry frames.
OI defaults & FLYplan automation
oi_automation_enabled
BOOL NOT NULL
Master switch — pilot-granted authority for automated OI declarations.
horizontal_speed_ms, vertical_speed_ms
FLOAT NOT NULL
Seeded from DLB max speed on first insert; operator may override.
fv_buffer_m
FLOAT NOT NULL
Flight Volume buffer around the predicted trajectory — horizontal only (v1.12). Vertical separation is handled by NMAC/SWC below, not FV/CV.
cv_buffer_m
FLOAT NOT NULL
Contingency Volume buffer, past the FV edge — horizontal only (v1.12).
oi_automation_square_m, oi_automation_radius_m
FLOAT NOT NULL
OI geometry parameters — valid 350–1400 m and 200–800 m respectively.
oi_automation_amend_min_trigger_m
FLOAT NOT NULL
Minimum trajectory deviation (m) that triggers an OI amendment.
oi_automation_amend_max_alt_agl_m
FLOAT NOT NULL
Maximum AGL altitude an amendment can cover.
oi_automation_amend_enabled
BOOL NOT NULL
Whether automated amendments are permitted for this drone.
waypoint_buffer_m
FLOAT NOT NULL
Radius within which a mission waypoint counts as reached (valid 5–500 m).
Default pilot & separation defaults
default_pilot_name, default_pilot_fs_guid
TEXT NULL
Pre-filled pilot when a mission is created without an explicit selection.
nmac_h_crewed_m, nmac_v_crewed_m
FLOAT NOT NULL
Near Mid-Air Collision minimum vs. crewed (manned) traffic — horizontal / vertical (v1.12 split).
Independent provisioning flags — a drone can have any combination on.
tisb_rebroadcast_enabled
BOOL NOT NULL
Rebroadcasts this aircraft's own telemetry into the TIS-B feed.
ownership_flag_enabled
BOOL NOT NULL
Operator's explicit surveillance intent — excludes this drone from DAA conflict calc when set.
Conspicuity / ADS-B detail (v1.13)
Provisioning detail behind the flags above — closes the gap flagged in §4. Not columns: the mockup's ec_callsign / ec_tc_reg are just local aliases for dlb_drones.inventory_number / identification_number (already Callsign / TC_Registration in v_dtwin) — no new column needed for either.
ec_adsb_hex
TEXT NULL
Static ADS-B ICAO hex code; set when adsb_hex_mode = 'static'.
The currently active Operational Intent — id, lifecycle state, and how it was created.
current_oi_window_start, current_oi_window_end
TIMESTAMPTZ NULL
Active OI's time window.
oi_last_regenerated_at, oi_next_regeneration_at
TIMESTAMPTZ NULL
OI Automation job scheduling bookkeeping.
Live state (written from GCS Plugin telemetry)
position_lat, position_lon, position_alt
FLOAT NULL
Last known position.
flight_mode, armed
TEXT / BOOL NULL
Current flight controller mode and arm state.
battery_remaining_pct
INT NULL
Battery state, for the map UI and low-battery alerting.
link_rssi_dbm
INT NULL
C2 link signal strength.
adapter_connected
BOOL NOT NULL
Whether the adapter currently has a live telemetry connection.
last_telemetry_at
TIMESTAMPTZ NULL
Last frame received — drives staleness/DISCONNECTED detection.
Cross-references & timestamps
aadms_id
TEXT NULL
Cross-tenant ADS-B routing reference back into AADMS.
created_at, updated_at
TIMESTAMPTZ NOT NULL
Standard row bookkeeping.
retired_at
TIMESTAMPTZ NULL
Set by POST /drones/{serial}/retire. NULL = active.
Where OI geometry actually lives (v1.13)
There is no drones.oi_geometry table. Earlier versions of this page (and the strategy doc, v1.11–v1.12) specified a dedicated drones.oi_geometry table for the Flight Volume / Contingency Volume / Submitted Volume polygons. That table was never built. The three geometries for current and upcoming flights are instead persisted in the existing flights_binding table (see §1 and §2) — outside the drones schema. This page doesn't restate flights_binding's own column-level schema; see its owning reference for that.
drones.daa_rules — DAA rule set (table, v1.13)
One row per Detect-and-Avoid rule per drone — backs the mockup's daa_rules[] array. Rules evaluate in sort_order; each can be enabled/disabled without deleting it.
Column
Type
Why it's there
tenant_id, serial_number
TEXT NOT NULL
Links back to the owning drones.drones row (FK).
rule_id
TEXT NOT NULL (PK)
e.g. R1, R2, R3.
condition_text
TEXT NOT NULL
Human-readable trigger condition, e.g. “Target within NMAC radius”.
action_text
TEXT NOT NULL
Action taken when triggered, e.g. “RTH — Immediate”.
priority
TEXT NOT NULL
critical | high | medium | low.
enabled
BOOL NOT NULL
Rule can be turned off without deleting it.
sort_order
INT NOT NULL
Evaluation / display order.
-- drones.daa_rules · one row per DAA rule per drone-- Read/written by the Custodian API's DAA endpoints; evaluated by the DAA service at runtime.CREATE TABLE drones.daa_rules (
tenant_id text NOT NULL,
serial_number text NOT NULL,
rule_id text NOT NULL, -- e.g. R1, R2, R3
condition_text text NOT NULL, -- human-readable trigger condition
action_text text NOT NULL, -- action taken when triggered
priority text NOT NULL, -- critical | high | medium | low
enabled bool NOT NULL DEFAULTtrue,
sort_order int4 NOT NULL DEFAULT0, -- evaluation / display orderPRIMARY KEY (tenant_id, serial_number, rule_id),
FOREIGN KEY (tenant_id, serial_number) REFERENCES drones.drones(tenant_id, serial_number)
);
aadms.drones — cross-tenant lookup (materialized view, central AADMS db)
Physically stored (unlike a plain view) so it has its own column list. Built with postgres_fdw — one foreign server + proxy schema per tenant — aggregating every tenant's drones.drones ⋈ dlb_drones into one flat, queryable surface. Refreshed after each Import sync run.
Column
Type
Why it's there
tenant_id
TEXT
Which tenant this drone belongs to — needed to route back to the right tenant pipeline.
serial_number
TEXT
Join key back to that tenant's own drones.drones row.
guid
TEXT
Aliased from dlb_guid — the DLB identity reference.
callsign
TEXT (UQ)
Aliased from inventory_number. The actual ADS-B routing key — this is what an inbound ADS-B record carries, so it has the unique index.
id
TEXT
Aliased from aadms_id — cross-tenant reference back to the AADMS side.
-- aadms.drones · cross-tenant drone lookup-- Materialized view aggregating every tenant's drones.drones rows (joined to dlb_drones for callsign).-- Foreign servers / user mappings / fdw_<tenant> proxy schemas are set up earlier in aadms-schema.sql.CREATE SCHEMA IF NOT EXISTS aadms;
CREATE MATERIALIZED VIEW IF NOT EXISTS aadms.drones AS-- One UNION ALL block per tenant (via its fdw_<tenant> proxy schema); extend as tenants are added.SELECT
d.tenant_id,
d.serial_number,
d.dlb_guid AS guid,
dlb.inventory_number AS callsign,
d.aadms_id AS id
FROM fdw_airmarket.drones d
JOIN fdw_airmarket.dlb_drones dlb ON dlb.guid = d.dlb_guid
WHERE d.retired_at IS NULLUNION ALLSELECT
d.tenant_id,
d.serial_number,
d.dlb_guid AS guid,
dlb.inventory_number AS callsign,
d.aadms_id AS id
FROM fdw_sait.drones d
JOIN fdw_sait.dlb_drones dlb ON dlb.guid = d.dlb_guid
WHERE d.retired_at IS NULLUNION ALLSELECT
d.tenant_id,
d.serial_number,
d.dlb_guid AS guid,
dlb.inventory_number AS callsign,
d.aadms_id AS id
FROM fdw_sandbox.drones d
JOIN fdw_sandbox.dlb_drones dlb ON dlb.guid = d.dlb_guid
WHERE d.retired_at IS NULL-- ... repeat for each tenant as onboarded ...
;
CREATE UNIQUE INDEX idx_aadms_drones_callsign ON aadms.drones (callsign);
CREATE INDEX idx_aadms_drones_serial ON aadms.drones (serial_number);
Refresh strategy. Refreshed with REFRESH MATERIALIZED VIEW CONCURRENTLY aadms.drones at the end of each Import sync run — CONCURRENTLY keeps it queryable during refresh, which is why the unique index on callsign exists. Full FDW plumbing (foreign servers, user mappings, fdw_<tenant> schema imports) is in database/aadms-schema.sql, not repeated here.
Plain views — what they add, not what they store
drones.v_dlb_drones — dlb_drones with inventory_number → Callsign and identification_number → TC_Registration renamed for reporting. No new data.
drones.v_dtwin — joins drones ⋈ dlb_drones on dlb_guid. Every field listed in §3 above, plus the DLB identity fields (name, brand, model, TC reg, callsign, speeds, weight, etc). This is what the Custodian API, frontend, and MCP tools should all read from.
④ Design vs. Mockup — Gaps to Resolve
Compared the design (Aircraft_Custodian_dTwin_Strategy.html v1.13 + the deployed .sql files) against the current RTM Map mockup (fsl-rtm-map-mu.html). Five rows were resolved on 2026-08-20; the remaining CLEANUP rows are still open for the next dev sync.
#
Area
Design says
Mockup does
What we need to decide
RESOLVED 2026-08-20
Vertical separation model
FV/CV were 3D volumes: fv_lateral_buffer_m + fv_vertical_buffer_m, cv_lateral_buffer_m + cv_vertical_buffer_m.
Horizontal only. Code comment: “FV/CV buffers — horizontal only; vertical separation is handled via NMAC/SWC below.”
Decision: mockup's model wins. FV/CV are now fv_buffer_m / cv_buffer_m — horizontal only. Vertical separation is exclusively NMAC/SWC's job (see next row). The FV/CV/Submitted Volume polygons (now stored in flights_binding, not drones.oi_geometry — see the resolved row below) stay PolygonZ (real trajectory altitude per vertex) but are no longer vertically buffer-expanded. Schema and API docs updated in v1.12–v1.13 — see the strategy doc's changelog.
RESOLVED 2026-08-20
NMAC / SWC / EVAL shape
Three flat scalars: nmac_distance_m, swc_distance_m, eval_distance_m.
Eight fields: nmac_h_crewed_m, nmac_v_crewed_m, nmac_h_uncrewed_m, nmac_v_uncrewed_m, and the same four for SWC, plus eval_distance_m.
Decision: mockup's model wins. Schema now carries the same 8 fields (crewed/uncrewed × horizontal/vertical) for NMAC and SWC; eval_distance_m stays a single scalar. Updated in drones.drones, v_dtwin, and the /separation-defaults API (v1.12).
RESOLVED 2026-08-20
Conspicuity / ADS-B detail fields
drones.drones had 4 boolean flags only: adsb_in/out/surrogate_enabled, tisb_rebroadcast_enabled.
Full “Conspicuity Configuration” panel: ec_callsign, ec_tc_reg, ec_adsb_hex, adsb_out_device, adsb_hex_mode, surveillance_mode, daa_enabled, broadcast_binding, networkid_streaming_enabled, observed status (adsb_observed), and a daa_rules[] array.
Decision.ec_callsign/ec_tc_reg needed no new column — they're local mockup aliases for dlb_drones.inventory_number/identification_number, already Callsign/TC_Registration in v_dtwin. Everything else got real columns on drones.drones (§3: Conspicuity/ADS-B detail + Observed ADS-B state) plus a new drones.daa_rules table for daa_rules[] (v1.13).
RESOLVED 2026-08-20
Current mission / current OI
13 columns exist for this (v1.9): current_mission_id, current_flight_state, the flight/OI windows, waypoint progress, current_oi_id/state/source, regeneration timestamps.
None of these appear anywhere in the mockup.
Decision. Confirmed backend-only. RTM Map does not surface a manual OI-regenerate action or a current-mission/current-OI UI panel; regeneration is a Flink-driven backend process. No new columns were added to support a UI surface — the existing v1.9 columns remain API/MCP-facing (v1.13).
RESOLVED 2026-08-20
drones.oi_geometry table
New table in the v1.11 design — stores flight_volume/contingency_volume/submitted_volume as real polygons.
Not referenced. Also absent from both deployed .sql files.
Decision: remove it from the design.drones.oi_geometry was never built and is now removed from both this page and the strategy doc. The three volumes for current/upcoming flights are persisted in the existing flights_binding table instead (v1.13) — see §1/§2/§3. flights_binding's own column schema isn't detailed in this doc.
CLEANUP
Deployed schema is stale
Aircraft_Custodian_dTwin_Strategy.html is at v1.13.
(not mockup — deployed SQL)rtm-tenant-schema.sql and aadms-schema.sql both still header-reference “v1.6.” They're missing every v1.7–v1.13 addition (waypoint/pilot/separation/data-source/current-mission/OI fields, the new conspicuity/ADS-B columns, and the daa_rules table) and still use the old scalar flight_volume_m/contingency_vol_m and keycloak_client_id instead of the current fv_buffer_m/cv_buffer_m and 8-field crewed/uncrewed NMAC/SWC model. (There is no oi_geometry to migrate — it was removed from the design in v1.13; see the resolved row above.)
Write and run a migration bringing both tenant and AADMS databases up to the current design before any more UI work locks in the old shape.
CLEANUP
keycloak_client_id still in deployed SQL
Removed from drones.drones in the design doc this week — the ID is fully derivable as tenant_id + "-" + serial_number, so it never needed to be a stored column.
Mockup never had this field (no change needed there).
rtm-tenant-schema.sql still declares the column. Drop it in the same migration as above. Also update the GET /drones/{serial}/identity endpoint description so it says “computed” rather than implying a stored field.
CLEANUP
adapter_type enum mismatch
Design doc comment: mavlink | dji_fh2 | dronesense. Deployed SQL comment: mavlink | dji_fh2 | dji_cloud | dronesense — one extra value, and they disagree with each other.
UI dropdown offers DJI / MAVLink / “ADS-B Out” — treating ADS-B as a fourth adapter personality, when adsb_out_enabled already exists as an independent flag that can combine with any adapter.
Agree one canonical enum (needs dji_cloud resolved either way), and fix the mockup so ADS-B Out isn't modeled as mutually exclusive with the telemetry adapter.
CLEANUP
Legacy duplicate fields in mockup
Superseded by adapter_type + adapter_config (JSONB) since v1.6.
Mockup data still carries personality, agent_deployed, agent_location, telemetry_source, dji_org_id, dronesense_url/api_key, mavlink_ip/port/protocol, open_hex_mode/static — flagged in its own code comment as “Legacy compat fields kept for filter functions.”
Find what filter logic still reads these, migrate it to adapter_type/adapter_config, then delete. Self-identified by the mockup author — just needs to be scheduled.
CLEANUP
Custodian runtime/deployment status
No columns for this — only adapter_connected (bool) + last_telemetry_at exist.
UI shows custodian_deployed, custodian_configured, custodian_runtime, gcs_plugin, gcs_plugin_status as if they were persisted state.
Confirm these are meant to be derived at request time (e.g. from a live health check) rather than stored — if so, document that; if not, they need columns.
CLEANUP
Altitude reference
drones.drones.position_alt — a single altitude value.
Mockup's dtwin_state.position carries both alt_msl and alt_agl separately.
OI/geometry math needs to know which reference it's getting. Decide whether drones.drones needs one column or two.
CONFIRMED OK
link_rssi_dbm, aadms_id
Both exist on drones.drones / v_dtwin.
Neither appears anywhere in the mockup UI.
No conflict — just confirm this is intentional (backend/API-only fields, not meant for this screen) rather than something forgotten.
⑤ Recommended Next Steps
Migrate the deployed schema (rtm-tenant-schema.sql, aadms-schema.sql) from v1.6 to the current v1.13 design: drop keycloak_client_id; add the v1.7–v1.13 columns, including fv_buffer_m/cv_buffer_m, the 8 crewed/uncrewed NMAC/SWC fields, and the new conspicuity/ADS-B columns; create drones.daa_rules. No oi_geometry to add — it's been removed from the design in favor of flights_binding.
Retire the mockup's legacy compat fields once the filter logic that depends on them is moved to adapter_type/adapter_config.
Reconcile the adapter_type enum and stop modeling ADS-B Out as an adapter personality.