Demo runbook · governed data platform

Counter-UAS: what the platform refuses, and what it permits

Two files, twenty minutes. The first one the platform reads and then refuses to govern, naming exactly what is missing. The second one it governs three different ways depending on who is asking. Every figure here was captured from the deployed build — including the ones that make the platform look less clever than a pitch would prefer.

counter_uas_remote_id.f3411 · binary radio capture · 4.5 KB counter_uas_detections.csv · enriched detection record

The story in one paragraph

A ground sensor network watches for drones over an airport. It hears their radio broadcasts and it paints them on radar. When a drone is registered, its broadcast identifier can be resolved through the civil aviation registry to a named person at a home address — and if that person is American, U.S. law requires their identity be protected. The demo shows a platform that knows the difference between a raw sensor feed and an analyst's finished record, and refuses to pretend the first one can carry obligations it has no field for.

Before you start. Open http://127.0.0.1:8092 and log in. Do not select a local model from the analyst dropdown while presenting over the public tunnel — it takes about 394 seconds and Cloudflare cuts the response at 100. The hosted default is already correct.
01

The file it will not govern

The raw radio capture · counter_uas_remote_id.f3411

01

On the Run tab, under 1 · Dataset, choose counter_uas_remote_id.f3411.

This is binary. Not a spreadsheet, not an export — the actual ASTM F3411 Remote ID messages a receiver hears off the air. Open it in a text editor beforehand if you want the room to see that it is unreadable.

What appears under the file picker
18 columns · 591 records · astm_f3411 · 591 packets

The platform worked out the format from the bytes, not the file extension. Rename it to .csv and it still reads as Remote ID — on the input path of a governance platform, a filename is a claim by whoever named it, and a claim is not evidence.

02

Open the line that reads 4 profile(s) cannot govern this file.

Counter-UAS Detections — ground sensor network
✗ 'Classification markings' cannot run on this dataset:
    missing element: security_classification
✗ 'U.S. Persons controls' cannot run on this dataset:
    missing element: us_person
✗ 'Declassification review' cannot run on this dataset:
    missing element: security_classification or creation_date

remedy: remove it, or supply the missing element

This is the moment the demo exists for. The platform read the file perfectly and then refused to govern it — and it did so before touching a single record, because whether a control can run is a property of the columns, not of any row.

What to say

“Remote ID broadcasts a registration number. It does not broadcast a name, it does not carry a classification, and there is no field anywhere in the standard that says whether the operator is a U.S. person. So the platform will not run the U.S. Persons control on it. It will not skip the control quietly and hand you a clean-looking output — it stops and tells you which field it needed.”

03

Under 2 · Profile, select Counter-UAS Remote ID capture — raw radio.

It shows unapproved and an approval box appears. Type your name and press Approve as named person.

If you try to run first
✗ profile 'counter-uas-remote-id' is a draft and has not been
  approved. A profile must be approved by a named person before
  it can govern a run.

Worth 15 seconds out loud: the fingerprint is f14ef6429e2a0f36 before approval and identical after. What was reviewed and what runs hash the same — approval records a decision, it does not modify the thing decided on.

04

Press 4 · Run.

Result
PROMOTED · 591 packs · identifier column: uas_id

  lineage      591/591
  pii_detect   591/591

  _pii_tags: uas_id:DEVICE_ID
             operator_latitude:LOCATION
             operator_longitude:LOCATION
             operator_altitude_m:LOCATION

The capture is not ungovernable — it is governed by the profile written for what it actually contains. Lineage is stamped on every record whether anyone asked for it or not.

05

Click into a record and show the four coordinate columns.

One record, four decimal degrees
ua_latitude         26.9091      ← aircraft
ua_longitude       -97.23268     ← aircraft
operator_latitude   26.6554      ← a person, standing somewhere
operator_longitude -97.13521     ← a person, standing somewhere

Two of these are where a drone is flying. Two are where a person is standing. Read the values alone and nothing separates them — same encoding, same units, overlapping ranges, four bytes apart in one broadcast burst.

What the machine proposed · claude-opus-5
ua_latitude        location of a vehicle or platform   0.85
ua_longitude       location of a vehicle or platform   0.85
operator_latitude  location of a person                0.85
operator_longitude location of a person                0.85

The model gets it right, and you should say so. Claiming otherwise invites the obvious rebuttal — the column is called operator_latitude. The interesting part is how it gets it right, and why that is still not enough.

What to say

“It is reading the label, not the data. Rename these to field_07 and field_11 and every bit of that confidence evaporates — nothing in the numbers themselves says which one is a person.”

“And it can only propose in words we gave it. Until this morning our vocabulary had a single term, ‘geographic location’, for all four — so the model returned the same answer for a drone and a human at 0.95. Not fooled; it had no way to say what it could see. Somebody had to decide that ‘location of a person’ is a thing this platform recognises.”

“So: a suggestion at 0.85, drawn from a label, inside a vocabulary a person wrote. Getting it wrong publishes somebody’s home position or deletes the flight track the system exists to produce. A machine reading is not permitted to write that declaration here at any confidence — a named person commits it, and their name is on the run.”

