SECTION 0x19·ARCHIVE
The Archive
Every post, newest first. 128 entries.
A 7-day forward lens for the things I already track
I have a morning briefing that summarizes today (weather, todos, calendar, bills, sobriety counter). Useful. But it's only today, and it's spoken once on…
Generating a leather pattern as a tabloid-size PDF
I wanted a coffin-shaped wallet pattern I could print and saddle-stitch. Online templates are mostly rectangles. The interesting work was turning a…
Draining a job queue before redeploying
A long-running service that does work in the background needs a deploy strategy that doesn't drop work on the floor. This is the rule I landed on after…
The repo with the same name was the dead app
I spent a couple of hours one afternoon making changes to what I thought was the current iOS source tree for a side project, running builds, watching…
Hurricane failover for a homelab assistant without paying Bedrock prices
My homelab assistant lives on an LXC in the garage. Hurricane season runs June through November on the Gulf Coast. A two-week power loss takes my whole…
Brand-aware location reminders without a custom geocoder
I wanted to add "remind me when I'm at any Walmart" to a location-reminder feature, alongside the existing "remind me when I get home" pin-based…
A tiny garden sprinkler watcher that only texts when it matters
I turned the garden sprinkler off because rain was forecast for a week. The question is: when do I turn it back on? I do not want to check a weather app…
Three permission dialogs for a working safety app on Android
A safety app I shipped for personal use on Android needs three distinct runtime permissions to actually do its job. Walking a first-time user through…
Gradle OOM on Android builds: just give the JVM more heap
Android build was failing partway through with a Gradle OutOfMemoryError. First few times I assumed it was a flaky CI worker. Then it failed locally too.
When every summer storm is a high-severity alert
A safety-monitoring side project I run paged me at 09:00 with "dangerous weather conditions near you. Conditions: Thunderstorm." It was a routine summer…
Disk full on the staging dir at 01:14 in the morning
I run a small S3-compatible proxy that fans writes across several cloud accounts to dodge per-account upload caps. It went into "all jobs stalled at 0…
Routing 24 silent catches to a real error bus
The "blocked on solo dev constraint" note in my own bug log was wrong. The work wasn't blocked, I just hadn't done it. Enumerating the silent catches in…
Never build new things inside the assistant
I built a small water-tracker for myself: pick a bottle preset, tap a button, get a daily total. First version landed as a few /water/* routes inside the…
A tiny S3 proxy in front of a pool of Drive accounts
I had ~50 TB of media I wanted to back up off-site. Synology HyperBackup can speak S3. Google Drive Workspace gives you generous per-user storage, but…
UI Automation beat my screenshot loop by 17x
I had built a Windows desktop agent around the obvious loop: take a screenshot, send the PNG to a vision LLM, ask where to click, click it, repeat. It…
A dashboard deadlock from missing per-task timeouts
My homelab home page started returning 504 for its /api/dashboard route. Nothing else was obviously broken. The page just hung.
Recovering orphaned jobs after a worker restart
A backup service kept getting into a state where the worker queue showed "running" jobs that nobody was actually working on. The in-memory job queue was…
Hanging the API at scale: SQLite was the wrong default
A custom backup engine I wrote was happily writing tens of thousands of chunk rows to SQLite. Then I bumped concurrency from two backup jobs to ten, and…
Moderate risk alert at midnight, awake at the desk
A side-project safety app generated a moderate-severity risk alert for me at 00:28 local time. I was at my desk, awake, trading messages with two…
Turning years of screenshots into a searchable journal
I have a Mac screenshots folder with thousands of files. Most of them are chat conversations — iMessage, Signal, Teams, email — captured because I wanted…
Why Google Drive eats HyperBackup, and what to do instead
I had 27 TB of stuff on my NAS that I wanted backed up to Google Drive. Synology HyperBackup ran for weeks, made it about a third of the way, and the…
OCRing the live screen was the wrong validator
I built a small nightly QA runner for my personal iOS apps. It builds each app, boots a simulator, installs and launches, takes a screenshot, and OCRs…
The crontab edit that loaded a stale /tmp file
I wanted to bump the cadence of one cron job from every 15 minutes to every 10. Three minutes later, seven of my scheduled jobs were gone — replaced with…
A wall-clock guard so cron jobs don't drift across DST
I wanted a nightly job to fire at exactly 00:00 Central, every night, forever. Debian's vixie-cron doesn't support CRON_TZ, and the host runs in UTC. The…
Date-scoped views on a todo list, with a sensible default
I migrated from Todoist to my own todo app a few weeks ago. The single biggest thing I missed: Todoist's Today / Upcoming views. So I built them.
Five cron jobs sharing one atomic-rename temp file
Five Python pollers, one per messaging source, all on *. Each one writes a shared cursors file to remember where it left off. The dashboard showed…
Never let the test agent grade itself
I have a small nightly QA runner that exercises my personal apps — builds them, drives them, takes screenshots, asserts they actually work. The single…
Driving a Windows VM install with qm sendkey
I needed to slipstream a Windows 11 install across a handful of VMs on Proxmox without sitting in front of a noVNC window pressing Enter for an hour. The…
When a ThinkPad disappears from the LAN after a NIC swap
Swapped a USB-to-Ethernet adapter on a ThinkPad to fix a flaky link. After the swap, the laptop dropped off the network entirely. No reservation hit in…
How to survive AWS toll-free SMS registration
A toll-free SMS number for a personal safety project I run took several denial cycles before AWS approved it. The denials were all about wording in the…
Migrating from Todoist with an idempotent import
Migrated all my active Todoist tasks into my own todo backend. 27 tasks across 6 categories (birthdays, bills, dev backlog, groceries, issues for a…
My per-todo chat was a toy LLM in a tool-capable house
My web todo app has a little chat button on every todo row. The idea: open a todo, ask the assistant "what should I do here?", get a tool-driven answer…
qBittorrent was deleting files before Sonarr could import them
Sonarr's queue kept stalling. Episodes would show up as grabbed, sit in the queue, and then quietly fail to import. Re-grabbing the same release didn't…
Tightening Radarr quality profiles to stop Emby transcode lag
Avengers playback was crawling on Emby. Not network, not the client — the Synology CPU was pinned, fan howling, every play.
A $25 bed occupancy sensor for Home Assistant
I wanted Home Assistant to know whether the bed was occupied, and by whom, so it could drive lighting, HVAC setbacks, and a few "is anyone home"…
Hyper Backup stuck in a scan loop over live LXC subvolumes
My Synology Hyper Backup job to Google Drive kept restarting and never finishing. It would run for hours, get most of the way through the scan, then bail…
qBittorrent's auto-remove racing arr imports
Symptoms looked like the same old "files imported but not really" pattern I'd dealt with before, but the root cause this time was subtler — and lived on…
Ring IR mode breaks face-distance thresholds
I have a small face-identification pipeline running against snapshots from my doorbell. It uses a fixed distance threshold to decide whether a face…
Door unlock on iPhone SOS when Apple won't expose the event
I wanted my front door to unlock automatically when my iPhone or Watch triggered Emergency SOS or Fall Detection. The catch is that Apple does not expose…
Throttling doorbell notifications to the ones that matter
I rolled out face-recognition notifications on my front-door doorbell and got spammed with hundreds of pushes in a few hours. I killed the cron, audited…
Downsampling the way out of a CNN out-of-memory loop
Doorbell camera snapshots come in at 1536x1536. I wanted to run them through a CNN face detector to label visitors. The detector kept getting OOM-killed…
Editing Home Assistant OS config without the SSH add-on
I deployed Home Assistant OS as a VM on Proxmox so I could try a Wi-Fi smart lock. HA proxied through nginx returned HTTP 400 on every request. The fix…
Face recognition on the front door with an active-learning loop
I wanted my front door doorbell to do something more interesting than "someone moved." Specifically: "the dog walker is here," or "an unknown person is…
Spoken morning briefing on an Echo, without writing a skill
I wanted a JARVIS-style morning briefing — calendar, weather, finances, todos, the day's notable anniversaries — spoken out loud on the Echo Studio in…
OneDrive sync was reverting my backend edits
Every few days my backend would lose a recent fix. The change would work, restart the service, work for hours, then vanish. Git showed the old code back…
Route files were losing the immutable bit
I set chattr +i on my backend's main.py after a sync bug kept silently overwriting it. Months later, the iOS app started losing connectivity on a…
Shipping an iOS build headlessly over SSH
I wanted to build and install my iOS app from a remote agent session — no Xcode window open, nobody at the Mac. xcodebuild over SSH refused to codesign…
Backfilling Signal history that the server doesn't have
I wanted my homelab assistant to have access to my Signal history the same way it has my iMessage and email. Signal's whole point is that the server…
A JARVIS-voiced morning briefing on the Echo Studio
I wanted something to talk at me when I get up — weather, todos, the date, sober days, mortgage-due reminder, homelab status. Not when an alarm fires…
Echo Studio silently drops Alexa announcements over 600 characters
I have a morning briefing skill that reads me a short summary of overnight events. It worked fine on the Echo Show in the kitchen and was completely…
manage-bde isn't broken, BitLocker just isn't installed
I had a fleet-status dashboard for some Windows boxes that pulled disk encryption state via manage-bde -status. Several rows were reporting hard errors…
A tiered mail classifier for cheap triage
Once all my mail was flowing through a Lambda, I wanted automatic triage — drop the obvious noise, bucket the rest, only forward the things I'll actually…
Building a TPM-bypass Win11 ISO with offline hive injection
I wanted to PXE-boot a fully unattended Windows 11 25H2 install onto a VM with no TPM, no Secure Boot, and not enough RAM by Microsoft's reckoning. The…
A one-shot CLI so 'add it to my todo list' isn't a research project
Whenever I said "add it to my todo list" to my homelab assistant, it would burn twenty tool calls poking around the codebase trying to remember how todos…
Cutting MX records to SES when your domain loops back to itself
I had cut over the MX records on several personal domains to AWS SES, because I wanted every incoming email going through a Lambda that writes to…
KDP rejected my novel three times for comp titles in the description
I submitted a thriller to KDP in three editions on the same day: Kindle, paperback, hardcover. All three got rejected within a few hours, with identical…
Native iOS todos with an interactive widget
I ship enough native iOS to make my own todo app worth the effort. I already had a backend that powered the web UI; I wanted a SwiftUI app that hit the…
Slipstreaming SSH into a PXE-booted Windows install
PXE booting a Windows 11 install is fine; getting a freshly installed VM to accept SSH from my homelab without me touching it is the actual interesting…
The arr-stack disk-full trap
The home media pipeline went quiet. Nothing new was finishing. CPU on the torrent boxes was high but throughput was zero. Time to actually look at why.
Workspace permission denied when SCP'ing from automation runner
I have a small homelab automation runner that orchestrates jobs across a handful of remote machines. New job, new bug: SCP transfers from the runner to a…
Malware disguised as MKV files on public trackers
My TV import rate had collapsed from about 90 episodes a day to single digits over a five-day stretch. Sonarr's queue was full but nothing was making it…
A 200KB Windows tray app for passive activity check-ins
The macOS companion for a personal safety app I run does one small thing: every five minutes, if the user was active in the last minute, it POSTs a…
Using AppSync as a pub/sub fan-out without any data source
I had two clients — a web app and a native iOS app — both polling my backend every few seconds to learn that an assistant reply had finished. Polling…
Producing an audiobook with Kokoro on a Mac mini
I wrote a thriller novel. I wanted an audiobook of it. The traditional path is ACX with a human narrator, six to twelve months and a few thousand…
A dead WireGuard tunnel stalled every music torrent
Music container in my torrent setup was stuck. 349 torrents, all in metaDL, all at 0 peers, all 0 bytes. The container itself was running, qBittorrent's…
Cutting a full-book TTS render down to one CLI
I have a habit of writing things — long-form, novel-shaped things — and then not being able to listen to them on a walk. The commercial-quality audiobook…
When the Proxmox dashboard disagrees with reality
My homelab status dashboard pulls container/VM state from the Proxmox API. One day it showed three containers as stopped that I was actively connected…
349 torrents and no queueing
The music torrent client had 349 torrents in flight. None of them were finishing. All of them were trying to download at the same time. This is what…
qBittorrent was on the wrong VPN port forward
The music torrent container had 349 torrents and zero of them were doing anything. Every single one was stuck in metaDL with 0 peers, 0 bytes. DHT showed…
Lidarr returns HTTP 500 on PUT /album/monitor mid-add
A note for anyone scripting Lidarr through its API: there's a race in the "add an artist with monitored albums" flow that surfaces as a 500 on the…
An OpenAI-compatible shim so Home Assistant could talk to my agent
I wanted "Hey, $name" voice control around the house using local satellite mics, with the actual brain being my own assistant on the homelab. Home…
I got the push but the chat was empty
Recurring complaint from myself: the assistant pushes a notification that it's done, I open the app, the conversation is there, the assistant bubble is…
Moving off OneDrive onto a Syncthing mesh
I had been sync'ing my ~/code folder through OneDrive for years. It worked until it didn't. The breaking point was Xcode and git both fighting OneDrive…
Cold-start 500s from leftover Composer dev dependencies
A PHP function on Lambda started returning 500 on cold start after a routine deploy. Warm invocations were fine. The error in CloudWatch was specific and…
Bref 2 to 3 and provided.al2 to al2023, the gotchas
AWS sent the usual EOL notice for the provided.al2 Lambda runtime. I had 13 functions on Bref 2 / al2 spread across two serverless projects. The…
When the upstream CDN just isn't reachable from your house
I needed to download Windows 11 25H2 ISO bits via the UUP build flow. Aria2c started fine, pulled the small metadata files (a few hundred KB total), and…
Patching boot.wim with chntpw to no effect
I was building a custom Windows 11 install ISO and trying to do a clever registry tweak inside boot.wim to make in-place upgrade scenarios work the way I…
A keyword router that lied about which mode it was in
My assistant has a cheap classifier that routes short, simple questions to a fast model without tools, and harder questions to a tool-capable model. The…
The CT-is-running-but-the-app-is-dead 502 pattern
Recurring failure mode on the homelab: the assistant's domain returns 502 from every browser, but pct list says the LXC is running and systemctl status…
Reverse engineering a Bluetooth-only irrigation controller
I have an Orbit B-hyve XD irrigation controller. It's Bluetooth-only — no Wi-Fi hub built in. Every existing integration (Home Assistant, pybhyve…
Two flags for one boolean, drifted apart
A user of a side-project reported that the "Set up 2FA in mobile app" link in their settings did nothing — and there was no way to move 2FA to a new…
An on-this-day journal feature from 3,000 Day One entries
I exported 3,060 Day One journal entries as JSON, imported them into DynamoDB, and built an "on this day" widget for a home dashboard. Once it was…
qBittorrent's API rate-limits fresh logins
A dashboard tile of mine that aggregates torrent counts across five qBittorrent instances started intermittently returning 403s. The page would refresh…
USB 2.0 Billboard means the cable is charge-only
Plugged an ESP32-class dev board into the Mac. Instead of a serial port, macOS showed it as a USB 2.0 BILLBOARD device from VLI Inc., vendor ID 0x2109…
A BYOS e-ink dashboard that wakes once a day
I wanted a tiny always-on display in my office that showed the day's useful information and lasted a season on a single battery charge. The constraint…
Counting a word across Signal messages with the macOS keychain
I wanted to count how many times I'd used a specific word across all my outgoing Signal messages. Sounded like five lines of SQL. The interesting part…
Disabled the schedule. Got paged anyway.
I disabled a stuck check-in schedule at 09:05. At 09:33 my phone buzzed with "Chester Frazier hasn't responded to their check-in." The schedule was…
A counter clock as the simplest thing that could possibly work
I needed a public webpage that counts time since a fixed instant. Two days, four hours, big number, mobile-friendly. The interesting part wasn't the page…
Disabling Zebra printer Bidi when PowerShell refuses to help
I write deployment scripts for Zebra ZQ521 mobile printers running CPCL. Two of the standard config knobs — disable bidirectional communication and…
Adding email to the Memex (and a Dynamo pagination gotcha)
A few hours after shipping the Memex over iMessage and Signal, I added email. The interesting part wasn't the feature — it was a DynamoDB pagination bug…
Memex — a queryable second brain over my messages
I wanted to ask my own data questions like "when did I last hear from X?" or "what did we say about that lake house?" and get an actual answer. So I…
Sketching a one-click honeypot installer
Honeypots are useful and almost nobody runs them, because the existing stacks are either too heavy (multi-container threat-intel platforms that need 16…
Decrypting Signal Desktop's database on macOS
I wanted Signal Desktop's message history available for indexing in my personal search. Signal Desktop stores messages in a SQLCipher database, and the…
Five identical schedules, thirteen SMS in ten minutes
09:33 to 09:43 on a Friday morning: thirteen SMS messages, several push notifications, all from my own safety app, all to me. The trigger was five…
Per-day-of-week sleep windows for a safety app
A safety app I run was firing "missed check-in" pre-alerts at 7am on Saturdays. I don't get up at 7am on Saturdays. The fix was moving from one global…
Auditing 22 references to GSIs that don't exist
A pre-release audit pass on a backend I run turned up 22 places where the code passed IndexName: foo-index to a DynamoDB query on a table where foo-index…
Updating a DynamoDB row by the wrong key shape, for a year
A pre-release endpoint sweep on a backend I run turned up two 500s on the check-in respond and snooze endpoints. The cause was old: a composite-key table…
PHP loose comparison turned an empty endpoint into a 500
A backend I run was returning 500s on GET /api.php with no endpoint= query param. Should obviously be a 400. Tracking it down landed me on one of those…
Replacing a binary threshold cliff with a one-hour ramp
The inactivity-monitor cron on a side-project safety app was paging me at 7:01 AM, every morning, with "you haven't checked in." I was asleep. The cause…
A risk email every fifteen minutes about nothing
The risk-monitor cron on a side project I run was emailing me every 15 minutes with "risk factor appeared" or "risk factor resolved" for low-severity…
Why the sleep-window learner kept collapsing to the wrong four hours
The v1 sleep-window learner for a side-project safety app produced a sleep window of 01:00–05:00 for me. My actual sleep is more like 23:00–07:00. The…
Sonarr was dead because of a Cyrillic filename
Sonarr's web UI was unresponsive. systemd said the service was active (running). The process was using 100% of multiple cores. Load average on the…
When Sonarr's UI hangs after a restart, blame the WebSocket cache
I'd just restarted Sonarr to recover from an unrelated issue. The service came back up, API requests responded fine, but the web UI in my browser was…
94% SpO2 is not an emergency
I got a high-severity SMS from my own safety app: "may be at risk. Factors: Low blood oxygen: 94%." 94% is normal. Consumer pulse oximeters have a ±2%…
Teaching the arrs to skip Russian and very long releases
Sonarr blew up at 3 AM with PathTooLongException thrown across the import queue. The release that triggered it was a translated upload with a…
Heartbeats were polluting my activity table
The phone in a side-project safety app posts a stationary_heartbeat every few minutes when it's not moving — so the server knows the device is alive even…
Sixty-five lines ahead of git, in production
I went to deploy a fix to a backend Lambda and stopped halfway through because the file I was about to overwrite was 65 lines ahead of the repo…
The arr stack wasn't crashing, qBittorrent was thrashing
I caught a "Sonarr's down again" frustration from a household user. The actual problem wasn't Sonarr at all — Sonarr was fine. It just had a download…
Voice on watchOS without the Speech framework
I wanted to talk to my homelab assistant from the Apple Watch. Action Button down, dictate, get a spoken reply back. Standard project except for one…
Mining 18,000 emails for facts about my own life
I had three years of email sitting in a DynamoDB table and a memory store wired into my assistant. The assistant was making decisions with no recall of…
Push notifications only firing on the final assistant message
My homelab assistant runs as a Claude Code agent in the background. When it finishes a long task, it pushes a notification to my phone with the result…
Designing a receipt matcher for my financial dashboard
Most of my transactions have a receipt sitting in Gmail somewhere. I just want to be able to click a charge in my finance dashboard and see the actual…
Dynamo as inbox, with a passkey
I built a single-page mail reader on top of the DynamoDB table my mail pipeline writes to. Three-pane layout (inbox list / messages / body), search…
Empty bubbles on iOS when the assistant only called tools
The iOS client kept showing blank assistant bubbles in the middle of a multi-step turn. Pull-to-refresh fixed it. Notifications fired fine. The text was…
MIME bodies disguised as attachments
My mail reader was showing "(no text body)" for a chunk of messages, including basically everything from iCloud. The body was right there in the raw…
Multi-select with context-aware bulk actions
I built multi-select for the mail reader UI and then almost shipped a bad version of the bulk-archive button. The fix was making the button's behavior…
Personal mail as a serverless pipeline (and a forwarder identity trap)
I wanted every email to my personal domains stored in DynamoDB as the primary read surface, with Google Workspace continuing to receive a copy as a cold…
Sensor Offline alerts while traveling, twice
A safety app I run pinged me every hour with "Sensor Offline — no data from IFTTT in the last 24 hours" while I was on a trip. That sensor is the motion…
Two weeks of silent torrent failures from one chown
Nothing new had landed in my movie library since the 4th of the month. Radarr looked healthy, the queue was just stuck at 321 items. Most of them showed…
A tiny SSO provider for personal subdomains
I run a handful of small web apps on subdomains — a finances dashboard, a map, a CV, a blog admin. Each had its own login. Each login was the same login…
Plaid to DynamoDB with read-time categorization
I wanted a personal-finance dashboard that I owned end-to-end. Plaid on one end, DynamoDB on the other, one Lambda in between, and categorization rules…
Use the Tailscale brew formula, not the cask
Quick note to past me: when installing Tailscale on a headless Mac, brew install tailscale (the formula). Not brew install --cask tailscale-app.
A personal location heatmap on cheap Lambda infrastructure
I had years of GPS pings in a database from a side project. I wanted to look at where I'd actually been on a map — heatmap of every ping, markers for the…
One Lambda serving a résumé and a blog, routed by Host header
I had a static résumé at one subdomain and wanted a blog at another. Two Lambdas felt absurd for what is functionally two HTML generators. So I put both…
Shipping 14 tiny iOS utilities in a batch
I shipped 14 small iOS utilities to App Store Connect in a batch. Subnet calculators, a WoL remote, a JSON pretty viewer, a regex tester, a ping monitor…
FreezeGuard: a Windows UWF agent shipped as a service
I shipped a small side-project that wraps Windows' Unified Write Filter (UWF) — the feature that lets you "freeze" a system drive so nothing written to…
An inventory app for a family craft business
A family member runs a craft-supply brand and needed a product inventory catalog. I built one weekend project: a public catalog page, an admin page, and…
Making bootable Windows 98 floppies from a modern Mac
I wanted to install Windows 98 SE on a period-correct Pentium III without going through the usual ISO-to-USB workaround. That meant making the actual…