Files
semprini-maintainer/docs/architecture.md
T
paulandClaude Opus 4.8 e91b129841 Initial commit: autonomous maintenance agent for semprini stack
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>
2026-06-21 17:20:06 +12:00

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:

  1. Detect and remediate service outages.
  2. Periodically check for and apply container upgrades (with test + rollback).
  3. 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/sqlite3 container mounting the uptime-kuma-data volume read-only (the daemon user has Docker but not host access to root-owned volume files).
  • Runner = Claude Code headless (claude -p), with agents/stack-support.agent.md injected as the system prompt. The agent returns a JSON envelope (status/summary/question/options) the daemon acts on.
  • Runs as paul (in the docker group), not root — Claude Code refuses bypassPermissions as root.
  • Autonomy = full by default: remediate and apply safe upgrades; escalate only major/breaking upgrades to Matrix and resume on reply. Configurable to fix-only or notify.
  • 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

  1. Agent returns status=escalate with a question + options.
  2. Daemon posts a numbered question to Matrix and pauses upgrade cycles.
  3. Admin replies (a number or free text).
  4. 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 @paul in Element.
  • Secrets (config.json, .bot-secrets) are gitignored.
  • A single unanswered escalation blocks new upgrade cycles (health remediation continues regardless).