Rebuilding a 60s Reservation System as a Modern (mostly) Serverless App From the Manual
Note: while I have a good understanding of how the original system was supposed to work thanks to the manual, I lack certainty on a lot of the background and surrounding context. If you know anything about Collins computers or CN computer systems, please get in touch!
The CN Electronic Passenger Reservation System was a system used by CN and later Via Rail from April 1, 1967 to 1980. It used a Collins C-8401 computer located in Toronto Union Station and operators at stations across Canada used a combination of a mark-sense reader and a modified Teletype Model 32 teleprinter.
Image of the mark-sense reader from the EPRS manual. If you know what model or make this is, please get in touch!
The system was designed and managed through Canadian National Telegraph and was hosted in their offices in Toronto, while the efforts of the rest of CN to computerize were largely managed in Montreal, as I will surely discuss in future blog posts and publications.
Ye olde block diagram
I rebuilt the system using the CN manual from Libraries and Archives Canada. I had found the manual while looking through their material related to TRACS and Car Control (if you have any resources on these, please contact me!) and realized that it was a complete enough manual for it to be possible to recreate thanks to a reasonably deep description of both input and output for each operation that the station agents and computer supervisors were expected to perform.
A sample of the depth of the manual.
When recreating the functionality, I started with a few assumptions based on systems that came later, leaning heavily on a relational database using SQL to manage the relationships between different data managed by the system. As I’ll describe, each of these had some flaw with them.
The main assumptions I made were:
Every leg of a trip existed independently of each other, but had to be validated against an allowed train. Almost every operation described can be done on a subset of stations that each train went to, and there is explicit mention of legs as constraints.
Every leg had to be tied to a specific allowed route (or edge in a graph) and was only validated through these edges. This one was the most correct, and came from the specification of specific cities that were used. My understanding here was flawed in that the validation was against the edges allowed per train, rather than every train using all allowed edges for legs.
Every booking made was saved as a specific reservation with a unique identifier. This was a modern assumption based on every system I’ve used today. I expand on this below.
Alternate trains along a route were directly tied to each other. I assumed this based on the discussion of the cross-continental and multi-destination Super Continental trains in the manual, but the behaviour described is more consistent with the system finding literally any train that goes to the same stations rather than having some alternate train tracking.
Taking the booking assumption as an example, here is a relevant section of what the manual says about reservations and what the computer does with them:
…If the seats are available, the Computer will decrease the inventory over the legs involved by the number of seats requested and reply:
OK00101 FEB12 040/1045
The meaning of the reply "FIELDS" is as follows:-
"OK" - The reservation has been made for the seats, legs and train requested….
Given my modern understanding of reservations, I assumed that each reservation would result in a record added to a table somewhere to track it, and it would be managed at that level.
However, this assumption was disproven in two ways. First, when implementing the cancellation logic, I ended up having to randomly grab reservations from the reservation table to match the behaviour described in the EPRS manual. See:
Input format:
MTLAKZ TORMTL 06 06410 045»
(Cancel six coach seats in car 06410 for Feb. 14).
And also the response:
Inventory will be adjusted upward, and the reply will be:
CL06410 FEB14 040/1053
The meaning of the FIELDS is similar to that of the "RESERVED" reply.
Given that I implemented the reservation system before the cancellation system, it was easy to justify storing that specific reservation rather than a simpler inventory system. But especially by the cancellation formats it became clear that there was no actual tracking of each reservation, and instead only the inventory of seats in each car is tracked.
I further confirmed this in two ways. First, the description of functionality for supervisors to add and decrease inventory expresses this behaviour more plainly:
THE "ADD" TO INVENTORY COMMAND
Except for FIELD 1 ("Action" requested) this command is identical in format and activity to the "CANCEL" card. It is provided as a separate card basically for operating convenience,
THE "DECREASE" INVENTORY COMMAND
Except for FIELD 1 ("Action" requested) this command is identical in format and activity to the "RESERVE" card.
However, I did not closely read this section until later as I was focused on implementing the core operators’ functionality. By that time, the reservation by specific bookings system was done and can be viewed as a long-passed commit on the Git repo for this system.
Once I discovered this mismatch in implementation, I re-worked my implementation to match the descriptions of the manual better. I was also able to confirm this in a second way at the Real Rails convention in Burlington this year where I talked to the fine people at the Via Historical Association booth about the system. While talking with them, someone who had travelled in this period and remembered the process in enough detail to provide me some certainty in my understanding joined in on the conversation.
This person had travelled regularly through the 70s from Quebec to Saskatchewan. He remembers clearly that the station agent would use a mark-sense card to check the availability and reserve seats and sleeper cabins (notably not in the manual) for travel. The agent then did the further action of issuing a ticket directly, not involving the EPRS machine.
Finally, I bought a 1966 manual for sales agents at CNR. This manual describes that the agent would use the EPRS machines to check inventory, but was ultimately responsible for writing a ticket manually. As well, it directly confirmed that the computer had no capability to register or otherwise track each customer:
Another interesting confirmation of my implementation came from an unexpected source: the file sizes involved in my implementation. I ended up with a system that used a ~11MB database. While I don’t have any documentation on how the data were stored for the original system I do know, courtesy of the Computer Society of Canada Census of Digital Computers in Canada of 1967, that the original system had 17.3MB (well, MegaWords, close enough) of storage attached to it. This tells me that I am storing the right magnitude of data and suggests strongly that I was right to remove the extra detail on routing and bookings.
In order to share this system with others I figured I needed to host it online and point a front end web application at the system. I had been using an AWS EC2 instance to run the Postgres DB and host the development environment for the iPad I was working on this during my cottage vacation. This made an easy core for the tech stack.
My very real development environment for the MVP. Shout out to Termius, though I wish I could get rid of the AI prompt at the bottom, given that it was entirely useless for this process.
As well I did not want to figure out running an API from an actual (or at least virtual) server given that the majority of my experience is around serverless applications, so I set up the backend to work within an AWS Lambda, and that Lambda would contact the EC2 instance. Finally, I put an API Gateway in front of the Lambda to run the API keys and provide a more seamless experience.
For a front end, I have been hearing developers that work on my products rave about the cross-platform abilities of React, so I figured it was about time to learn at least the basics of it. I made a simple page in React with a lot of help from w3schools and some bug hunting with Gemini. I hosted it on AWS Amplify to round out my all-AWS stack.
You can view the finished application here, the code for the backend is here, and the code for the front end is here. If you have experience with any of the systems that CN used, either freight or passenger, as either a CN employee or as a customer, please let me know!