Managing workstation config across a small fleet Managing workstation config across a small fleet

Someone else's laptop, my agent: config management for people who won't read a README

TL;DR I maintain workstations for people who are never going to run git pull — so the config lives in a repo I own and they never see. It is a reference/restore repo, not config management: per-machine folders hold snapshots of what’s actually installed, and restore is a deliberate install -Dm755, not a sync. The no-auto-sync part is the whole point. Silent convergence across machines that are supposed to differ is how you break someone else’s laptop from your desk. The boring 40 minutes — partitioning, encryption, sudo, base packages — is an unattended installer (a Debian preseed, now on its eleventh revision). The interesting 20 minutes is the per-machine folder. The agent’s job isn’t to apply config. It’s to read the repo, diff it against the live box, and tell me what drifted — then write the restore commands for me to approve. What I’d do differently: put the agent’s own profile in git before you have three machines copying it from each other over SSH. The problem with managing other people’s computers I run a handful of machines that aren’t mine. A couple of family desktops, a bench box at the office, and a laptop belonging to someone who does knowledge work and would rather not learn what a dotfile is. ...

August 11, 2026 · 9 min · zolty
A funnel from surplus auction listings to a ranked shortlist A funnel from surplus auction listings to a ranked shortlist

Two dozen crawlers and a shortlist: how I stopped browsing surplus auctions

TL;DR Government and institutional surplus auctions are a firehose: thousands of live lots, most of them badly photographed, vaguely titled, and worthless. Browsing them by hand doesn’t scale and — worse — it’s biased. You click the pretty photos, and the pretty photos are the ones everyone else clicked. The fix is a funnel: ~24 scheduled crawlers feeding a single Postgres table, photos and paperwork in object storage, an hourly evaluation pass, and one ranked board. The judgement layer (reading the goods out of photos, estimating what they’re worth) runs as a small model pipeline with a second pass that checks the first. I’m deliberately light on its internals here. The output isn’t a decision. It’s a shortlist with reasons attached — and a “this is wrong” button that puts the lot back in the queue. The single highest-value change in three months was raising how many photos get looked at. The label is often in photo nine. None of this is my job. It’s the hobby that pays for the hobby, which is the only reason it’s allowed to be this over-engineered. Why I built a data pipeline for a weekend habit Let’s be honest about the proportions here. I built two dozen scheduled crawlers, an object store, an hourly evaluation pass and a ranked dashboard, in order to buy second-hand equipment on the internet slightly better than I did before. ...

August 7, 2026 · 11 min · zolty
Fetching secrets into CI without leaking them Fetching secrets into CI without leaking them

Secrets without leaks: a Bitwarden CLI into CI and agent permissions

TL;DR Secrets leak through echo, debug tracing, logs, process args, and env-var inheritance — guard the flow, not just storage Fetch from Bitwarden once, assign to a shell variable, reference the variable — never inline the raw value Verify with first-3/last-3: ${VAR:0:3}...${VAR: -3} — confirm you have the right token without exposing it GitLab masked vars hide values in logs (8+ chars); protected vars only expose on protected branches — use both for production secrets Agent permission rules prefer command substitution inside the allowed command, not as an env var Rotate tokens immediately; verify before use; pin your bw CLI version The leak surface — where secrets escape I used to think storing secrets in Bitwarden (or Vault, HashiCorp, whatever) meant they were safe. I was wrong. ...

August 4, 2026 · 8 min · zolty
Three domain-specific MCP servers Three domain-specific MCP servers

Building domain-specific MCP servers: three I actually use

TL;DR Three production MCP servers I run: a docs/wiki wrapper, a personal-finance aggregator, and an inventory-ops tool for a side project. Type hints are your schema. The FastMCP SDK extracts JSON Schema from Python docstrings and type annotations — you almost never hand-write schemas. Flatten nested APIs. If the upstream API returns nested JSON, unwrap it in the MCP tool and return clean markdown or a simple dict — agents prefer predictable structures. Reads always-on, writes gated. Use environment variables (MCP_WRITE=1) or similar to gate mutation tools; put mutations behind a validated service layer and audit every call. Log to stderr, not stdout. Stdio protocol uses stdout for JSON-RPC; anything else breaks the connection. The lineup I’ve written three MCP servers from scratch over the last few months. This is the cookbook — concrete patterns, not theory. If you haven’t read the intro to writing MCP servers, start there; I won’t re-explain the anatomy. ...

July 28, 2026 · 10 min · zolty
An autonomy ladder for coding agents An autonomy ladder for coding agents

The autonomy ladder in practice: letting agents commit, then merge

TL;DR Five rungs: Rung 0 (read-only) → Rung 1 (draft MRs) → Rung 2 (commit on /llm fix) → Rung 3 (auto-merge patches) → Rung 4 (exceptions-only). Promotion is earned, not promised — ~30 days at Rung 1 + <10% reverts → Rung 2; ~60 days at Rung 2 + <5% reverts → Rung 3. Infra agents cap at Rung 2 — Kubernetes, Terraform, secrets, cert-manager always need human approval to merge; blast radius is too high. Demotion is immediate on test deletion, secret leak, scope creep, or any deploy needing manual rollback. Non-negotiable gates (deterministic, run at the CI runner) block everything: no deleted tests, no secrets, no medium+ CVEs, destructive commands on allowlist only. From concept to operational I wrote about the trust ladder for agents in May — the philosophy of supervised → monitored → trusted → autonomous, and why you can’t just flip a switch. That post is the why; this one is the how. I’m going to walk through the exact rungs, the promotion criteria that actually work, the demotion triggers, and the CI rules that make it safe. ...

