A visitor asks what is on at two o'clock. The person behind the desk opens the public website on a shared tablet, scrolls to today, and gets an answer. The next visitor asks whether the school group in the main hall will be gone by eleven. That answer is on an operations board, in a different app, behind a different sign-in. Then the duty manager wants to know how full the building is, which lives in a vendor console nobody on the desk has open. None of these questions is hard. Every one of them costs a tab switch while someone waits.
The front-of-house app exists to remove that cost. It is a single web app, installed on the desk tablets as a full-screen app and available on staff phones, that puts every one of those answers behind one home screen of large tiles. This is a look back at its first seven weeks, from an empty repository to a production app the desk runs a day on.
01 · THE PROBLEMEvery answer existed. None of them were at the desk.
The important thing about the front desk's problem is that nothing was missing. The programme was published. The runsheet was maintained, every day, by the people who run the building. School group arrivals, duty managers, facility issues, opening hours: all of it was written down somewhere by someone whose job it was. Visitor counts were measured by sensors at every entry. The data was good. It was simply in six places, and the desk is the one place in the building where nobody has time to look in six places.
The second thing is who uses it and how. The desk is a standing job. The device is a shared tablet, touched by fingers, often mid-conversation. Anything designed for a mouse, a dropdown, a date picker, a small link, fails there in a way it never fails at a desk with a keyboard. And the desk has no patience for a login screen two minutes before the doors open on a Saturday.
02 · THE WRONG TURNThe two obvious designs, and why neither survives the floor
There are two designs everyone reaches for first, and both are reasonable for about a day.
The first is a portal of frames: a home screen of tiles where each tile opens the existing tool inside the app. It is cheap and it ships in a week. It also breaks on contact with reality. A service desk that sends a frame-ancestors policy renders blank when embedded, and redirects to its own login anyway. Several tools only make sense signed in. The ones that do embed still look like six different products wearing one frame, with six different idioms for touch. The app does embed where it genuinely works, the wayfinding map and the collection site, and opens the rest in a new tab. But a portal is a bookmark bar, not an answer.
The second is a front-of-house database: copy the programme, the runsheet and the counts into a store the app owns, and render from that. It feels robust. In practice it creates a second source of truth for every field, a sync job per source to keep it honest, and a new question nobody had before, which is whose copy is right when a duty manager edits the runsheet at 8:50 and the desk still shows the old one at 9:05.
The trap in both
Both designs make the app responsible for something it should not own. The portal makes it own the look of other people's products. The database makes it own other people's data. A front-of-house screen has no business being the system of record for anything. The moment it is, the desk is reading a rumour about the runsheet rather than the runsheet.
03 · THE KEY INSIGHTOwn nothing. Read the source. Decide at the door.
The design that held is a short set of refusals.
The app stores no business data. Every screen reads its source of record through a small route on the app's server, at request time, and shapes the answer for a finger. The runsheet is the operations board. What's On is the content API. Visitors are the sensors. When a duty manager edits the runsheet, the desk sees it on the next read, because there is nothing in between to go stale. Credentials for those sources live on the server and never reach the tablet.
Who gets in is decided once, at the door, per request. On the venue's own network, the desk walks straight in: no login, no sign-in library even loaded. Anywhere else, a staff member signs in with their work account and the server verifies that identity on every request. There is no client secret anywhere in the design to leak or rotate. And if the door is misconfigured, it stays shut: in test and production, nobody gets in until the rules are set.
The UI is designed for fingers first, always. Every control is at least 44 pixels. There is not one native dropdown or date picker in the app; both were replaced with chips, sheets and a touch calendar. Every screen is checked at phone width and tablet width, the way a finger would use it.
The desk does not need another system. It needs every other system to be one tap away and never wrong.
04 · THE ARCHITECTUREA door, a thin app, and the systems that already knew the answers
Read the picture left to right. Two kinds of device arrive at one door. Behind the door is a thin Next.js app on Vercel whose only job is to read and shape. To its right are the systems that own the data, reached through the integration gateway where one exists and directly where it does not. Nothing on the right knows the app exists, which is the point.
Thirteen screens, and where each one reads from
This is the controlling table for the app. Every screen has exactly one source, and when a new screen is proposed, the first question is which system already owns its data.
Two rules came out of building that table. A tile that cannot be embedded properly opens in a new tab rather than being embedded badly. And the home screen itself carries the weather, the time and a "Did you know" fact from the daily runsheet, because the desk glances at it far more often than it taps it.
05 · THE DETAIL THAT SEPARATES REAL FROM DEMOWhat has to be true before the desk can trust it
A demo of this app takes an afternoon. What took the seven weeks were four things that never appear in a demo.
The first is the door. The desk tablets must never stop for a login, and a staff phone on the train must never get in without one. The hosting platform sees the address traffic leaves the venue from, after the network translates it, so the trusted network is defined by the venue's outbound addresses, not the ones inside the building. Off the network, the work sign-in runs in the browser, the resulting token is posted once to the server, checked for the provider's signature, the right issuer, this app as its audience and its expiry, and kept in a cookie the page's scripts cannot read. While the tab is open it is renewed before it expires. Close the tab on a shared device and the sign-in ends with it.
A desk tablet on the venue network STRAIGHT IN
The common path, and it should be invisible. The request comes from the venue's network, the door opens, and the sign-in library is never even loaded.
door: trusted network · no sign-in · no token
A staff phone anywhere else VERIFIED
One work sign-in, then every request carries a token the server checks on its own. No client secret exists to leak, because the app is registered as a single-page app and only verifies what the provider signed.
door: signature · issuer · audience · expiry
Nothing configured, or anything wrong SHUT
The path that protects the venue. In test and production, an unset or broken rule means nobody gets in, rather than everybody. A page sends the visitor to sign in; an API answers 401, or 403 for a signed-in person outside the allowed groups.
door: fails closed · never fails open
The second is names, not numbers, for the sensors. The obvious way to read the visitor counters is by their numeric IDs. The trouble with an ID is that it silently changes when someone replaces a sensor or rebuilds a zone in the vendor console, and the desk would show a confident wrong number. The app asks the vendor for its list of counters, finds each one by the name the building team gives it, caches that list for ten minutes, and falls back to the known IDs only if the lookup fails. A renamed sensor is a configuration change, not a deploy.
The third is the cold morning. The first person at the desk at opening is the person least able to wait for a gateway that has been idle all night. A scheduled warmer reads every data route every ten minutes through opening hours. A reload draws the last answer instantly and refreshes behind it. A tapped tile shows that it landed even before its data arrives. None of that is visible when it works, which is the only acceptable state for it.
The fourth is contracts that fail the build instead of the desk. The content API client is generated from its published spec, and every response from every source is checked against a shared schema before a screen sees it. When an upstream shape changes, a test goes red in a pull request, not a tile at 10am.
06 · THE DAY IT BROKEEvery call failing in under a tenth of a second
Two weeks before release, every data call the app made began to fail at once, and fast. A fast failure is a useful clue: it means nothing is being slow, something is missing.
Incident · integration gateway
The serverless function behind the gateway had been replaced by a new one during an infrastructure change. The gateway still pointed at the old host, and that host no longer existed. The app's own code had not changed; a separate build that had not been touched in days failed identically, which settled it. We traced the chain end to end, wrote down the exact fix for the infrastructure owner, and the API was rebuilt on the new function that afternoon with its own key. Every endpoint was back the same day.
The lasting change was to how we test. The team now replays the exact upstream call a screen makes before trusting the screen, and each environment's gateway is written down in one place: local development and previews read the dev gateway, production reads the prod gateway, and the two are never crossed. The next time the chain breaks, it is a five-minute diagnosis rather than an afternoon.
A fast failure is a gift. Something slow is struggling. Something fast is gone.
07 · THE SEVEN WEEKSThree engineers, six releases
- WEEK 1Repository created. First detail pages and overview.
- WEEK 3Workspace, error reporting and the first promotion to production.
- WEEK 3Dark tablet redesign, the content API client, weather, live visitors and the phone layout. Then the gateway outage, found and fixed the same day, and the runsheet and FAQs go live.
- WEEK 4Icon set, cache warming, instant reloads, app icon, UI theme. Promoted to production.
- WEEK 5VIPs from the content API, previews pinned to dev and production to prod, configurable home tiles, production hardening.
- WEEK 6Event filters and a content API update.
- WEEK 7Facility issue groups, form titles, then staff sign-in and sensors by name. Released after review.
All of it went through the same path: a test branch, a reviewed pull request, then production. Nobody pushes straight to production. Type checks, the full test suite and a build run locally in Docker before a change reaches CI, which keeps the hosted build bill small without skipping a single gate.
Three people did this. One engineer started the project and laid the visual groundwork: the first pages, the type, the icon set. Another led the content API integration, events, VIPs, facilities and forms, and owned every promotion to production. I led the kiosk experience, the gateway integration and the incident, the runsheet, caching, the visitor sensors and staff sign-in.
08 · THE PROOFWhat the design guarantees, and what is not proven
What the app commits to, in a form that can be checked rather than asserted.
- Every screen reads one named source of record at request time, and the app keeps no copy of anyone's data
- No source credential reaches the device; every upstream call is made by the app's server
- The desk tablets on the venue network never see a sign-in, and every other request carries a verified work identity
- The door fails closed in test and production: a missing or broken rule lets nobody in
- Visitor counters are found by name, so a replaced sensor is a configuration change, not a release
- An upstream shape change fails a test in a pull request before it can reach a screen
- Previews can never read production data, because each environment is pinned to its own gateway
And the edges, because a write-up that does not name its unknowns should not be trusted.
- The app has not yet carried peak-season load. The busiest weeks of the year are the first real test of the event screens and the warmer
- We measure delivery, not adoption. There is no record yet of which tiles the desk actually uses, or when
- The gateway is a single point of failure for the operations screens. The outage was diagnosed fast, but nothing yet tells us before the desk does