How the paper is built

How it’s built

A field guide to The Lexington Times

A searchable AI transcript archive of every LFUCG meeting. A machine-readable news feed with MCP servers built for AI agents. A real-time 3D map of fire calls, flood gauges, and aircraft over Fayette County. All glued together by open-source code, public data, and one very opinionated editor. (An earlier 24/7 AI livestream, LexBot, has been retired — the meeting archive, feeds engine, and situation map carry the work forward.)

What you’re looking at

The Lexington Times is a small newsroom bolted onto a set of civic-data machines. Here are the three that sit behind the front page — with a live 3D map and one retired experiment further down.

01

The paper

lexingtonky.news

WordPress. Original reporting, commentary, and meeting agendas, plus AI-summarized wire copy that the feeds engine publishes straight in through the WordPress REST API. SEO via Yoast, stats via Jetpack, cached behind Cloudflare. The human-readable front door.

02

The feeds engine

feeds.lexingtonky.news

Node + Express. Scrapes public government and news sources, rewrites them with Claude, and stores each story as JSON. Serves RSS, sitemaps, social-card images, full-text search, a Markdown / JSON / llms.txt version of every article, and an MCP server so AI agents can query it directly.

03

The meeting archive

meetings.lexingtonky.news

Civic Memory. A Python pipeline turns every LFUCG council and commission meeting into a searchable, timestamped transcript with a plain-language summary, a RAG question-and-answer endpoint, and its own MCP server. The same pipeline runs for Paris, Kentucky at civicmemory.news.

The stack

Runtime Node 20 + TypeScript (feeds, maps API) · Python 3.10+ (meeting pipeline) · React + Vite (situation map)
Storage Article JSON on disk · ChromaDB + SQLite FTS5 for meeting search & RAG · S3 for backups and static hosting
Writers Anthropic Claude — Opus, Sonnet & Haiku · OpenAI GPT-4o for meeting fact extraction · Whisper for transcription
Agent access Model Context Protocol servers on feeds & meetings · llms.txt · open-CORS JSON
Maps Cesium + Google Photorealistic 3D Tiles · a stateless Fastify overlay API · MapLibre + Puppeteer for social cards
Host AWS Lightsail (WordPress, meeting boxes, maps API), EC2 (feeds), S3 + CloudFront (maps & SPAs), Cloudflare (DNS / CDN / WAF)

Public data sources

Everything below is either public record, a licensed re-publisher, or data we generate ourselves. No scraping of paid sources. No social-media private APIs. No PII.

LFUCGGranicus agendas + live meetings, events API, traffic-camera ArcGIS, council districts, construction ArcGIS, parcels, parks + facilities
Weather & hazardsNWS alerts (Louisville WFO), USGS flood gauges, SPC outlook, drought monitor, FEMA county-level declarations
Public safetyLFD incident dispatch (HTML scrape), CrimeScape (LPD redistributor), jail roster, the Flock / ALPR camera map
Transportation & skyKYTC traffic cameras, LFUCG construction, snow-plow GPS, live aircraft over Fayette via ADS-B, satellite passes via CelesTrak
Records & moneyKentucky Legislature (LRC), US DOJ Eastern District of KY, US Attorney’s Office, Kentucky AG, FRED (economic), Fayette PVA property sales, KREF campaign finance
SportsESPN (UK Kentucky Wildcats), Bengals via AP wire

The meeting pipeline

LFUCG publishes council and commission meetings on Granicus with scanned agenda PDFs and auto-generated captions. That’s where our civic coverage starts — but the raw feed isn’t something you can skim. A dedicated Python pipeline turns each clip into something searchable.

1IngestScrapes Granicus for new meeting clips, downloads the video, pulls the scanned agenda PDF (OCR’d via tesseract), and captures captions + timestamps.
2Transcribeffmpeg extracts audio; Whisper transcribes; segment-level timestamps are preserved so the frontend can deep-link into the video at any moment.
3Fact pass (GPT-4o)Extracts structured facts — votes, dollar amounts, names, agenda items, timestamps — into extracted_facts.json. Pure structured extraction, no narrative.
4Narrative pass (Claude Sonnet)Writes a section-by-section summary grounded in the extracted facts, with [timestamp: MM:SS] markers for clickable video seeking.
5ServeStatic JSON + a React app for browsing the archive, plus a RAG question-and-answer endpoint and an MCP server — so a reader or an AI agent can ask the archive a question and get a cited answer that deep-links into the video.

The whole thing is open source — code, docs, and the Granicus reverse-engineering notes (granicus.md) that made timestamp deep-linking possible.

paul-codes-1 / fuzzy-potato

The situation map

A photoreal 3D globe of Fayette County that pulls the same public data as the paper and plots it in space and time. Every layer below is fed by a stateless overlay API and rendered in the browser with Cesium over Google’s 3D Tiles — no game engine, no login.

LiveFire & EMS callsActive LFD dispatches dropped on the map as they come in, collapsed by location.
CamsTraffic camerasKYTC and LFUCG camera feeds pinned to their real intersections.
USGSFlood gaugesReal-time creek and river levels, colored by flood stage.
ADS-BAircraftPlanes and helicopters over Fayette, dead-reckoned between polls so they glide at true ground speed.
TLESatellitesISS and other tracked objects propagated live from CelesTrak orbital elements.
LPDCrime heatmapReported incidents aggregated to a heat surface by district and type.
PVAReal-estate salesThe last three months of Fayette PVA sales, geocoded and colored by price tier.
ALPRFlock camerasEvery mapped automated license-plate reader in the network.
GISBoundariesCouncil districts, voting precincts, school-board districts, parcels, and greenways.
PresetsVisual modesNight-vision, thermal, CRT, noir and other looks for the same scene.
Open the live map

