Case study · Theodore Roosevelt Presidential Library · 2026
labs.trlibrary.com: Twenty-Odd Production Tools, Zero Servers
A new institution with a two-person communications shop and no engineering team. Every tool a vendor would have built cost $40,000 and an annual contract. So we built them instead — as static files on GitHub Pages, with scheduled jobs doing any work that needs fresh data. There is no backend anywhere in the portfolio, and no API key has ever reached a browser.
The brief
A presidential library opening in 2026 needs roughly the same digital surface area as any modern cultural institution: trip planning, collections search, ticketing signals, donor recognition, review monitoring, email production, quizzes, maps, timelines. The standard route is to buy each one from a vendor, at somewhere between $15,000 and $60,000 a piece, plus a recurring licence and a support queue.
We had a communications team of two and no engineers. The constraint turned out to be the architecture.
The pattern
Every tool follows the same shape, and the shape is the whole idea:
- The product is static files served from GitHub Pages on a
*.labs.trlibrary.comsubdomain. - Anything needing fresh data is a scheduled GitHub Action that fetches, transforms, and commits the result as JSON. The site reads a file. It never calls an API at runtime.
- Credentials live in repository secrets, used only inside Actions. Nothing sensitive is ever shipped to a browser.
- Git is the database. Every number on screen is also a line in version history, which means every change is diffable, attributable and reversible.
The unlock is realizing how much institutional data changes daily rather than per request. Opening hours, review counts, ticket availability, a press portal, a social calendar — none of that needs a live query. A cron job and a committed JSON file serve it perfectly, for nothing, with no uptime risk and no 3 a.m. page.
Two jobs that are really CORS proxies
My favourite pieces of the whole portfolio are the two places where a scheduled Action exists purely because a browser isn’t allowed to do the thing.
HoursEmbed puts the Library restaurant’s opening hours on a Squarespace site. The hours live in Drupal on trlibrary.com, which blocks cross-origin requests — so the browser can’t read them. A scheduled job scrapes the page server-side, where the block doesn’t apply, and commits static JSON. Its failure design is the best one-liner in the repo set: if parsing fails, the script exits non-zero and the last good file stays published. A layout change upstream degrades to stale-but-correct, never to blank.
AcquiaDAM-Photo-Embed renders the press portal as native gallery tiles. It downloads each thumbnail rather than hot-linking, because Acquia serves previews through pre-signed URLs that expire — so hot-linked images 403 intermittently. Same principle: do the impossible-in-a-browser part on a schedule, ship the result as a file.
Five worth describing
Trip Planner — deliberately not AI
trip.labs.trlibrary.com is a ten-step wizard that builds a Badlands itinerary. It leads with desire rather than logistics: what do you want to see and do is step one; where you’re travelling from is step six.
In a portfolio full of AI work, the interesting claim is the opposite one: “It’s a rule-based wizard — deterministic, no AI, nothing to hallucinate.” It schedules against real pace budgets, places time-anchored events only on days they actually run, and puts meals inside real opening hours. Stops within about 110 miles fold into the Medora block as day trips; farther ones become en-route legs with gateway overnights. It recommends open-jaw airports and greys out rental companies that would strand your car.
Its data integrity system deserves the credit, though. A weekly Action scrapes five regional sources, and on failure preserves prior events and opens a GitHub issue — one broken scrape never empties the planner. A second job checks every booking URL for rot and diffs advertised show seasons, but never edits data: automation watches the hours and tells us when to look; a person makes the change.
TRC Widget — and the thing the graph revealed
trc.labs.trlibrary.com makes 139,714 archival items across 52 partner institutions searchable. The problem statement is exact: the Theodore Roosevelt Center’s advanced search exposes seven facets with no autocomplete on any of them, over a controlled Library of Congress vocabulary — so a visitor typing “henry cabot lodge” gets zero results while “lodge” surfaces “Blodgett.” The vocabulary is excellent; the door into it is the problem.
The fix caches the taxonomy as static JSON on a weekly schedule, so facet searches cost the Center’s server nothing and their flaky full-text endpoint is never in the critical path.
Then the relationship graph turned up something genuinely interesting: Roosevelt sits on 51% of all edges. He is connected to everything, so he obscures everything. A second precomputed layout removes his direct links — and the real communities appear: the Army command clustering around Corbin and MacArthur, the White House staff around Loeb and Cortelyou. That’s the six degrees, and it only appears once the sun is out of the frame.
The repo also records a reversal I like: the first version hand-rolled its force layout to stay dependency-free. That was the wrong call — a graph layout is exactly the thing not to hand-roll, and the result looked it.
Medora Weather — built in a day
weather.labs.trlibrary.com is the thesis in miniature: only the scheduled job ever calls the National Weather Service. The widget and images are static files, so no amount of visitor traffic touches the government API.
It ships two outputs — a JavaScript widget and eight hotlinkable PNG/SVG variants — which is what lets non-technical staff drop weather into an email, a Drupal block or a signage screen without asking a developer. If NWS is unreachable it falls back to the last published forecast, flags it stale, and shows a “data delayed” note rather than going blank. Active alerts tighten polling from fifteen minutes to five. The README keeps the honest caveat: this is an ambient display, not a life-safety alerting channel.
Benefactor Kiosk — the v1 that was too clever
Donor recognition on a 60-inch 4K touchscreen, running unattended twelve-plus hours a day. Roughly 5,466 recognized records across 13 societies — with Founding Member and Supporter alone accounting for about 4,874 of them, which is exactly why the interaction model mattered.
The version history is the lesson. v1 computed donor society from dollar thresholds. The real data didn’t work that way — each donor already carried an assigned society value, and the kiosk should read that field rather than infer it. v1 also built a bespoke 4K on-screen keyboard with diacritic normalization; v2 deleted it in favour of a level selector and an A–Z index. Both times the answer was less cleverness.
It displays no dollar amounts — recognition by tier only, which is standard donor-wall practice and a deliberate privacy decision. And it has the engineering you only write for kiosks: burn-in mitigation, and any uncaught error returns the kiosk to Attract mode rather than showing an error screen.
Rough Rider — the one that’s a game
Roosevelt’s life from 1858 to 1919 as ten platformer chapters, each ending in its own mini-game and a learning recap. Forty fact achievements, twenty historically-motivated enemy types, and characters drawn entirely in code — TR visibly ages and changes costume across all ten chapters. Every sound is synthesized at runtime with Web Audio; there are no audio files at all, including ten chiptune themes.
The serious engineering is the mobile work: touch controls that build nothing on desktop and feed the same key handlers, and iOS Safari’s 100vh-behind-the-toolbars problem solved by measuring the visual viewport and letterboxing a 16:9 box inside the actually-visible area.
Content came from the Library’s own digitized book collection with dates and quotations verified against source texts. One editorial call worth repeating: Roosevelt’s death is placed in the closing legacy screen rather than inside the Amazon chapter, to keep each chapter period-accurate.
The rest of the shelf
The other tools, each a scheduled job and a static file:
- Reviews — daily collection across Google, TripAdvisor, Yelp and Facebook, model-classified into a hand-authored theme vocabulary, with response triage and a screened pull-quote widget. A new theme only enters the vocabulary after three reviews over at least fourteen days.
- Ticketing Widgets — six availability widgets on a fifteen-minute refresh. Risk is modelled as supply against expected demand for that weekday, because percent-sold alone is misleading: a day can sit at 60% the night before and still sell out from walk-ups.
- Quizzes — 85 fifteen-question quizzes, with live head-to-head play running peer-to-peer over WebRTC. The README explains why: GitHub Pages is static hosting — there is nothing on it that can introduce two browsers to each other.
- Campus Map — a 3D model taken from a 338 MB source file to a 7.7 MB delivered GLB, with markers that hide when a berm blocks the line of sight, via a precomputed heightfield rather than a raycast engine.
- Family Tree — 46 people across six generations. The deploy renders the whole tree in a headless DOM to confirm nobody overlaps before publishing; a broken build never reaches the domain.
- Timeline — TR’s life against world events, in two files with zero dependencies.
- Photo Gallery — 926 MB of originals become 5.6 MB of thumbnails; the build only processes what it hasn’t already built.
- Social Calendar — a read-only public view of the Hootsuite schedule. Posts appear on time without a rebuild, because the page ships future items and holds each back until its send time.
- Digital Invite — animated envelope invitations with no backend and no JavaScript dependencies.
- Newsletter Builder — a browser-only email builder whose saved HTML file is its own project format.
- Link Checker — a weekly crawl of every trlibrary.com page for broken links and spelling, published as a triage report.
- Anniversaries — five-year milestones in TR’s life, for commemoration planning.
- Elkhorn Panoramas — 360° views of the Elkhorn Ranch ruins, in seven near-identical files of about 750 bytes each, because a router would have been more code than the thing itself.
What it cost, honestly
Hosting is genuinely zero. Compute isn’t quite: the Reviews pipeline pays for a scraping service and a small language model, which together run to a few dollars a year, and the ticketing widgets sit on an API the Library already licenses. The claim is “no servers and no hosting bill,” not “free.”
One tool is also honestly a prototype rather than a product: the Living Building Challenge dashboard currently runs on demo data while the building-management integration is negotiated. It is on this page because its README states the constraint everything else here obeys: never put an API token in a public page — route it through a scheduled job that writes a static file instead.
What I’d carry forward
- Ask whether the data changes per request or per day. Almost all institutional data is the latter, and the latter doesn’t need a server.
- Scheduled jobs are a legitimate backend. They also make excellent CORS proxies, which is the trick that unlocks the awkward integrations.
- Design the failure before the feature. The best line in the portfolio is “the last good file stays published.”
- Automation should raise a hand, not make the call. The link-rot checker opens an issue; it never edits the data.
- Delete the clever thing. Twice on the kiosk, the right answer was less engineering, not more.
- Constraints beat budgets. No servers meant no procurement, no vendor, no annual contract, and nothing that can go down at 3 a.m.
Why it mattered
A nonprofit with no engineering team can operate every one of these indefinitely, because there is nothing to operate.
Twenty-plus tools that would have cost several hundred thousand dollars to procure, running for the price of a domain name — and maintainable by whoever comes next, because the entire stack is files in a repository.
Source: the Theodore Roosevelt Presidential Library GitHub organization. Related: AI at the Library · the Google Ad Grant optimizer