Sharing enthusiasm for creating and exploring.
Building a homelab LLM router with LiteLLM and Headroom
TL;DR: Built a homelab LLM router out of two independent Docker Compose stacks: one for local model serving, one for routing. The routing stack, litellm-headroom/, runs a LiteLLM proxy that gives every client (agents, IDEs, scripts) one OpenAI-compatible endpoint that routes to local silicon, other homelab GPU boxes over Tailscale, or cloud APIs. A Headroom sidecar compresses only the requests actually headed to OpenAI/Anthropic, decided by where the request is actually going rather than whatever alias I happened to name the model. Postgres backs per-bot LiteLLM API keys so I can tell which agent is burning tokens and cap it before it blows a budget. Every secret lives in one Bitwarden note and reaches the containers as Docker Compose file-based secrets instead of environment: values docker inspect could read; direnv handles the Bitwarden unlock automatically so plain docker compose up still works. Full writeup and working compose files in homelab-llm-router. ...
vLLM on the R9700
TL;DR: Replaced Ollama with vLLM on an AMD Radeon AI PRO R9700 (gfx1201), running in Docker via a community image built specifically for this chip. The hard part wasn’t the model, it was getting vLLM to run as a non-root user instead of root. Three separate, unrelated permission traps had to be fixed together before it would even start: numeric vs. named GPU group IDs, an unreadable /root, and Docker silently root-owning auto-created bind-mount directories. Working docker-compose.yml and setup script in homelab-llm-router. ...
Debugging chrome-devtools-mcp on WSL2: Puppeteer Was Secretly Launching Windows Chrome
chrome-devtools-mcp is a Puppeteer-based MCP server that gives an agent direct control over a real Chrome instance — navigate, screenshot, inspect the DOM, read console/network output. On a fresh WSL2 setup it failed on every single call, immediately, with an error that gave no indication of what was actually wrong: Protocol error (Target.setDiscoverTargets): Target closed The environment: WSL2 (Ubuntu-based) on a Windows 11 host, WSLg enabled (GUI apps work fine via DISPLAY=:0/Wayland), chromium installed on the Linux side via apt (/usr/bin/chromium — not google-chrome), and a normal Google Chrome install on the Windows side. That specific combination — Linux Chromium from apt, Windows-side Chrome installed separately — turned out to be exactly the trigger condition. ...
How Claude Code Loads Context: Skills, Plugins, and CLAUDE.md Hierarchy
Claude Code pulls configuration from a lot of places at once: a user-level home directory, a project directory, plugins, and — if you’re on a managed machine — an organization policy layer above all of it. Once you’re running more than one or two projects, it stops being obvious what’s actually in context for a given session, and that matters in two directions. Too much loaded and you’re paying token cost for instructions and skill descriptions a session will never use. Too little loaded and a rule you were relying on silently isn’t there. ...
Managing Shared Terraform Modules: From Copy-Paste to Centralized Dependencies
Infrastructure as Code (IaC) teams frequently encounter challenges when sharing Terraform modules across multiple projects and repositories. Organizations often resort to copying and pasting module code between repositories, creating maintenance overhead and version drift issues. This article examines strategies for establishing a centralized module management system similar to package management approaches used in application development. The Module Sharing Challenge In distributed repository architectures, teams commonly define reusable infrastructure components such as Route53 configurations, Web Application Firewalls (WAF), and Virtual Private Clouds (VPC) as Terraform modules. Without a formal sharing mechanism, these modules are often duplicated across repositories through manual copying, leading to several issues: ...
Powering Disaster Response Drones: Why Three-Phase Beats Single-Phase for Tethered Communications Systems
When natural disasters strike, communication infrastructure often becomes the first casualty. Cell towers topple, fiber optic cables break, and emergency responders find themselves operating in communication blackouts. Enter the tethered drone—a promising solution that can hover at 400 feet, acting as a temporary telecommunications tower to restore critical connectivity. But here’s the engineering challenge: these systems require substantial continuous power to maintain flight operations while running telecommunications equipment. The question becomes: what’s the most efficient way to deliver that power through a 400-foot tether cable? ...
Building a Low-Cost Drone SAR System: Democratizing 3D Mapping with Software-Defined Radio
TL;DR I’m planning to build a drone-mounted Synthetic Aperture Radar (SAR) system using a LimeSDR Mini 2.0 and Raspberry Pi 5 that can generate high-resolution 3D maps at a fraction of the cost of traditional methods. This system will operate in any weather (assuming the drone does as well), penetrate vegetation, and provide sub-meter elevation accuracy for under $10,000 total hardware cost. The key innovation is using “snapshot SAR” processing to work around the navigation limitations of low-cost platforms. ...
Mergerfs With Nvme Usb Drives
Combining Multiple NVMe USB Drives with MergerFS Recently, I wanted to combine several NVMe drives using NVMe-to-USB adapters into a single large storage pool on my Linux system. My goal was to use mergerfs to create a unified mount point, making it easy to manage files across all drives. In particular i followed the instructions outlined on Perfect Media Server The Challenge: Identical USB Adapters After connecting the drives, I ran into an issue: Linux couldn’t easily distinguish between them. They weren’t each showing up in /dev/disk/by-id/, only one was. Since all the NVMe-to-USB adapters were from the same manufacturer, this causes udev (the system that creates entries in /dev/disk/by-id) to ignore or overwrite it, because: ...