Build a small, dependable system that:
The important idea is: this is not one giant application. It is a collection of small programs with stable interfaces. Grafana observes them. Pi helps you operate them. Deterministic code does the recurring work.
North star: if the AI model, Grafana, or internet is unavailable, scheduled work should still run and your data should still be readable.
Your current setup already contains:
alert-on-fail.sh;
Do not replace this. Standardize it gradually.
Inputs ──> small CLI tools ──> domain SQLite ──> Grafana
^ ^
| |
cron / Pi run ledger
--help, non-zero failure exits, and JSON output when another program consumes it.
Create a short inventory with these columns:
| Workflow | Trigger | Current steps | Minutes/month | Failure impact | Needs AI? | Status |
|---|
Rank candidates by time saved × frequency, then reduce the score for risk and implementation complexity.
Choose one workflow that is frequent, read-only or reversible, and easy to verify. Good candidates from your current system are the hourly system review or market-data updates. Do not begin with autonomous email sending or financial actions.
Done when: one workflow has a written input, output, failure condition, and acceptance check.
Create one new database, automation.sqlite, for execution metadata only. Start with one table:
job_runs(
id, job_name, trigger, started_at, finished_at,
status, exit_code, duration_ms, summary, log_path
)
Add one small runner around scheduled commands. It should:
Avoid a generalized event platform. A run ledger answers the immediate questions: Did it run? Did it work? How long did it take? Where is the log?
Done when: the chosen workflow can fail deliberately and the database records the failure correctly.
Provision it from Git like your existing dashboards. Add only panels that cause a decision:
Keep domain dashboards separate and link to them from the home dashboard. Do not squeeze every chart onto one page.
Continue opening SQLite data sources in query-only mode. Grafana’s SQLite plugin reads databases available on the Grafana host.1 Your existing provisioning already follows this model. Grafana also supports file-provisioned dashboards and data sources, so definitions can remain version controlled.2
Done when: opening one page tells you what needs attention in under 30 seconds.
For each workflow you touch, use this shape:
tool-name check ... # read-only inspection
tool-name run ... # perform the bounded action
tool-name status --json # machine-readable result
A tool should validate inputs itself. Never depend on the model to enforce permissions or business rules.
Turn repeated Pi operating knowledge into small skills. A skill should explain when to use a tool, the safe workflow, and verification steps. Keep implementation in scripts rather than duplicating code inside prompts.
Do not add MCP immediately. Your current local tools and Pi skills are simpler. Add a local stdio MCP adapter only if the same tools genuinely need to work across several AI clients. MCP standardizes tools, resources, and prompts, but it is an interoperability layer—not an operating system or workflow engine.3
Done when: the same command works interactively, from cron, and through Pi.
Use a three-level autonomy model:
| Level | AI may do | Examples |
|---|---|---|
| Observe | read and summarize | inbox digest, system review, portfolio summary |
| Propose | prepare an action for approval | draft email, suggest reminder, propose trade research |
| Act | perform a narrow reversible action | file a known report, tag an item, refresh cached data |
Start every capability at Observe. Promote it only after reviewing a useful sample of runs and defining rollback or recovery.
Keep untrusted content—emails, web pages, documents—separate from instructions. OWASP identifies excessive functionality, permissions, and autonomy as root causes of damaging agent actions, and recommends narrow tools, least privilege, downstream authorization, logging, and human approval for high-impact actions.4
Done when: every AI-enabled write action has a narrow permission boundary and an explicit approval rule.
For every new automation, repeat this loop:
A sensible order for your system is:
Keep cron while jobs are local, independent, and understandable. systemd timers become useful if you need missed-run catch-up with Persistent=, stronger service supervision, or journal integration.5
Consider Windmill only when you need multi-step workflows, retries, approvals, secret handling, and a web interface often enough to justify another service. It is code-first and self-hostable, but also introduces a worker runtime, orchestrator, and application platform.6 Consider n8n when visual SaaS integration is the dominant need.7
Do not adopt either merely to replace ten readable cron entries.
pass or environment files with restricted permissions; never in dashboard JSON, prompts, logs, or Git.
job_runs schema and runner contract on paper.
Then implement only that slice end to end. Let actual friction—not architecture enthusiasm—choose the next feature.
Grafana SQLite data source and plugin repository — local SQLite databases as Grafana data sources.↩︎
Grafana: Provision Grafana — file provisioning for data sources and dashboards.↩︎
Model Context Protocol architecture — the host/client/server model and tools, resources, and prompts.↩︎
OWASP LLM06:2025 Excessive Agency — least functionality, least privilege, approvals, and monitoring.↩︎
systemd.timer manual — timer activation and persistent timers.↩︎
Windmill introduction — its workflow engine, workers, scheduler, and code-first model.↩︎
n8n documentation and self-hosting overview — visual workflow automation and self-hosting.↩︎