Lab
Four systems that keep running when I am asleep or on a plane. Explore what they produce, then open the details to see how they work.
SigIntZero blog writer
cron, 10:33 every morningA security research blog that picks its own subject, writes it, checks itself, illustrates itself and ships, then skips the day when nothing is worth writing about.




Output of the pipeline itself, unretouched. - Shipped
- 28 posts live between 5 July and 10 September 2026
- 50 covers generated, every one of them text-free
- Publishes from an isolated worktree, so it has never touched a working tree
How it works
- briefthe 10:07 security brief hands off scored picks
- pickre-scores for bloggability, dedupes against what shipped
- writea slash command with a persona and a corpus
- gateAI tells, fact check, editorial review, schema validator
- covercodex renders a text-free 1280x720 illustration
- shipisolated worktree, branch, commit, deploy
- postLinkedIn, X, and the canonical article repo
Security incidents have a short half-life. Writing one up properly takes a couple of hours, so most of them never get written up at all and the ones that do land a week late. The morning brief was already finding them. The gap was everything after finding them.
The interesting part is the skip. Most of this pipeline is gates that can stop it: the selector disqualifies weekly digests, proof-of-concept tracker commits, generic index URLs, anything outside a 14 day window, and any topic a published post already covers. A quiet cycle produces nothing, and that is the correct outcome rather than a failure. The first autonomous run got it wrong in an instructive way. It read the X posting queue instead of the brief's curated incidents, picked a stale off-brand story and skipped a $20M governance takeover. The fix was a structured hand-off file between the two stages, not a smarter prompt. One smaller thing worth stealing: the stat cards are drawn programmatically with Pillow and a truetype font rather than generated, because image models garble text and always will.
- Built on
- Pythonclaude -pcodexgit worktreePillow
StockRip card art
on demand, one collection at a timeForty eight trading cards rendered by an agent, from a CSV of names to the shipped cut, one command per stage.






Output of the pipeline itself, unretouched. - Shipped
- 48 shipped cuts across four collections, 342 renders behind them
- Its own repo now, after a git stash -u deleted the generator out of the app
How it works
- laddera CSV of twelve items per ticker, cheap to absurd
- refsbrowse for a real reference, stop if there is none
- briefan agent task file, not a prompt
- rendercodex exec with craft and geometry pinned as inputs
- reviseone named change, current art as the authority
- promoteadopt a variant as that card's master
- postdeterministic pixel ops, then the webp cut
- verifyintegrity check across every path
The cards had to look like one set made by one hand, across four collections and forty eight subjects with nothing in common beyond a ticker. Twelve separate prompts give you twelve unrelated pictures, which is exactly what the first attempt produced.
Two decisions carry the whole thing. The art is generated as a bare panel with no text, no logo and no border, because the app draws the label block over it and baking text into the image collides with live UI chrome. And the briefs are agent task files with explicit inputs, outputs and prohibitions rather than prompts. The first version emitted bare prompts, and codex had no idea where to save, what to read as a style reference, or what it was not allowed to touch. The reference step has an explicit stop instruction so it cannot invent the geometry of a real object it failed to find. One flag selects the ladder and every path underneath it, so a new collection is a CSV plus two dict entries and never a new script.
- Built on
- PythoncodexPillowHTML + SVG renderer
Multi-account posting engine
systemd timers, randomized smearA registry of accounts, a voice contract for each, and a scheduler built so that two of them never speak in the same moment or out of the same burst of traffic.
- Shipped
- 38 test files, and a dry-run gate every account has to clear before it posts live
- The same timeline scrapes feed a read-only opportunity radar
How it works
- registryone row per account: session, database, persona
- readscrape that account's own timeline for this tick
- decidethe model returns original, reply, or skip
- capedge-vocabulary quota, cadence and halt thresholds
- locka session lock so parallel ticks cannot collide
- postdry run to JSONL first, live only after the gate
- watchan operator delete trips a halt, engagement is recorded
One account is a script you write in an afternoon. Several accounts, each with a voice you would recognise blind, is a scheduling and isolation problem, and every failure worth learning from lives in the isolation.
There is no API key anywhere in it. The composing model is Claude Code itself, reading the persona file and the fetched timeline each tick and returning one decision, which means the voice lives in a markdown contract rather than in a prompt buried in Python. The scheduler is the part I would actually show another engineer. Ticks fire from randomized timers rather than a fixed cron minute, and the per-account refresh jobs were split onto disjoint windows after I noticed a single address fetching two accounts inside one ninety second burst. It is worth being precise about what that buys, because I oversold it the first time I described it. Splitting in time does not remove the fact that one address reaches both. What it removes is the tight co-occurrence, which is the strongest and most machine-obvious form of the signal. It is a reduction, not a fix, and it is written into the script header that way so the next person does not inherit my nicer version.
Nothing from this one is on the page. The accounts are not mine to put next to my own name, and a screenshot would be the whole point of the isolation undone for a thumbnail.
- Built on
- Pythonclaude -pSQLitesystemdvibatchium
Fleet Terminal
systemd user timer, every three minutesOne screen for every autonomous job on the box: schedule, health, and notional cost, across forty units and three species.
- Shipped
- 40 units tracked across cron ticks, systemd timers and always-on daemons
- Read-only by construction, never deployed anywhere
How it works
- collectcrontab -l, systemctl show, ps, log tails
- classifycron ticks, systemd timers, always-on daemons
- costnotional pay-as-you-go equivalent, never billed
- servea JSON overwrite, so no restart between refreshes
Forty scheduled jobs on one machine is well past the point where you can hold the schedule in your head. And the question that actually matters is never what is running. It is what quietly stopped four days ago.
The collector is strictly read-only. It shells out to crontab -l, systemctl --user show, ps and file reads, and there is no code path in it that edits a crontab or touches a running job, which is the only reason I am willing to point it at a fleet that includes live bots. It runs on a user-bus timer rather than cron, because the collector itself calls systemctl --user, and a cron job would have to do the XDG_RUNTIME_DIR dance just to reach the user bus.
There is no screenshot of this one either, for a duller reason. The file it builds embeds absolute paths, working-directory slugs, session UUIDs, full command lines and log tails, so it is local-only by construction. A public view needs a separate scrub pass, and a redacted screenshot is just a picture of black rectangles.
- Built on
- Next.jsPython collectorsystemd
Everything here runs on one Linux box under my desk, on systemd timers and cron, with Claude Code as the model in the loop rather than a hosted API. The full schedule lives in the last one on this list.