Skip to main content
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
0b6995bMerge pull request #80 from ManningWorks/docs/evidence-integrity-rule2026-10-07T17:10:08Z
88b8173docs: add Evidence integrity section to AGENTS.md2026-10-07T13:47:30Z
e48bc9cMerge pull request #79 from ManningWorks/feature/accept-reason-flag2026-10-07T10:51:20Z
413a7ebfix(show-reasons): skip install-seeded # comment lines (PR #79 MEDIUM follow-up)2026-10-07T10:00:01Z
74236e0test: fix show-reasons phase timer units in local-integration2026-10-07T07:16:34Z