This is the honest version of human-in-the-loop, and it is far more defensible than “machines can’t do this”. The machine did well. The consequences of the residual error are asymmetric and severe, so the commitment stays with a person who can be asked why.

02

The file it governs three ways

The enriched detection record · counter_uas_detections.csv

06

Choose counter_uas_detections.csv under 1 · Dataset.

What appears
27 columns · 2,000 records · CSV

Same drones, same afternoon. An analyst has since joined the registry, made a classification call, and recorded whether there is a lawful basis for holding the identity.

Nine of the twenty-seven columns did not come off the antenna. They are judgements somebody made afterwards — which is the point of the two acts. Governance obligations attach at enrichment, not at the sensor.

07

Open the profile dropdown and read it aloud before selecting.

The picker, ranked
✓ Counter-UAS Detections    — describes 100.0% of columns
✓ UAV / ISR Track Data      — describes  11.1% of columns
✓ Telecom CDR               — describes   0.0% of columns
✗ Federal Records           — cannot govern this file

The telecom profile can run on drone data — it just describes none of it and would apply almost nothing. Ranking on coverage keeps “able” from reading as “appropriate”. Select Counter-UAS Detections and approve it the same way as before.

08

Leave 3 · Recipient authority unticked. Run.

This is the airport security desk. Cleared to see the air picture; not cleared for U.S. Persons information.

Result · unauthorised recipient
PROMOTED · 2,000 released · 0 withheld
   MASK 456   RELEASE 1,544

  DET-00000000
    drone position    26.90910, -97.23268   ← kept
    operator name     [U.S. PERSON]
    operator address  [U.S. PERSON]
    operator position [U.S. PERSON], [U.S. PERSON]
    reporting officer Sgt Vasquez           ← kept

    reason  U.S. Persons information; recipient not
            USPER-authorised

Two things survived on purpose. The drone's position, because a machine is not a protected person and minimising it would destroy the track. And the reporting officer, because §2.3 protects the subject of collection, not the government employee who filed the report — masking them would protect nobody and destroy accountability.

09

Tick Recipient is USPER-authorised. Run again.

Result · authorised recipient
PROMOTED · 1,911 released · 89 withheld

  DET-00000000
    operator name     Nils Vasquez
    operator address  786 Pecan Ave
    operator position 26.65540, -97.13521

    basis   'counterintelligence'
    reason  authorised recipient; retention basis
            'counterintelligence'

Note the record count fell. 89 records went from masked to withheld entirely — and that is the subtlest and best part of the whole demo.

10

Stop on those 89 and explain them.

What to say

“The clearance was not enough. Under E.O. 12333 §2.3 you need authority and you need a lawful basis for retaining the information. These 89 detections involve U.S. persons and nobody recorded a reason for holding their identity — so an authorised recipient gets less than the unauthorised one did. Not a masked name. No record at all.”

“Most systems treat permission as binary. This is what the statute actually says.”

03

The slide to leave up

One corpus, three answers

Who is asking Disposition Records What they see of the operator
Anyone — drone is foreign or unregistered Release 1,544 Nothing to protect; full record
Airport security desk — not authorised Mask 456 Name, address and position all [U.S. PERSON]
Federal partner — authorised, basis stated Release 1,911 Full identity, and the reason is recorded
Federal partner — authorised, no basis Withhold 89 Nothing. The record does not leave.

Throughout all four, the drone's own position is never touched and the reporting officer is never masked.


04

Questions you will get

And short answers that are true

“Can it do this without sending our data to Anthropic?”

Yes, and it is measured. A local model via Ollama scored 17 of 17 columns on our telecom corpus with nothing leaving the machine. It takes about 6.5 minutes per reading on a CPU with no GPU, so it is a real option with a real cost — not a checkbox.

“What stops the AI getting it wrong?”

Nothing rests on it being right. The model only ever proposes meanings — it cannot mark a field required, write a personal-data declaration, raise a profile’s authority, approve anything, or choose which profile governs a run. Each of those is enforced in code with a test behind it. It is often correct; it is never load-bearing.

“Is this a catalogue? We already have one.”

No — it is a data plane. It enforces on data as it passes through, then publishes what it enforced to your catalogue. It emits OpenLineage to DataHub rather than replacing it.

“How long to add our data type?”

Counter-UAS was added in a day, and the drone use case before it needed no Python at all — one vocabulary entry and two mapping rules, both plain JSON files reviewed as a diff.


05

If something goes wrong

The three things most likely to

SymptomCauseFix
Profile is greyed out in the picker Refused for this file, or not yet approved Read the refusal panel — it names the missing element. If it says unapproved, select it and use the approve box.
Page hangs, then a Cloudflare error A local model was picked over the tunnel Reload, choose claude-opus-5. Local models need 127.0.0.1:8092.
Demo URL is dead The tunnel container restarted and the hostname changed docker compose logs tunnel | grep trycloudflare

Every figure on this page was captured from the deployed container, not estimated. If a number on screen differs from a number here, trust the screen and tell me.