the engine · Go · MIT · github.com/adamf/jetway

Everything jetway does.

jetway is a messaging gateway for airline and GDS reservation traffic. It terminates carrier links, decodes what arrives on them, keeps passenger name records and answers. It is also a distribution system, a seat inventory, a departure-control system, a datalink and AFTN endpoint, a fare engine and a switch, because wholesky needed each of those to be real, and each is meant to be usable on its own.

the inventory

The whole stack, in one Go module

If you have run a reservations system, a GDS, a departure-control system or a Type B network, this is the list you would write on the whiteboard. Every row is implemented, tested, and exercised daily by a simulation of the world's schedule.

Messages it sends and receives

domainmessages
reservations · Type BAIRIMP sell, reply, cancel, change · SSR · OSI · OSI/locator exchange · ticket numbers · remarks · received-from
reservations · EDIFACTPAOREQ · PAORES · TKCREQ · TKCRES · CONTRL
settlement · fileBSP HOT (DISH 23): BFH01 BCH02 BOH03 · BKT06 BKS24 BKS30 BKS39 BKI63 BAR64 BAR65 BKP84 · BOT93 BOT94 BCT95 BFT99
government · EDIFACTPAXLST (APIS, WCO/IATA/ICAO guide) · PNRGOV push (PADIS guide, with the check-in group)
airport ↔ airport · EDIFACTDCQCKI · DCRCKA (IATCI through check-in)
distribution · HTTPNDC OrderCreateRQ · OrderRetrieveRQ · OrderCancelRQ · OrderViewRS
availability & scheduleAVS · SSM · ASM (CNL, EQT, TIM applied) · SSIM
reservations → airportPNL (multi-part) · ADL
airport → worldPFS · PTM · PSM · ETL · LDM · CPM · BSM · BPM · BUM (rush bag) · AHL · OHD · FWD (tracing files) · loadsheet (AHM 560)
aircraft & operationsMVT · MVA · DIV · ACARS OOOI (OUT OFF ON IN)
air traffic servicesAFTN envelope · FPL · DEP · ARR · DLA · CNL · CHG
transportsType B over framed TCP · MATIP (RFC 2351) · EDIFACT over TCP · HTTPS with mutual TLS · file drop · dialled trunk links between switches (link_dial + via routing)

What a reservations system does, and it does

  • Book across carriers and dialects in one record; free-sell from the availability cache or request and await the answer.
  • Hold the statuses the industry uses: NN SS HK HL HN HX KK KL UC UN US UU TK TL NO XX.
  • Price at booking under filed rules and taxes; carry fare basis and amounts on the record.
  • Ticket with coupons and check digits; issue EMDs; exchange ticket control with the operating carrier.
  • Divide a booking; cancel and advise every holder; add and drop segments; sweep ticketing time limits.
  • Refund a document: open coupons go back to the purchaser, used ones stay used, the settlement plan reports the refund with the amounts reversed. Tell every carrier its ticket numbers, over EDIFACT ticket control or as SSR TKNE on a teletype link.
  • Interline and codeshare: marketing and operating carriers each hold their record and point at each other's locators.
  • Queues for confirmations, waitlists, unables, schedule changes, ticketing and divergence; IROPS rebooking that asks and waits.
  • Availability as claims with age and provenance, broadcast and consumed.
  • Special services carried and honoured at the airport: WCHR WCHS WCHC UMNR INFT CHLD CBBG VGML DOCS MEDA BLND DEAF and more.

What a DCS does, and it does

  • Opens the flight from the name list; checks in with seat assignments by cabin and bag tags; applies ADL amendments.
  • Boards, closes the counter and the door on the clock; produces PFS, PTM, PSM, ETL, LDM, CPM to their addresses.
  • Load control by the AHM 560/565 index method, a legal loadsheet per departure.
  • Reconciles the hold against the cabin at the door: a loaded bag whose passenger did not board holds the flight until it is pulled; a boarded passenger's bag never loaded is named for the rush and announced ahead of itself to the arrival station (BUM); the station traces it: an AHL when its passenger arrives without it, an OHD when it arrives alone, matched on the tag and forwarded (a profile; WorldTracer's formats are the vendor's). Offloads on cancellation.
  • Through check-in across carriers over IATCI: accepts another airline's connecting passengers by name, seats them and answers per passenger; asks the onward carrier for its own passengers and records the seat it gave.
  • Changes the aircraft under a checked-in flight: rebuilds the cabin from the new type, keeps every seat that still exists, re-seats the rest and puts whoever no longer fits on standby with a denied-boarding alert.
  • Tells the state: the APIS passenger list at the door (PAXLST) and the PNR push with each traveller's seat, sequence and bags (PNRGOV), both to the public guides.
  • Derives MVT from the aircraft's OOOI reports; files the flight plan and hears the towers back.

