Projects

Things I've built and run, mostly on my own hardware, all in production.

Status tiles for the media and monitoring containers on the NAS and NUC (illustration)
live · homelab

Homelab media and monitoring platform

A self-hosted media stack on a Synology NAS and an Intel NUC: Plex with hardware transcoding, Sonarr and Radarr, SABnzbd, Seerr and Requestrr for requests through Discord, Tautulli and Organizr on top. Monitored by Uptime Kuma, container updates through WUD, and n8n workflows that scrape Docker logs, group errors into incidents, and post a triage digest to Discord, with Discord bots for restarts behind a user-ID auth gate and Cloudflare Tunnel exposing only what needs exposing.

  • Docker
  • Plex
  • Sonarr / Radarr
  • n8n
  • Discord
  • Uptime Kuma
  • Cloudflare Tunnel
2024 – present
Insulin dashboard in demo mode with synthetic data: current glucose, time in range and a 6-hour trend chart
live · health data

Insulin dashboard

A self-hosted insulin and glucose dashboard for managing type 1 diabetes. A Python API on Postgres ingests Dexcom CGM readings every minute, pulls pump doses, basal states, and automation modes from Glooko, deduplicates across writers, and backfills nightly; the frontend shows trend charts at multiple zoom levels, dose calculation with correction targets, insulin-per-day totals, estimated insulin on board, and an insights view, hardened with CSRF guards, audit logging, a security-event consumer, and a pytest regression suite.

  • Python
  • Postgres
  • Docker
  • Dexcom API
  • Glooko
  • Tailwind
  • Cloudflare
  • Stitch
Garmin widget view with glucose value, trend arrow, delta and sparkline (illustration drawn from its layout code)
shipped · health data

Garmin glucose widget

A Connect IQ glance and widget for the Garmin Venu 4 that shows current glucose, trend arrow, delta, and staleness, with a swipeable multi-timeframe sparkline chart. Pulls from the insulin API over header-token auth, with live refresh and a pre-flash verification gate. Redesigned in Stitch after a first working version.

  • Monkey C
  • Garmin Connect IQ
  • REST
  • Stitch
  • Open source
Three 32x8 LED clock faces rendered by the firmware: unicorn, big text, and graph with value
live · embedded

TC001 glucose clock firmware

Custom firmware for the Ulanzi TC001 pixel LED clock, built on the open-source nightscout-clock project: reads glucose from my insulin API, renders glucose with animated faces (unicorn, dragon, night modes), per-face colour schemes, alert windows and sounds, a stale and no-data indicator, and a rebuilt web settings page with live save and config import. Runs on three clocks on the LAN; 12 pull requests merged upstream so far (alert windows and sounds, a daily schedule, the settings page, settings backup), with 12 more in review, including the new faces and a live preview drawn by the firmware's own code.

  • C++
  • ESP32
  • PlatformIO
  • LittleFS
  • Web UI
  • Open source
Two 52x16 Pixbar frames from the renderer tests: a large value with a rising arrow, and a graph with the value
in progress · embedded

TC002 Pixbar firmware and customisation

Bringing the glucose clock to a new platform: the Ulanzi TC002 Pixbar, a 52x16 LED panel running embedded Linux on a Sigmastar ARM chip rather than an ESP32. Reverse-engineered the device over HTTP and adb, captured goldens of the stock app and containerised a control daemon, then built a glucose app on Stipple, the open-source TC002 firmware: six faces that render byte-identical to the reference corpus, Nightscout read directly on the device, a glucose mode the knob moves through, and a configurable speaker alarm, on a vendor-supplied development unit.

  • Embedded Linux
  • ARM
  • Reverse engineering
  • Docker
  • Stipple
  • Open source
Diagram: investigator agents look, the owner approves, an operator agent makes one change, then it is verified; nine skills and tool-level limits underneath
live · ai / claude code

Claude Code agents and skills

Four subagents and nine skills built around one rule: looking is kept separate from touching. Two investigators (homelab-investigator, kb-researcher) are read-only by their tool list, not by instruction. One operator makes exactly the change I approved and proves it applied, through a restricted key that runs one action on one stack and never gets a shell. An independent reviewer never sees my theory of the bug, so it can't just agree with me. The nine skills load procedure deterministically, because memory gets skipped: deploying firmware to the glucose clocks without bricking them, reviewing code against the upstream maintainer's 20 written rules, auditing the knowledge vault for drift (210 findings on its first run), and a pre-push scanner that matches secrets by their live values and tests every pattern on a planted fixture first. Two of its patterns failed that self-test on day one. I also security-scanned the 13 third-party skills I installed.

next (planned): an eval harness over 511 human-labelled triage decisions, and a prompt-injection test suite against the six MCP servers.

  • Claude Code
  • Subagents
  • Skills
  • MCP
  • Security
  • Evals (planned)
Sep 2026 – present
Architecture diagram: Claude Code connected to six MCP servers, none on the public internet
live · ai / mcp

Homelab MCP server suite

Six Model Context Protocol servers that let Claude operate the homelab, and none of them is reachable from the public internet. docker-control deploys, restarts and rebuilds through a socket proxy, only for an allowlist of 17 containers, with no exec and no volume or network management. postgres-mcp has 28 tools: read-only SQL, container observability, the error board and dosing tools. knowledge-mcp, the first one (May 2026), searches a ~300-note vault of runbooks and journals. I wrote my own zero-dependency Discord server rather than hand a bot token to an unaudited npm package. A Stitch shim prunes oversized design responses, and a read-only SQL server covers service-request data at work. In August I took every public hostname and gateway down; everything is now LAN-only or local stdio. Two more were retired, one of them before it was built, because something better came along.

  • MCP
  • Python
  • FastMCP
  • Docker
  • Security
  • Claude Code
2026