Theo Ferguson

Projects

Integration Health Dashboard

A self-hosted, general-purpose observability dashboard for third-party integrations — a minimal, AI-assisted Sentry or Datadog that any application can report to. Applications send events with an API key, either over a plain POST /api/ingest endpoint or through the published @theof/ihd-sdk npm package; integration and event-type names are free-form, so nothing is hardcoded to a particular domain. The dashboard shows per-integration health at a glance — healthy, degraded, or down — with success rates, a recent-event stream, and trend sparklines drawn from reported metrics, plus a paginated All Events view that is filterable, searchable, sortable, and exportable to CSV, and Datadog-style saved queries rendered as time-series graphs with predicates over metrics, tags, payload fields, severity, and environment. Any failure can be classified on demand by AI (OpenAI gpt-4o-mini) into a category, severity, root cause, and suggested fix, which engineers then acknowledge, resolve, or reopen: detection stays deterministic, the AI only suggests, and a human decides. It is multi-tenant, with GitHub OAuth sign-in, org-scoped projects, and member and admin roles. A companion project, the Integrations Host App, exercises it end to end with live weather, NYT, and NYC campaign-finance integrations emitting real events. Built with React, Vite, TypeScript and Tailwind on the frontend, Node and Express on the backend, with SQLite on a Fly volume, and deployed on Fly.io. Github: https://github.com/theoferguson/integration-health-dashboard

Integrations Host App

A dashboard of four live third-party API integrations — National Weather Service alerts, NYC Campaign Finance Board contribution filings, and NYT Top Stories and Bestsellers — each fetched, normalized, and stored on its own hourly, daily, or weekly schedule. It doubles as the real-world reporter for the Integration Health Dashboard: every scheduled refresh reports its own success or failure into IHD through the @theof/ihd-sdk package, the same way a customer application reports into Sentry or Datadog. Built with React, Vite, TypeScript and Tailwind on the frontend and Node and Express on the backend, with SQLite storage, an in-process node-cron scheduler that keeps the writer and reader on the same database instance, and GitHub OAuth admin auth gating manual refreshes and a raw pre-normalization payload viewer. The NYC campaign finance adapter streams the Campaign Finance Board's ~50 MB bulk CSVs rather than buffering them, discovers election cycles from the data library at runtime, and uses ETag/Last-Modified signatures to skip unchanged downloads — all necessary to fit inside a 256 MB Fly.io VM. Deployed on Fly.io. Github: https://github.com/theoferguson/integrations-host-app

Streamlist

Your Letterboxd watchlist, finally streamable. StreamList reads a Letterboxd account's public RSS watchlist feed, checks which of those films are actually streaming right now, and filters the result down to the services you already pay for — turning a long aspirational list into a short one you can press play on tonight. A separate recommendations tab uses Claude to suggest taste-matched films based on what is already on the watchlist. React 18 with Redux Toolkit and Vite on the frontend, Node and Express behind it, streaming availability from the Movie of the Night API, and an in-memory response cache to stay comfortably inside its free tier. The streaming provider is deliberately isolated behind a single route module, so swapping it for Watchmode or JustWatch touches one file and leaves the store and UI untouched. Deployed on Fly.io. Github: https://github.com/theoferguson/streamlist

Portfolio Site

The site you are reading this on, built as its own project rather than from a template. A Django REST Framework backend exposes a paginated, public read-only API — writes require an admin session — and a Qwik and Tailwind frontend server-renders each page and fetches that API at request time. The practical consequence is that projects and blog posts are written through the Django admin and appear on the live site immediately, with no rebuild or redeploy in between. The contact form is hardened against the usual drive-by spam with Google reCAPTCHA verification, a honeypot field, and per-IP rate limiting, and every submission is stored with its referrer for later triage. The backend runs on Fly.io against Postgres and serves uploaded media; the frontend deploys separately to Cloudflare Pages behind theojohnferguson.com. Docker Compose brings the whole stack up locally in one command.

NYC Campaign Finance

A full-stack platform for analyzing roughly 400,000 NYC campaign finance donor records. The hard problem is identity, not scale: the same person appears across filings under dozens of spellings of their name, address, employer, and occupation, so nothing useful can be counted until those variants are collapsed. The pipeline runs entity resolution over the raw filings using fuzzy and phonetic matching to merge them into single donors, then works on the resolved graph — detecting donation communities with Louvain community detection over shared giving patterns, and flagging potentially coordinated activity through temporal burst detection, where a cluster of donors gives to the same recipient in an unusually tight window. The architecture is split by language strength: a React and TypeScript frontend, an Express and Knex API, BullMQ workers on Redis to run resolution jobs that take far too long for a request cycle, and a separate FastAPI service in Python carrying the matching and graph work with NetworkX, python-louvain, jellyfish, and thefuzz. PostgreSQL with the pg_trgm extension backs fuzzy search across the whole donor set.

Polybot

A Python trading bot for Polymarket prediction markets, with three modes: paper for validating a strategy against simulated fills, live for real trading through the Polymarket CLOB API, and copy for tracking chosen wallets and replicating their trades at proportional size. The core strategy hunts cheap tail bets priced between 2 and 12 percent, on the thesis that these markets systematically underprice genuine tail risk in political and geopolitical events, while filtering out sports markets, anything expiring within a day, and any book with a spread wider than 10 percent. The risk rules are deliberately hardcoded rather than tunable per trade — a fixed unit size, hard caps on total deployed capital, on any single position, and on aggregate exposure to correlated themes, plus a standing prohibition on averaging down — because the failure mode of a bot like this is not a bad model but a loosened limit. Exits ladder out in stages as a position moves toward multiples of its entry price, with a forced reduction before expiry.

Scabble App

A Scrabble-style dictionary for words that don't exist. Type in any word or phrase — especially complete nonsense — and it returns a straight-faced, entirely fabricated definition written in the register of a real dictionary entry. Underneath the joke it is an exercise in making an LLM feature cheap enough to leave running indefinitely: it uses gpt-4o-mini capped at 300 tokens per response, caches every definition for seven days so repeat lookups never reach the API, and rate-limits to 50 requests per day per IP, which holds the bill to a few cents a month at a thousand requests. Django 5.2 served by Gunicorn with WhiteNoise for static files, SQLite for usage tracking, and an admin dashboard for request analytics. Deployed on Fly.io. Github: https://github.com/theoferguson/scabble-app