things i've built

Projects

A build-log of what I'm making — shipped, half-built, and occasionally on fire. Mostly in the home lab, mostly at 1am. Links land as things go live.

quipu

building2026

Shared memory for AI agents, across every machine you own

A knowledge store agents query over MCP — code symbols, git history, issues, and verified concept links fused into one cited graph. Postgres + pgvector underneath.

  • Jun 2026Per-caller access control: you only retrieve from repos your git account can already see. The server decides — a token never gets to vouch for itself.
  • Jun 2026Graduated from search to an actual knowledge graph: structural edges from code and git, semantic edges from a model, one hybrid query that ranks the good stuff last.
  • Jun 2026Outgrew SQLite and moved to Postgres + pgvector. The whole index is a rebuildable projection, so 'migration' meant deleting it and pressing go.
  • May 2026Picked SQLite + plain git-tracked markdown over a graph database — whose makers archived it that October. Dodged a bullet.
aisystem-design

homelab k3s platform

building2026

A single-node Kubernetes cluster that runs my whole house

GitOps all the way down — ArgoCD reconciles everything, Keycloak does SSO, Traefik serves real TLS off one wildcard cert, and nothing is clicked by hand.

  • Jun 2026Real Let's Encrypt certs on every internal service via a DNS-01 wildcard. One cert, zero inbound ports, no more browser scolding me about my own house.
  • Jun 2026Single sign-on across six apps. Hard-won lessons: Go's Alpine resolver can't see '.local', a 16-byte cookie secret means exactly 16, and JSON in a Helm value will ruin your afternoon.
  • May 2026Went full app-of-apps: one root manifest and everything else self-deploys. Even the secrets live in git — sealed, but in git.
  • May 2026Backups that survive a bare-metal wipe. Got tested for real when a node came back from the dead and the restore actually worked. Mild shock.
home-labkubernetes

the-bureau

live2026

An orchestration engine that bosses other AI agents around

Declare a task graph and a swarm of agents picks up the work, hands off, reviews each other, and reports back — each in its own sandbox, all over MCP.

  • Jun 2026Taught workers to commit their half-finished work before I kill them, so a retry resumes instead of starting from zero. Then found the kill path had been a no-op for pods the whole time. Cool, cool, cool.
  • Jun 2026Agents can now be steered mid-task — whisper a course-correction to a stuck one instead of killing it and praying the retry goes better.
  • Jun 2026Moved every agent into its own Kubernetes pod with its own cryptographic identity. 'Agent as a pod' sounds tidy; three days of probe-tuning disagreed.
  • Apr 2026Rebuilt it on a task DAG with a real lifecycle state machine. The naive in-memory version did not survive contact with reality.
aisystem-design

solar geyser controller

live2026

Heating my water with sunlight the inverter didn't know it had

A Home Assistant controller that heats the geysers only from surplus solar. It can't measure 'spare sun', so it tests for it — add load when the battery's full, shed the instant it starts discharging. Never touches mains. Survives load-shedding.

  • Jun 2026Taught it to dump surplus sun into the tanks when the battery hits full — by testing for spare power, not measuring it. The MPPT only reveals the real ceiling once you give it something to do.
  • Jun 2026A 'satisfied' tank kept stealing priority from a cold one, then flip-flopping every second because the signal died the moment I acted on it. Latched it. Lesson: never route on a relay-gated signal without latching first.
  • May 2026Rewrote six tangled automations into one introspectable controller: never both geysers at once (6 kW plus the stove would trip the inverter), heat from battery only, stand down for load-shedding.
home-automationsolar

mini-rack fan controller

live2026

Smart cooling for a 10-inch rack that refuses to roar

An ESP32 + ESPHome controller driving three PWM fans off rack temperature, with a 3D-printed vented back-plate — quiet when idle, loud only when it has to be.

  • Jun 2026The gotcha that ate an evening: 3.3V logic into 12V fans needs more thought than 'wire it up'. Tach reading and the PWM pin reality finished the job.
  • Jun 2026Curve-tuned the fans against rack temp so the thing stays silent until the lab actually warms up. A perforated panel, it turns out, is not a fan mount.
home-automationhardware

github-profile

live2026

A GitHub README that updates itself from my home lab

A terminal-aesthetic profile page wired to live cluster telemetry — tokens burned this year, lab activity, and a nightly arcade game running on my contribution graph.

  • Jun 2026Lost a full day to this: Claude Code exports its metrics as 'delta', and the collector drops delta silently — no error, no log, just vibes. One processor later, real numbers.
  • Jun 2026The README renders whatever arcade game is actually live on the contribution graph, so it can never lie about which one's running.
  • Jun 2026A lab cron pushes fresh stats every six hours. The profile updates itself while I sleep.
