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.
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.
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.
The raw radio capture · counter_uas_remote_id.f3411
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.
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.
Open the line that reads 4 profile(s) cannot govern this file.
✗ '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.
“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.”
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.
✗ 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.
Press 4 · Run.
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.
Click into a record and show the four coordinate columns.
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.
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.
“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.
The enriched detection record · counter_uas_detections.csv
Choose counter_uas_detections.csv under
1 · Dataset.
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.
Open the profile dropdown and read it aloud before selecting.
✓ 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.
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.
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.
Tick Recipient is USPER-authorised. Run again.
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.
Stop on those 89 and explain them.
“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.”
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.
And short answers that are true
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.
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.
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.
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.
The three things most likely to
| Symptom | Cause | Fix |
|---|---|---|
| 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.