Writing
Technical writing on systems, tooling, and the odd rabbit hole worth documenting.
From dotfiles to declarative desktops
A Linux workstation evolved from shell scripts to NixOS, with agentic tooling and defence-in-depth security.
-
The declarative NixOS stackHow my dotfiles went from symlink scripts to one Nix flake that rebuilds a Linux workstation: system, shell, editor, devshells and secrets.
-
The agentic development stackWiring up OpenCode, Claude Code, MCP servers, agent skills and a research, plan, implement agent pipeline with Nix, so it rebuilds on any machine.
-
Security posture: defence in depth on a declarative desktopEight security layers on a NixOS desktop, from sops-nix secrets and DNS-over-TLS to opensnitch and encrypted restic backups, with gaps marked.
-
Agent security: a design for running untrusted codeA design, not yet built, for unattended coding agents: a gVisor sandbox with no secrets and a host broker that holds credentials and logs every call.
-
Future roadmap: what's nextThe February 2026 plan for the dotfiles: a gVisor agent runner, Firecracker microVMs, skill cleanup, and a home server, with a September status update.
-
From NixOS to macOS: porting a declarative workstation across platformsHow the NixOS and Hyprland config from Part 1 moved to a MacBook with most of the home-manager modules unchanged, and the five bugs that cost me evenings.
-
Auditing a declarative desktop: what to look for and what I foundA layer-by-layer security audit of my nix-darwin setup: what to check, the firewall and secrets gaps I found, and a ten-line checklist to reuse.
-
New machine, one hour: a fully automated macOS bootstrapOne script takes a bare MacBook to the full nix-darwin setup: ten idempotent stages, a data restore script, and a Lima VM that isolates the OpenCode agent.
-
The production pass: shared config, security scripts, and a three-tier agent designTwo sessions after the macOS bootstrap: one shared file for SSM tunnels that fixed a port collision, Touch ID in tmux, and a design for three OpenCode agents.
-
Runtime state is not configuration: what a Nix bootstrap cannot restoreOpenCode plugins, agent session history, and the Zen browser profile live outside Nix, plus the small fixes from the first day of using the macOS setup.
-
One flake, two people: moving per-user config into one file eachHow 17 commits of fork drift collapsed into one nix/personal file per person, and the audit process that keeps a shared nix-darwin flake shared.
-
The case of the generic 'sh'Every launchd agent on my Mac showed up as 'sh' in Activity Monitor. A small Rust wrapper built by Nix restored their names and kept the /nix/store wait.
-
66 loose directories and how I made them Nix's problemAn audit found 202 agent skill entries and only 4 managed by Nix. Here is how I moved 66 loose directories into the flake and fixed the skill enumerator.
-
Four small fixes from July and AugustA meeting overlay that fixes multi-monitor coordinate math, herdr next to tmux, an AeroSpace config bump that changes a default, and a local model cull.
-
Reaching an agent session from anywhere, and not losing it afterwardTailscale plus a disposable Lima VM for remote agent sessions, a nightly session backup gated by a secret scan, and the pipefail bug that nearly broke it.
-
Automating PR review: stale reviews, a new engine, and a PATH bugGitHub hides a review once new commits land. I replaced my hand-rolled OpenCode reviewer with ocr, handled stale reviews, and fixed an exit 127 bug.
-
Bonsai 2: a 2-bit local model that fits in 10 GB of RAMA 2-bit ternary model runs 2.5x faster than a 4-bit rival on my M5 and needs a custom FastAPI server. It now serves three agent roles and Perplexica.
-
What changed in the dotfiles since Part 17A command guard that stuck, a semantic-search layer, a nix cleanup that split up two oversized modules, an agent-version migration that's still just a plan, and a workaround for a Bedrock bug that kills long agent sessions.
-
Blocking destructive commands before an agent runs themHow a hook denylist and a local classifier screen coding agent shell commands, what each one missed when I tested them, and why I removed one.
-
The agent VM I actually run, and the NixOS jail I designedWhat the Lima agent VM mounts, how secrets reach it over stdin, why its no-sudo claim only holds for a NixOS image I never booted, and what it still does not isolate.
-
Giving an agent VM Bedrock access without long-lived keysHow an assumed AWS role and a 50 minute launchd job give an agent VM one-hour Bedrock credentials, and the gap where the VM copy goes stale.
-
Moving my agent sessions from tmux to herdrWhy I moved OpenCode sessions into herdr, how the plugin, the nix config and the prefix key fit together, and what still lives in tmux.
-
Pinning the agent skill supply chainWhat a skill can do on my machine, how my catalog installs them, which installs are pinned, and why my pin script no longer pins anything.
-
What I dropped and whyEight agent sandbox and tooling designs I wrote about as if they existed, checked against the repo, with the reason for each cut where one was recorded.
-
A git policy for agents belongs in tested configI thought a restricted git MCP server protected main. Reading it showed it never did, so I moved the rule into a tested, declarative dcg pack.
-
Constraining the tool and the session, not the promptA web fetch sanitiser plus a taint rule that blocks side effects after hostile content arrives, what I measured and rejected, and the gaps that remain.
-
How I ship production Java with AI agentsSix months of production Java on a backend service: agents that build, review and audit code, and the decisions I still make myself.
-
My notes are a codebase. The LLM is the build step.How I keep notes in a Git repo, capture them with one fish function, and let an LLM compile raw daily captures into a wiki I read.