What a switch does, and it does

  • Trunks to another switch: holds a dialled, bidirectional link open to a second switch and routes the other switch's subscribers via it, so a message between carriers on different switches crosses the trunk once and is never carried back to its own origin.
  • Terminates hundreds of circuits; relays by address line and UNB recipient to every addressee named.
  • Captures before interpreting; keeps possible duplicates marked; sequence gaps and repeats counted per peer.
  • Spools before acknowledging; redelivers with backoff when a link comes back; a peer that stops reading is refused, never stalled behind.
  • Rate limits per peer with a shared cap; a lease for standby takeover; hot reload of peers; retention by daily partition.
  • Peaks above 16,000 messages a second on one 4-vCPU machine under the world's departure banks.

The API

recordsPOST /api/book · GET /api/pnrs · GET /api/pnr/{locator} · POST /api/pnr/{locator}/ticket · /emd · /split · /cancel
messagesGET /api/messages · GET /api/message/{id} · POST /api/message/{id}/replay · GET /api/stream
sellingGET /api/availability · GET /api/flights · GET /api/journeys · POST /ndc
workGET /api/queues · GET /api/queue/{name} · POST /api/queue/item/{id}/work
carriersGET /api/carrier/{designator}/pnrs · GET /api/carrier/{designator}/inventory · GET /api/insights
operatingGET /healthz · GET /readyz · GET /metrics · GET /api/status · POST /api/admin/retire · GET /api/admin/export
the wire

Speaks what the industry actually sends

Wire syntax is exact; message grammar is a profile. The envelope layers are strict about what they validate, because ISO 9735 and the Type B envelope are universal. What sits above them varies by carrier and bilateral agreement, so those layers are ordered recognisers you replace per link without forking anything. An unknown message still decodes at the syntax layer, so it can be captured, routed and replayed even when nothing above knows what it means.

Type B and AIRIMP envelope specified

The teletype format the SITA and ARINC store-and-forward networks carry.

  • Envelope: priority and address lines, origin line, character repertoires, the 4 KB cap and possible-duplicate marking from IATA's public Type B whitepaper, edited textually so raw bytes are never regenerated.
  • AIRIMP grammar as an extensible recogniser profile: sells, replies (KK, US, UC, UN, NO), cancels, name and segment changes, SSRs and OSIs, ticket numbers, locators exchanged both ways.
  • Long items fold at 63 characters and parts paginate at 60 lines, because a family of four on a filled 737 overran both.
  • Relay by address line: one message reaches every addressee it names.

UN/EDIFACT and PADIS syntax specified

ISO 9735 interchanges carrying IATA PADIS messages such as PAOREQ and PAORES, with CONTRL sent and consumed.

  • Syntax: UNA service characters, release characters, repetitions, envelope validation, fuzzed round trips.
  • PADIS as a segment-handler profile; the free PNRGOV implementation guide corrected four bugs whose tests had encoded the same guess as the code, and describes how a divided booking is represented (EQN, RCI).
  • Both dialects settle the same booking: a carrier asked over EDIFACT and one over Type B, each answering separately and each returning its own locator.

MATIP RFC 2351

The airline transport for teletype over IP: packet format and the Type B session handshake, for the share of the world that dials in that way.

NDC schemas public

IATA order messages over HTTP: create, retrieve, cancel and the order view, mapped onto the same record store as the teletype and EDIFACT traffic. Payloads carrying card numbers are refused before capture; there is no encryption at rest.

Availability AVS inferred

Availability Status messages as a per-link profile, feeding a cache in which every belief carries its age and where it came from. A status older than the trust window stops being evidence and the booking falls back to asking. Free sale where the cache offers it, a request to the carrier where it does not.