home-lab

GPU inference node

live2026

A tiny PC that serves LLMs to the rest of the lab

A Lenovo Tiny with a Blackwell GPU running local models — Ollama plus a LiteLLM gateway, with Grafana watching every token, watt, and degree.

  • Jun 2026GPU finally allocatable to pods. Blackwell needs the open kernel module, and the headless driver ships no nouveau blacklist — so it silently refused to load until I added it by hand.
  • Jun 2026Cycled through four local models in 48 hours chasing the latency-vs-cost sweet spot. The gateway speaks OpenAI's API, so nothing downstream noticed.
  • Jun 2026Full observability: per-model latency, tokens, VRAM, thermals. If it's slow, I know exactly why.
aihome-lab

frigate + hailo vision

live2026

Local camera AI on a Pi, no cloud in sight

Frigate on a Raspberry Pi 5 with a Hailo-8 NPU doing real-time object detection across eight cameras (~14 ms a frame), pushing person and vehicle alerts into Home Assistant with tap-to-play clips.

  • Jun 2026Eight cameras, ~14 ms per frame on the Hailo NPU, zero cloud. Person and vehicle alerts land in Home Assistant with a clip you can tap to play.
  • May 2026Frigate bundles one version of HailoRT; the Pi's kernel driver shipped another, and the exact-match check refused to start. Rebuilt the image to match. Of course the tarball nested itself one directory too deep.
aihome-automation

turing-smart-screen

live2026

Turning a cheap USB LCD into a real status display

A fork that drives a 3.5" USB screen as a YAML-defined page carousel — animated network map, host stats from Prometheus, diff-based rendering so it never flickers.

  • Jun 2026Rebuilt it around a YAML page carousel with diff-based rendering, so the screen updates without flicker. Upstream feature request is open; we'll see.
  • Jun 2026The network map reads the ARP table passively now. The previous version actively scanned the whole subnet and took down the house WiFi. Twice. Lesson learned.
home-labhardware

SARS.TaxCalculator

live2026

South African payroll tax, done by the book

A .NET library that turns gross into net the way SARS actually wants — PAYE, UIF, SDL, ETI, medical credits — across five tax years, behind a fluent API.

  • 2026416 tests, 100% line coverage, and the rounding rules SARS only documents if you squint. It's compliant so you don't have to read the legislation.
  • Mar 2026Shipped to NuGet. Handles mid-year rule changes too — because of course the ETI rate changed in the middle of a tax year.

esphome weather station

live2025

A weather station I can actually reflash from my desk

A DIY weather station on an ESP8266 reading a serial sensor over UART, wired into Home Assistant over MQTT with a hand-written ESPHome component.

  • 2026Migrated it to a proper external ESPHome component (UART, 9600 baud) so the sensor logic lives in version control instead of a copy-pasted lambda.
  • 2026The builder UI can't resolve the device over mDNS from inside Docker, so OTA flashing goes by explicit IP. Small indignity, reliable result.
home-automationhardware

tronxy → klipper

live2025

Dragging a decade-old printer into the Klipper era

A multi-week conversion of an old Tronxy XY2 Pro to Klipper + Moonraker + Mainsail — flashing failures, a nuked bootloader, and finally a board swap that brought it back to life.

  • Nov 2025Swapped the whole control board after a chip-erase nuked the bootloader and a 'serial connection closed' wall refused to budge. It prints again. The second printer was free; the lesson was not.
  • Oct 2025Lesson learned the hard way: identify your exact STM32 variant and crystal before you touch a single jumper. Guess wrong and you lose the bootloader and a weekend.
3d-printinghardware

smart terminal

live2025

A Linux command line that speaks plain English — locally

A terminal assistant that turns natural language into safe system actions through a local model and an MCP + validation layer. The model is the easy part; the guardrails are the project.

  • Jul 2025The whole thing lives or dies on the layer between the model and the kernel — dry-run, validate, apply atomically, log everything. Turns out 'give an LLM root' is a validation problem wearing an AI costume.
  • Jul 2025Targeting a sub-4GB local model so it runs offline with no cloud bill. Embedding a blocking inference binding into an async runtime was its own special afternoon.
aisystem-design

another-json-lib

live2025

JSON in C#, minus the boilerplate

A small .NET library for the JSON chores the framework makes tedious — comparison, merging, diffing, serialization — bundled into one tidy NuGet.

  • 2025Born from copy-pasting the same JSON-merge helper into one project too many. Now it's a package.
dotnetoss