July 24, 2026 · 9 min · zolty
Background monitors watching an automated cluster Background monitors watching an automated cluster

Background agents that babysit the cluster: drift, test-deletion, and cost sentinels

TL;DR Deterministic guardrails run first. PreToolUse regex blocks destructive commands (kubectl delete, terraform destroy, DROP TABLE, rm -rf) before the LLM is consulted. LLM safety is probabilistic; regex is not. Goal-drift detector compares initial objective vs final commit via embedding cosine similarity; alerts if divergence > 40%. Test-deletion sentinel hard-fails CI if tests are deleted without a [test-refactor] tag—you can’t hallucinate past the regex. Cost-spike alerts monitor 24h rolling spend at 80% (warn) and 100% (critical), plus a circuit breaker that opens after 3 failed runs. Action audit flags when the execution plan diverged wildly from the initial strategy—catches scope creep before it becomes expensive. The Problem: Autonomy Without a Leash Last year I started automating routine cluster work—dependency upgrades, CI improvements, minor bug fixes—by running Claude in the background with Git/Terraform access. The first three months were great. The fourth month I woke up to a $180 cloud bill. ...

July 21, 2026 · 8 min · zolty
Auditing repository history against ecosystem adoption timelines Auditing repository history against ecosystem adoption timelines

I audited four months of my own repos to see what I was actually early on

TL;DR I ran an audit over my repos going back to March: pull the first commit that introduced each agentic pattern, then go find out when that pattern actually became common practice in the wider ecosystem. The results were humbling in a useful way. Four things I quietly considered myself early on were catch-up, in one case by two years. Four other things turned out to be genuinely ahead of documented practice. The interesting part is what those four have in common, and it is not what I expected. ...

July 19, 2026 · 8 min · zolty
Multiple language models consulted as a panel Multiple language models consulted as a panel

A panel of LLMs: using Gemini and Claude to pressure-test decisions

TL;DR One model = one blind spot. Claude and Gemini were trained on different datasets, have different architectures, and will miss different things. Consult for breadth, not consensus. The goal is to widen your analysis surface and catch blindspots, not to reach agreement. If all three models agree, that’s suspicious—run stress tests. The bridge is simple: Playwright drives a logged-in Gemini tab via Chrome DevTools Protocol, pastes your prompt, and scrapes the response when done. Real session, no API key. Two modes: (1) cold second-opinion—hand off a problem and ask for an independent take; (2) adversarial debate—assign sides, force defense, then reconcile. Both work; adversarial is brutal and useful for high-stakes calls. Anchoring is the trap. If you show Model B what Model A said and ask “do you agree?”, you get theater. Always pose cold or ask for the opposite argument first. Every Model Has Its Blind Spot I make the same mistakes in code as everyone else. My reasoning hits walls. I miss architectural gotchas and cut corners on testing. An LLM does the same, just in different places. ...

July 17, 2026 · 8 min · zolty
Two AI agents talking over a chat channel Two AI agents talking over a chat channel

Claude asks Claude for help: an agent-to-agent bridge over Mattermost

TL;DR Two Claude agents running in separate project contexts share a private Mattermost channel to ask each other for help without human intervention. A deterministic polling gate (bash + curl) checks for new messages before spawning the expensive responder LLM — idle polls cost almost nothing. The responder runs on the Claude Code subscription (keychain OAuth), not API credits, and keeps the LLM cheap. Scheduled every ~3 minutes by macOS launchd; armed/disarmed by loading/unloading the launch agent. Supervised autonomy: the responder can read, triage, draft, and explain — but NOT commit, spend money, change access, or deploy. Why one agent asks another I run parallel Claude Code sessions on different projects. They’re isolated — each has its own repo context, its own workflow, its own focus. But sometimes they need each other. ...

July 14, 2026 · 8 min · zolty
Supervising AI agents by reading the documentation they generate instead of the diffs Supervising AI agents by reading the documentation they generate instead of the diffs

I don't read the PRs: supervising Claude by reading the docs it leaves behind

TL;DR I point Claude at a problem, give it a direction, and let it run — and most of the time I don’t read the merge request it opens. On an internal homelab where the blast radius is my own cluster, backups are real, and everything’s reproducible from code, line-by-line diff review is the wrong altitude. Instead I supervise after the fact by reading the artifacts the agents leave behind: the /docs/ folder, a Wiki.js wiki full of Mermaid diagrams, auto-generated architecture SVGs, and this blog. Reading those — not the diffs — is what’s actually caught problems: dead systems still wired in, duplicate config, a deploy that quietly deleted itself. ...

July 12, 2026 · 8 min · zolty

Affiliate Disclosure: Some links on this site are affiliate links (Amazon Associates, DigitalOcean referral). As an Amazon Associate, I earn from qualifying purchases. This does not affect the price you pay or my editorial independence — I only recommend products and services I personally use and trust.