The livestream (2025–2026), retired

For about a year, The Lexington Times ran LexBot — a 24/7 civic-radio livestream on YouTube. An orchestrator drove OBS through rotating traffic cams, scanner ambience, NWS alerts, and real-time LFD incidents, while dual AI hosts (scripted by Claude, voiced by ElevenLabs) read the day’s news.

The formats were deliberately unhurried — a civic radio feed, not a scroll: hourly news briefs, a weekday morning briefing and evening wrap, weather cut-ins, listener “Ask Lex” questions fact-checked against a live web search, per-meeting recaps chosen by the RAG archive, an aggregated crime blotter, and lighter turns like a pet of the day and a neighborhood of the week.

The broadcast was retired in mid-2026. Its scripts remain in the meeting archive as historical transcripts, and the code stays in the repo — but the meeting archive, feeds engine, and situation map now carry the work the livestream used to do.

Guardrails

  • AI disclosure — every AI-summarized article and meeting summary carries a visible note that it was machine-generated, which model wrote it, and when.
  • Fair use — aggregated headlines carry a title + ≤200-character summary with the canonical source link. Everything else is paraphrased and cited; only licensed re-publishers run verbatim.
  • Grounded answers — the meeting Q&A runs at low temperature behind a similarity gate with a per-claim citation check. If it isn’t in a transcript, it doesn’t go in the answer.
  • Fresh facts — questions that need current information route through Anthropic’s web_search tool, geo-biased to Lexington, KY, rather than guessing.
  • Circuit breakers — every poller opens after 3 consecutive failures and stays open 5 minutes before retry. No silent infinite-retry.
  • Corrections — a public corrections log and a one-click way to flag an error, linked from every footer.
  • Aggregated crime — crime data is shown by council district and type. We don’t name individuals or addresses, even when the public data set would allow it.

Humans in the loop

There is a human behind all of this. Most of it was built, is operated, and is continuously debugged by Paul Oliva, with help from friends and open-source contributors. The machines are fast: AI drafts the wire summaries and the meeting recaps. The judgment is not automated — a human decides what gets promoted to the paper, writes the original reporting and the Glaze and Off-Beat columns, answers tips, and owns every correction.

The paper will only ever be as opinionated as the humans behind it.

Questions we get

What’s automated, and what isn’t?

The plumbing is automated: scraping public sources, summarizing wire stories, transcribing and summarizing meetings, and refreshing the map all run on their own. The editorial calls are not. A person decides which summaries get promoted onto the paper, writes the original reporting and the columns, and signs off on corrections. Think of the AI as a very fast wire desk, and the human as the editor who still has to say yes.

How do you stop the AI from making things up?

Three layers. First, grounding: meeting answers are built only from facts extracted out of the transcript — low temperature, a similarity gate, and a citation check on each claim — so if it isn’t in the clip, it isn’t in the answer. Second, fresh lookups: questions that need current information route through Anthropic’s web_search tool, geo-biased to Lexington, so “when did that park open” pulls a real source instead of a guess. Third, disclosure: every machine-written article and summary is labeled as such, with the model and time, so nothing hides behind a byline it didn’t earn.

Can I contribute?

Yes — the meeting pipeline is open source at paul-codes-1/fuzzy-potato, and we accept tips, corrections, and pitches at [email protected].

This is pretty cool. Can we hire Paul?
Paul Oliva

Paul M. Oliva — Senior Full Stack Engineer

Yes — he is open to new opportunities. Paul built the paper, LexBot, the meeting pipeline, and most of the civic-data plumbing on this site on top of a full-time engineering job — shipping features end-to-end across a Next.js frontend and a Node/TypeScript API for a high-volume ticketing platform.

What you’re hiring, in short:

  • FrontendReact, Next.js, TypeScript, Redux, Tailwind
  • BackendNode.js, Express, REST, GraphQL, tRPC, FastAPI
  • AI / LLMOpenAI, Claude, RAG, ChromaDB, Tesseract OCR
  • CloudAWS (Lambda, S3, RDS, SNS/SQS), Docker, GitHub Actions
  • DataPostgreSQL, MySQL, DynamoDB, MongoDB, Redis
  • PaymentsAdyen, Stripe, PayPal
  • ObservabilityDatadog, Sentry, CloudWatch
pauloliva.com

The code

Public data begets public code. Much of the infrastructure powering this newsroom is open source and happily borrowed from:

  • WordPress for the front-of-house, with a custom feeds→WP REST publishing bridge
  • Node + Express + TypeScript for the feeds engine, and Fastify for the maps API
  • Python + FastAPI + Whisper + ChromaDB for the meeting pipeline and Civic Memory RAG
  • Cesium + Google 3D Tiles for the situation map
  • MapLibre GL JS + Puppeteer + sharp for civic maps and social-card images
  • Model Context Protocol servers so AI agents can query the archives directly
  • pino, node-cron, cheerio, fast-xml-parser, Zod
  • Anthropic Claude & OpenAI for the AI pieces

Back to the paper: lexingtonky.news. Or read the editorial stance: about the paper.

Founded & published by