Schedules SSIM inferred

SSM and ASM schedule messages as an extensible profile, and the SSIM chapter 7 schedule file read and written: header, carrier, flight leg and segment data records to the 200-column layout, codeshares tied by DEI 010/050. A cancellation as ASM reaches distribution and the airport, and the bookings under a marketing carrier's code are told under that code. An equipment change (EQT) queues the holdings for reprotection; a time change (TIM) moves the held segments to the new times at TK, on the station's clock when the message is in UTC, and queues the advice.

records and selling

Holds the record and does things to it

The passenger name record is an event-sourced projection with optimistic concurrency: every change is an event naming the message that caused it, a write carries the version it read, and a stale write is refused rather than allowed to overwrite what it never saw. A gateway and a carrier can be modifying one record at the same instant; that is normal.

The pipeline

capture ▸ classify ▸ decode ▸ dedupe ▸ apply ▸ queue ▸ respond. Raw bytes are made durable before anything interprets them, so a parser fix can be replayed onto traffic that already failed instead of asking a partner to retransmit. Unrecognised lines become fragments on the record; undecodable messages go to the dead-letter queue with bytes intact. Nothing leaves the system.

Booking

  • Sell one or many segments across carriers, in either dialect, with SSRs, contacts, received-from and channel; the record carries every carrier's locator for the same booking.
  • Answers are what the carriers said: HK held, HL waitlisted, UC refused and the record cancelled behind it, UN when the requester's schedule is wrong.
  • Locators from a keyed permutation per system, so filled and live records cannot collide.
  • Pricing at booking time against the gateway's tariff: fare basis per segment, amounts on the record.

Documents

  • Tickets issued per passenger with coupons per segment, numbers with airline code and check digit (advisory, because public sources disagree), and ticket control exchanged with the carrier flying the passenger.
  • EMDs, standalone and associated, with the coupon status vocabulary corrected by the free EMD guide.
  • Ticket-not-advised and every other gap a partner leaves lands on the divergence queue, named.

Changing the record

  • Cancel a booking and tell everyone holding it; a carrier that cannot be reached is a queue item, not silence.
  • Divide one booking into two, seats going with the passengers, each carrier still holding one record until advised (the AIRIMP divide message itself is the most expensive missing document).
  • Add and drop segments, including a marketing carrier forwarding a confirmed codeshare sell to the operator as the interline sell it is.
  • Ticketing deadlines swept by time: an unticketed booking cancels when its limit passes.

Work queues

Confirmation, waitlist, unable, schedule change, ticketing and divergence queues, with placement by the pipeline, a time-based sweeper for silence (a request nobody answered, a deadline that passed), counts per queue, and an external-publisher seam.

Irregular operations

The IROPS engine works the schedule-change queue the way a desk does: the next flights over the same city pair, own metal first, free sale where the cache offers it and a request where it does not, each request awaited until the carrier answers. A confirmed seat drops the dead leg with a real sell and a real cancel on the wire; a waitlist is kept and named; what nothing can carry stays on the queue for a person.

fares and inventory

Prices the trip, controls the seats

pkg/fare structure inferred · data none

The structure of a filing, and no fare of its own: ATPCO's are licensed, so callers supply a tariff.

  • Filings per market and class with a fare basis; rules for advance purchase, minimum and maximum stay, seasons, change and refund fees.
  • Taxes by kind: percentage, per segment, per ticket, per enplanement.
  • Passenger types ADT, CHD and INF at their discounts.
  • Pricing sells each segment under the cheapest fare whose rules the trip meets as of the purchase date, and says why when nothing does.

pkg/bsp DISH 23 specified

Settlement: the Airline Accounting/Sales data file (HOT) a Billing and Settlement Plan hands each airline for its agents' sales, written and read to IATA's public handbook, column by column. Sales and refunds, over-punch signing, the document-amount, commission and remittance arithmetic of section 6.7, office, cycle and file totals; tested against the handbook's own worked figures. Exchanges carry their original issue (BKS46), agency debit and credit memos name the document they correct (BKS45), and the agents' side, the RET of chapter 5, is written and read too. Ticket numbers reach teletype carriers as SSR TKNE so their books reconcile.

pkg/prorate method public · provisos not

