systemd-deployed daemon that drives the semprini-core stack-support agent headless via Claude Code, monitors health via Uptime Kuma, applies safe upgrades, and escalates to the operator over Matrix. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2.9 KiB
2.9 KiB
Maintenance Agent (semprini-maintainer)
Autonomous, always-on agent that keeps the container stack healthy and current.
Implementation: this repository — see README.md. It maintains
the semprini-core stack (a separate repo) referenced via repo_dir.
Purpose
Continuously maintain the running stack without an operator at the keyboard:
- Detect and remediate service outages.
- Periodically check for and apply container upgrades (with test + rollback).
- Communicate with the operator over Matrix (chat.semprini.me), including asking questions when an upgrade needs a human decision.
It is the unattended driver for the canonical stack-support agent
(agents/stack-support.agent.md) — it does not re-implement that logic, it
feeds the agent state and relays its results.
Key decisions
- Scope = Uptime Kuma's live monitor list. Read every health cycle, never
hard-coded. Anything Kuma tracks is in scope — including services owned by
other projects (e.g.
semprini-blog) that register Kuma monitors. This keeps the maintained set and the monitored set identical by construction. - Health signal = Kuma heartbeat. The daemon reads Kuma's own up/down result rather than re-probing, reusing the stack's existing monitoring.
- Kuma data access via a throwaway
keinos/sqlite3container mounting theuptime-kuma-datavolume read-only (the daemon user has Docker but not host access to root-owned volume files). - Runner = Claude Code headless (
claude -p), withagents/stack-support.agent.mdinjected as the system prompt. The agent returns a JSON envelope (status/summary/question/options) the daemon acts on. - Runs as
paul(in thedockergroup), not root — Claude Code refusesbypassPermissionsas root. - Autonomy = full by default: remediate and apply safe upgrades; escalate
only major/breaking upgrades to Matrix and resume on reply. Configurable to
fix-onlyornotify. - Matrix transport: dedicated bot
@maintainer:semprini.me(created via the Synapse registration shared secret) in a private room with@paul:semprini.me.
Cadence
| Loop | Default interval | Config key |
|---|---|---|
| Health check + remediation | 5 min | health_interval_seconds |
| Upgrade review | 7 days | upgrade_interval_seconds |
| Matrix reply polling | 30 s | matrix_poll_seconds |
| Quiet health heartbeat | daily | heartbeat_interval_seconds |
Escalation flow
- Agent returns
status=escalatewith aquestion+options. - Daemon posts a numbered question to Matrix and pauses upgrade cycles.
- Admin replies (a number or free text).
- Daemon feeds the decision back to the agent, which carries it out and reports.
Operational notes
- The bot DM room invite must be accepted once by
@paulin Element. - Secrets (
config.json,.bot-secrets) are gitignored. - A single unanswered escalation blocks new upgrade cycles (health remediation continues regardless).