case study / dev tooling

Keelwatch: watching the part of the fleet nobody looks at

A command-line tool that scans a whole workspace of unrelated projects for the structural problems nobody checks until they have already become a problem: git hygiene, leaked credentials, and docs that no longer match what is actually running.

WorkspaceChecksReportDiff vs last runNotify
keelwatch — scan
An example run. The project names are made up; the findings, their wording and the exit code are what Keelwatch prints.

01

The problem

Once you run more than two or three independent projects, a pattern shows up. Nothing is wrong, exactly. It is just never watched. A repo drifts behind its own remote. A token sits in plain text in a git remote URL nobody re-reads. A service goes live and its README still says it isn't deployed.

None of these needs a mistake. They need someone to notice, repeatedly, across every project at once, and that does not scale by hand. I know because an audit of my own workspace turned up exactly this kind of thing. The infrastructure instinct is that anything nobody is watching is already a fault, so I built the watcher.

02

What it does

One pass over a folder of projects, four kinds of check:

  • →Git hygiene: no version control at all, credentials embedded in a remote URL, uncommitted work, and how far ahead or behind the remote each repo is.
  • →Secret scanning: hands the heavy lifting to Gitleaks, then adds one narrow check of its own for what pattern scanners can't catch, a human-chosen phrase assigned to a variable that is named like a secret.
  • →Docker reality: matches each project to the containers actually running for it and flags a project that has Docker config but nothing up.
  • →Docs drift: a local model reads the README's deployment claims and compares them with what was just observed. Is the URL it says is live actually reachable?

03

Decisions that mattered

  • →A check never throws. Each one returns a list of findings, and a check that cannot finish (a timeout, a missing tool, a crash) reports that as a finding. One broken project never blocks the scan of the rest.
  • →Every git call runs under a hard timeout, so a dead or unreachable remote cannot hang everything behind it.
  • →I threw away my own secret scanner. The first version used my regexes. Gitleaks has hundreds of maintained patterns and reads git history, so I replaced mine and kept only the gap it leaves.
  • →Containers are matched by Docker Compose's project label, not by name. Container names routinely have nothing to do with the folder they came from.
  • →Severity depends on context. "Not in git" is a shrug for a notes folder and a real problem for something confirmed live in Docker right now, and the report says so.
  • →The model check is opt-in. Local inference on a CPU takes 10 to 30 seconds per project, which is fine unattended and miserable if you are waiting, so it sits behind a --deep flag.

04

What went wrong

The first version always exited with code 0, whatever it found. That made it useless to a scheduler, which is the one place it most needed to work. The exit code now reflects severity: 0 clean, 1 findings, 2 at least one high.

I added the test suite late, and it caught a real bug on its first run. That was the argument for having written it earlier.

One limitation is still open. Watch mode fingerprints a finding by its message text, so two different changes that produce the same sentence look identical to the diff. It is documented, not fixed.

05

Where it is now

It runs on its own every day against my workspace, remembers the last run, and only reports what is new, with a Windows notification when there is something to look at. A self-contained HTML dashboard shows current status and a capped history log.

It costs nothing to run. The only model involved is a local one.