Interline billing's arithmetic: a through fare divided between the coupons of an itinerary by mileage (straight rate proration), rounding on the last coupon so the shares add to the fare, and the interline service charge the carrier that flew keeps back. The Prorate Manual's minima, factors and special agreements are sold and not reproduced.

pkg/inventory method specified

Leg-based seat control, the textbook kind; the numbers are the caller's.

  • Cabins per leg keyed by carrier, flight, date, boarding point and compartment, with booking classes mapped onto the cabins the aircraft has.
  • Nested class authorisations: a full cabin waitlists, a closed class with seats still in the cabin refuses, the last seats go at the higher fares.
  • Revenue management: an EMSR-b controller sets the authorisations from a demand forecast by class (fare, mean, spread), pooling the classes above each discount and protecting seats until the marginal seat is worth less than the discount in hand; re-optimised on every question and told what each class has sold, so a forecaster reading the booking curve moves the ladder through the day. Tested against the normal table, not the code. Network control: a connecting itinerary must cover the sum of its legs' bid prices -- the ladders' displacement costs, or the duals of the deterministic network programme (a plain simplex over legs, itineraries and demand) when the caller supplies them.
  • Waitlists a tenth of the cabin deep, at least two; overrides per class.
  • Rebuilt from the book of record at boot, because anything that remembers sold seats in memory oversells after a restart. Publishes per-carrier sold, waitlisted and full-cabin gauges and counts every answer by status.
the airport and the aircraft

Closes the flight, and hears it fly

Reservations to the airport PNL/ADL inferred, closely

The passenger name list at T−180 in as many parts as it takes, the additions and deletions list at T−60, per-passenger elements naming who they belong to (a child, a cabin bag with its own seat), hyphenated ticket numbers, and pagination by rendered lines rather than by item.

Departure control practices inferred from worked examples

  • The manifest opened from the name list; check-in in waves with real seat assignments by cabin and bag tags to sortation (BSM), then bags reported loaded from the hold (BPM).
  • Boarding and close: the counter closes at 45, the gate boards from 30, the door closes at 10, and the airport says so: PFS final sales, PTM transfers, PSM special services, ETL the ticket list, LDM load and CPM container-pallet, to the addresses that receive them.
  • Load control: the AHM 560/565 index method with a representative fleet, producing a legal loadsheet with fuel, trim and limits.
  • Bag reconciliation at the door: every bag the hold reports loaded must belong to a boarded passenger; unaccompanied bags are named and come off the load, short-shipped bags are named to follow, and the door can be held until the hold matches the cabin.
  • Cancellation offloads whoever had checked in and pulls their bags; the manifest is let go at the hour it would have landed.
  • Aircraft substitution: a smaller type takes a checked-in flight; the cabin is rebuilt, seats kept where they exist, the rest re-seated, the overflow denied boarding by name, and distribution hears the ASM EQT under every code the flight sells under.
  • Through check-in (IATCI, DCQCKI/DCRCKA): connecting passengers checked in onto another carrier's flight by the delivering station, refusals per passenger in the standard vocabulary, the onward seat recorded on the manifest.
  • The state's messages: the APIS list at the door to the WCO/IATA/ICAO PAXLST guide, and the PNRGOV push, records as booked before departure and with the check-in group at the door, to the IATA PADIS guide, both tested on the guides' own worked examples.

Movements and datalink AHM 780 · ARINC 620, from published tables

  • ACARS OOOI out, off, on, in, as a datalink provider forwards them to the airline.
  • MVT/MVA/DIV derived from the reports rather than asserted: departures, arrivals, delays with their codes, diversions to the field really landed at.

Air traffic services Annex 10 · Doc 4444 via FAA reproductions

  • AFTN envelope: priority, eight-letter addressee indicators, origin, ZCZC and NNNN, tested against the Annex's own example.
  • ATS messages: FPL, DEP, ARR, DLA, CNL, CHG, so a flight plan is filed with the towers and their DEP and ARR come back.
The flight panel in wholesky, showing what departure control produced for one flight: name list parts, seat map, load and loadsheet, flight plan and towers' replies, manifest

What one departure's closure produced, as wholesky shows it: everything here came out of jetway's departure-control, movement and AFTN packages.

