Skip to main content
luke@terminal:/$ ls
luke@terminal:/projects/box-audit$ cat box-audit.md

box-audit

[active](github)

My server checks itself every evening so I don't have to.

Daily-delta security and health audit for my NucBox. One bash script, a systemd timer, and a Hermes cron that posts the report to Telegram.

stats:

github:
0stars
0forks
stack:
Bash
Python
Docker
GitHub Actions
links:

background:

The NucBox that runs half my homelab sits in a corner and mostly behaves. Every so often I'd run a full Lynis audit, read the score, nod, and forget all of it by the next morning. What I actually wanted was smaller and daily. Is the disk filling up? Did ssh auth failures spike? Is anything new listening on a port? And how is any of that different from yesterday? Nothing off the shelf did the delta part, so I wrote a bash script.

why:

box-audit runs a curated subset of a system audit every evening and writes the findings as JSON. A Hermes cron on another machine picks it up and posts a formatted report to my Telegram. Monthly Lynis covers the deep dive; this covers the wait-what-changed stuff. It has also quietly become my testbed for doing open source properly: property tests, a fixture corpus, seeded-container CI, a changelog people can actually read, and release tags that exist before CI demands them.

struggles:

[challenge]--tail and --diff shipped in v0.5.0 having never worked — every call died with command not found and exited 0 anyway
[challenge]The perms fix made audit history group-readable — and broke the daily cron on its first run, because the gateway process had started before the group existed
[challenge]My first CI spec needed a webhook receiver on the NucBox so GitHub could trigger test runs — except I'm behind CGNAT and can't receive webhooks. The whole tier collapsed into one local pre-flight script
[learning]Adding a user to a group fixes nothing that's already running — every long-lived process keeps the group list from when it started
[learning]A command that fails with exit 0 is the bug you can't see — the smoke suite asserts actual output now, not exit codes
[learning]Agents wrote most of the code. Stacked small PRs were how I reviewed it — one 1,500-line diff would have gotten a rubber stamp

timeline:

2026-09-14Initialised the repo. One bash script, a systemd timer, and a Hermes cron that posts the report to Telegram
2026-09-15Shipped v0.5.0 through six stacked PRs in one day: delta mode, per-box config, an agent-facing skill, and install CI
2026-09-15Property tests and a fixture corpus landed. Plain bash, no new test framework, 33 green smoke assertions
2026-09-16Fixed the install contract: audit artifacts now land root:boxaudit and group-readable, so non-root admins can actually read history
2026-09-17The group fix broke the daily cron on its first run. Lesson filed: adding a user to a group silently breaks every long-running process they own until each one restarts
2026-09-18v0.7.0: stale-group self-diagnosis, so the next Permission denied points at the real cause instead of blaming the timer
luke@terminal:/projects/box-audit$ git log --oneline -5
3361b57Merge pull request #36 from ManningWorks/docs/agents-md2026-09-17T23:28:00Z
03901fbdocs: add AGENTS.md — release tag convention + testing convention2026-09-17T23:25:41Z
9b694bcchore: empty commit to re-trigger release-check after v0.7.0 tag push2026-09-17T23:19:12Z
02ad052chore: bump VERSION to 0.7.0, stamp changelog date (#35)2026-09-17T23:10:21Z
985cc9cMerge pull request #34 from ManningWorks/feat/issue-32-stale-group-selfdiagnosis2026-09-17T22:55:54Z