feat(tuskd): headless rules daemon (#5)#9
Conversation
Add cmd/tuskd, a standalone binary that runs a profile's rules engine without any TUI dependency (no tview/tcell linked), resolving issue #5's "deployable as a daemon without all the TUI bloat". It reuses the existing config, db, and rules packages: loads a profile, compiles its rules, and evaluates them against a live snapshot on a configurable interval (-i, default 2s), honoring readonly/dry_run. Fetch errors for a single resource are logged and skipped rather than crashing the loop, and it shuts down cleanly on SIGINT/SIGTERM. - .goreleaser.yml: build and ship the tuskd binary alongside tusk - README: Daemon section with install, usage, and a systemd unit - tests: evaluate() with a fake snapshotter (incl. partial fetch failure) and runDaemon() context-cancel shutdown Closes #5 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fraser-Isbester
left a comment
There was a problem hiding this comment.
Reviewed for simplicity and failure behavior.
Appropriately small: the engine was already TUI-free, so this just gives it a main and a loop rather than adding machinery. The snapshotter interface is a clean seam that keeps the poll loop testable without a live DB. Failure handling suits a long-running process — a single resource's fetch error is logged and skipped instead of crashing, interval is validated, and shutdown is signal-driven and clean.
One non-blocking note: on a partial fetch failure the tick still evaluates with an empty slice for the failed resource, so its active violations get marked closed and then reopened on the next good tick. It can't cause a wrong action (a match needs present data) — it's just churn in the violation log. Fine to leave; worth a follow-up only if audit history needs to be precise across DB blips.
Looks good.
What
Adds
cmd/tuskd, a standalone daemon that runs a profile's rules engine headlessly — no TUI (tview/tcell) linked into the binary.config,db, andrulespackages: load profile →BuildRules→NewEngine→ poll +Evaluateon a-i/--interval(default 2s).readonly/dry_run; requires the profile to have ≥1 rule.SIGINT/SIGTERM..goreleaser.ymlshipstuskdalongsidetusk; README gains a Daemon section (install, usage, systemd unit).Why
Issue #5: "Tusk's Rules engine should be deployable as a daemon without all the TUI bloat." The engine was already TUI-free; this exposes it as a deployable binary.
Validation
go build ./...,go vet ./...,golangci-lint run ./...(0 issues),go test ./cmd/tuskd/...pass;go mod tidyclean.evaluate()with a fake snapshotter including a partial fetch failure;runDaemon()context-cancel shutdown.pg_sleep(12)query each tick and fired the action; gracefulSIGTERMshutdown (exit 0); clear errors for no-rules and non-positive interval.Review guide
cmd/tuskd/main.go— CLI + poll loop (snapshotterinterface is the test seam)..goreleaser.yml,README.md— packaging + docs.Closes #5
🤖 Generated with Claude Code