operating it

Built to be run, not only read

One assembly, pkg/node, built by both jetwayd and the scenario suite, so what the tests drive is what the binary runs. Everything under pkg/ is importable, which is how wholesky hosts 518 gateways in one process.

Ingress and identity

  • Listeners: framed TCP, MATIP, HTTPS with mutual TLS, and a file drop.
  • Identity comes from the certificate presented or the circuit arrived on, never from a name asserted.
  • Rate limits pace each peer's reader so the partner's own circuit pushes back, with a shared cap per ingress and a peer's own share where a partner needs one.
  • Outbox per link: a bounded queue and a writer goroutine, so a read loop never waits on a peer's window; a peer that stops reading gets refusals, not a stall. Frames sent in the same burst as a hello are kept.

Durability

  • Spool: raw bytes fsynced locally before the partner is acknowledged; a drainer moves them into the store and retries for as long as it takes. Bounded; a full spool refuses rather than promises.
  • Store: append-only message log and event-sourced records, in memory or Postgres. Records partitioned by retirement day; the day's partitions are dropped when it wraps; a partition that cannot be created never stops a write. Bulk load with COPY.
  • Egress with backoff and restart recovery; redelivery when a link comes back.

Running it for real

  • A lease so a standby node takes over when the holder stops renewing, and releases after a drain.
  • Readiness that pings the store and fails while standing by; drains that wait for in-flight work.
  • Hot peer reload on SIGHUP; peers keyed by name with format, transport and addresses.
  • Retention and archive: jetwayctl retire --before drops the day; jetwayctl export --out streams every record as newline-delimited JSON for the archive a regulator asks about years later.
  • Metrics in Prometheus exposition with no client library; OpenTelemetry tracing with a hand-rolled OTLP/JSON exporter. A production plan for GCP is in docs/production-gcp.md.

The console

  • Message flow: every link, every message on the tape, opened and read field by field, raw bytes beside the decode.
  • Records: found by who is travelling; interline marked; their locators; status as the carriers said it; the fare card with base, taxes, total and each passenger's fare bases.
  • Queues: confirmations, refusals, waitlists, and the divergence queue naming each gap.
  • Insights: selling, documents, records and traffic. Departures: a flight mid-check-in, its manifest and seat map, and the closure's messages.

The command line

  • jetwayd the gateway, with three simulated carriers and the console at :8080 by default; relay mode makes it a switch.
  • jetwayctl decode captured.tty for any captured message; jetwayctl retire for retention.
  • jetwayload runs the scenario suite concurrently and reports latency.

Tested how

  • Fixtures written by hand from the spec; fuzzed round trips on every codec; conformance against both store backends.
  • End-to-end scenarios through the real assembly on real TCP; the load driver reuses them.
  • A new test counts only once it has been watched to fail against the old behaviour.
  • wholesky is the load test: a day of the world at warp 1 with an invariant suite as the pass criterion.
the honest table

What is specified, and what is inferred

Most of these formats are defined in paid IATA publications that were not bought. That is not a reason to guess quietly; it is a reason to say which layer is which.

specified the document is public and conformance is checked · inferred built from free reproductions and worked examples · no data structure only
layerstandingsource
edifact · CONTRL · matipspecifiedISO 9735, RFC 2351
typeb limits · PDMspecifiedIATA's public Type B whitepaper
padispartlythe free PNRGOV implementation guide
ndcspecifiedpublic schemas and carrier examples
aftnspecifiedICAO Annex 10 Vol II
atsspecifiedFAA reproductions of Doc 4444 forms
inventorymethod specifiedleg-based nested authorisations; numbers are the caller's
dcs load controlmethod specifiedAHM 560/565 arithmetic; representative fleet data
mvt · acars · PSM PTM LDM CPMinferred, closelyOAG tables and verbatim examples; airports' reproductions of the practices
airimp · avs · ssim · PFS · ETLinferredprofiles, not conformance; AIRIMP and SSIM are paywalled
farestructure onlyhow ATPCO filings and tickets work; the filings are licensed and the package carries none

The roadmap names each paid document and what its absence costs, so they are one procurement decision rather than six unrelated apologies. The AIRIMP divide message is the most expensive single absence: it is why a split booking cannot be advised to its carriers.