Contrail

An aviation schema for every flight

Contrail is a simple set of lexicons for the AT Protocol to describe flights. It provides a common structure to support building compelling aviation-centric experiences on the ATmosphere.

To get started, view the definitions or try creating some records using the Flighty importer demo.

Introduction

Contrail is a proposal to provide lexicons to describe flight segments. A contrail record merely asserts that a particular flight existed, additional optional fields exist to provide context. For instance, a user's connection to a particular flight (i.e. as a passenger or pilot) is entirely optional. In fact, the only mandatory field required is createdAt. This is the most important property of the schema and everything else follows from it.

An absent field means the value is unknown or does not apply. It never means zero, empty, or none, and the schema defines no placeholder values. A record describing a flight where only the date and the tail number are known is as valid as one with every field populated. Tools that require complete data should enforce that requirement themselves, since the schema cannot do so without excluding general aviation, where most of the commercial fields have no meaning.

The relationship field is the one exception to this rule. When it is absent, no claim is being made about the author's involvement, rather than the claim being unknown. A record created from ADS-B data about another operator's aircraft simply omits it.

Records for every kind of flying

Contrail is intended to accommodate commercial airline travel and general aviation equally. A widebody operating a transpacific sector and a Super Cub flying circuits at a grass strip are described by the same record type. The second has no flight number, no airline, and no airport code, and it is still complete.

Commercial
{
  "$type": "com.airplaneian.contrail.temp.flight",
  "createdAt": "2026-07-25T09:14:00-07:00",
  "date": "2025-07-13",
  "origin": { "iata": "SFO", "terminal": "INTL", "gate": "A2" },
  "destination": { "iata": "LHR", "terminal": "3" },
  "operatingAirline": "VIR",
  "flightNumber": "20",
  "marketingAirline": "DAL",
  "marketingFlightNumber": "5964",
  "registration": "GVBOW",
  "aircraftType": "Boeing 787-9",
  "actualGateDeparture": "2025-07-13T15:57:00-07:00",
  "actualTakeoff":       "2025-07-13T16:25:00-07:00",
  "actualLanding":       "2025-07-14T10:14:00+01:00",
  "actualGateArrival":   "2025-07-14T10:23:00+01:00",
  "relationship": "passenger",
  "seat": "46K",
  "status": "normal",
  "source": "flighty",
  "sourceId": "bef37d51-2876-433b-bffd-95c81f8fe569"
}
General aviation
{
  "$type": "com.airplaneian.contrail.temp.flight",
  "createdAt": "2026-07-25T09:14:00-07:00",
  "date": "2026-07-19",
  "origin": {
    "name": "Twin Oaks Airpark",
    "faaLid": "7S3",
    "geo": { "latitude": "45.42361", "longitude": "-122.94028" }
  },
  "destination": {
    "name": "Twin Oaks Airpark",
    "faaLid": "7S3",
    "geo": { "latitude": "45.42361", "longitude": "-122.94028" }
  },
  "registration": "N12345",
  "icao24": "a1b2c3",
  "icaoTypeDesignator": "PA18",
  "aircraftType": "Piper PA-18 Super Cub",
  "actualTakeoff": "2026-07-19T10:12:00-07:00",
  "actualLanding": "2026-07-19T10:58:00-07:00",
  "relationship": "pic",
  "source": "manual"
}

Note that the origin and destination in the general aviation example are the same place. Local flights are ordinary and the schema treats them as such.

Identifying flights across repositories

Because a record describes the flight rather than the author, several people aboard the same aircraft may each write a record for it. None of these is canonical, and Contrail treats that duplication as expected. It does not attempt to define a global flight identifier, because no authority exists to issue one.

This does mean readers need some way to recognise two repositories describing the same flight, so the identifying fields carry more weight here than they would in a personal logbook. Write them consistently wherever they are known.

Commercial date + operatingAirline + flightNumber
General aviation icao24 + actualTakeoff

The operatingAirline field takes the ICAO designator (UAL) rather than the IATA code (UA). IATA airline codes are reassigned between carriers over time, which makes a historical record tagged with one ambiguous. Registrations are written in uppercase with no hyphens, so that G-VBOW and GVBOW do not describe two different aircraft.

What Contrail leaves out

Duration, distance, and great circle mileage are all derivable from the times and places already present in a record. Storing them would guarantee that records written by different tools eventually disagree with each other.

Airport names, coordinates, and elevations are reference data about the world rather than assertions by the author, so they do not belong in a personal repository. The one exception is place.geo, which exists for landing sites that no identifier system covers.

Times and time zones

Contrail defines eight timestamps, following the standard out, off, on and in convention, each with a scheduled and an actual variant. All of them are optional. A general aviation record will often carry only a takeoff and a landing.

Every timestamp is written with an explicit UTC offset and no fractional seconds. The Lexicon specification notes two round tripping ambiguities for datetimes, one involving lost precision and one involving trailing fractional second zeros, and a fixed convention avoids both.


Flighty importer demo

The demo below imports a Flighty CSV export into your repository as Contrail records. It runs entirely in this page. Your file is parsed in the browser and is never uploaded, because there is no server to upload it to.

Before you write anything

A complete flight history is also a movement history. Once these records are in your repository they are public and permanent, and a reader can work out where you are based, how often you travel, and when you are away from home.

This holds regardless of what the records claim. The relationship field being optional does not change it, because records about flights sitting in your repository disclose your connection to those flights either way.

Definitions

The following schemas are published to did:plc:5jsivedaksbcc4n2epiegflk and resolve by NSID.

These are currently published under a temp namespace while the shape settles. Published lexicon constraints can never be loosened afterwards, so the stable names will not be claimed until real data has been through them.