An autonomous uptime guardian you deploy on a Debian VM in one command. It watches your services and agents 24/7 (HTTP, TCP, systemd, or any command), restarts them when they crash, opens an incident ticket, alerts your webhook, and tracks uptime. The core is deterministic and works out of the box; it gets smart when you connect your existing coding-agent subscription — Claude Code, Kimi Code, or Codex — with no API key.
Agents and services that run 24/7 need a guardian for the hours when their human is asleep. This pack is that guardian.
Architecture
targets.json (HTTP / TCP / systemd / command)
|
v
┌─────────────────────────────────────────────────────────────────┐
│ SRE AGENT v1.0 │
│ │
│ ┌────────┐ ┌──────────────┐ ┌──────────┐ ┌────────────┐ │
│ │ Probes │──▶│ Failure │──▶│ Decision │──▶│ Actions │ │
│ │ (OODA) │ │ streaks (DB) │ │ engine │ │ │ │
│ └────────┘ └──────────────┘ └──────────┘ │ restart │ │
│ │ ticket │ │
│ ┌────────────────────────────────────────┐ │ webhook │ │
│ │ Anti-flap budget (max N restarts/hour) │◀────│ diagnose* │ │
│ └────────────────────────────────────────┘ └────────────┘ │
│ │
│ * diagnose & report = LLM (optional, BYO CLI) — see PROMPTS.md │
└─────────────────────────────────────────────────────────────────┘
- Probes : HTTP status, TCP connect,
systemctl is-active, arbitrary command - Failure streaks : a target is down only after
fail_thresholdconsecutive failures (no single-blip pages) - Auto-restart : per-target
restart_command, capped bymax_restarts_per_hour(anti-flap) - Tickets : one open incident per outage, markdown ticket in
tickets/, closed on recovery - Alerts : JSON POST to
SRE_WEBHOOK_URLon open/close (Slack/Discord/anything) - Uptime : per-target uptime % and latency over any window (
status,report)
One-shot install (Debian/Ubuntu)
unzip agent-sre-v1.zip && cd agent-sre-v1
cp targets.example.json targets.json # describe what to watch
sudo bash install.sh # system deps + venv + systemd service (enabled)
That's it. The guardian starts as a systemd service (agent-sre-v1), survives
reboots, and restarts on failure. Logs: journalctl -u agent-sre-v1 -f.
No root / no systemd?
bash install.sh --no-servicesets up the venv, then runvenv/bin/python agent.py run.
Connect your LLM (optional, no API key)
install.sh auto-detects the first CLI it finds. Install & log in to one:
| CLI | Install | Connect |
|---|---|---|
| Claude Code | npm i -g @anthropic-ai/claude-code |
run claude, /login |
| Kimi Code | see platform.moonshot.ai | kimi-code |
| Codex | npm i -g @openai/codex |
codex login |
Force a specific one with PACK_LLM=claude|kimi|codex. If none is connected,
the guardian still runs — see the honesty table below.
What's LLM vs deterministic (honest by design)
| Capability | Without an LLM CLI | With an LLM CLI connected |
|---|---|---|
| Probing (HTTP/TCP/systemd/command) | ✅ deterministic | ✅ deterministic |
| Failure streaks + thresholds | ✅ deterministic | ✅ deterministic |
| Auto-restart + anti-flap budget | ✅ deterministic | ✅ deterministic |
| Incident tickets (open/close) | ✅ deterministic | ✅ deterministic |
| Webhook alerts | ✅ deterministic | ✅ deterministic |
| Uptime stats | ✅ deterministic | ✅ deterministic |
| Root-cause diagnosis on each incident | not available | the LLM runs the Diagnostician prompt (PROMPTS.md), result appended to the ticket |
| Written uptime reports | plain-text summary | the LLM runs the Reporter prompt (trends, recommendations) |
If a CLI is installed but not logged in, the agent logs a warning and continues without the diagnosis — monitoring never blocks on the LLM.
Quick commands
python agent.py check # one pass over all targets
python agent.py run --once # one full cycle (probe + decide + act)
python agent.py status # uptime % + open incidents + LLM backend
python agent.py tickets --open # open incidents
python agent.py report --hours 24 # uptime report (LLM-written if connected)
python agent.py close --ticket inc-ab12cd34 # close a ticket manually
bash smoke_test.sh # offline test suite (no keys)
python EXAMPLE.py # runnable end-to-end demo
Contents
| File | Purpose |
|---|---|
agent.py |
Runnable guardian — probes, streaks, restart, tickets, alerts, OODA, BYO-LLM wiring |
llm_adapter.py |
BYO-LLM CLI adapter (claude/kimi/codex), no API key |
targets.example.json |
Example targets config (HTTP, TCP, systemd, command) |
install.sh |
One-shot Debian installer (deps + venv + systemd) |
agent-sre-v1.service |
Reference systemd unit (install.sh generates the real one) |
smoke_test.sh / test_agent.py |
Offline test suite (deterministic) |
EXAMPLE.py |
End-to-end runnable demo of the shipped pipeline |
PROMPTS.md |
The Sentinel / Diagnostician / Escalation / Reporter prompts the agent actually loads |
SOUL.md |
Guardian identity, values, OODA loop |
SPEC.md |
Technical spec: target types, thresholds, restart policy, interfaces |
CHECKLIST.md |
Deployment checklist |
CUSTOMIZE.md |
Where to plug in your own services (5 minutes) |
SUCCESS_METRICS.md |
KPI targets + failure definition |
OPERATING_COST_ESTIMATE.md |
LLM cost estimate (only relevant in LLM mode) |
requirements.txt |
Python deps (stdlib only) |
deploy.sh |
Thin wrapper → install.sh |
Use a local model (Ollama)
The optional LLM step can also run on your own machine through Ollama, so no text leaves your server. No account and no API key.
ollama pull <model> # any chat model you like
export PACK_LLM=ollama # prefer Ollama even if a coding-agent CLI is installed
export PACK_OLLAMA_MODEL=<model> # optional: default is the first chat model the server lists
python3 -c "import llm_adapter; print(llm_adapter.detect())" # prints: ollama
PACK_OLLAMA_HOSTpoints to another server (defaulthttp://127.0.0.1:11434);OLLAMA_HOSTis also read whenPACK_LLM=ollama.PACK_OLLAMA_NUM_CTXsets the context size if your reports or prompts are long.- Without
PACK_LLM, a local Ollama server is used only when no Claude Code, Kimi Code or Codex CLI is found. - If the server is down or the model is missing, the pack keeps working in its deterministic mode.
Learning memory (local and private)
Every LLM call this pack makes is journaled in ./.pack_memory/ next to the code, so the pack can improve for you. Nothing is ever sent anywhere.
- What is kept: the task name, the masked input, the masked answer, status and latency. Keys, tokens, e-mails, phone numbers, IP addresses, card and IBAN numbers and personal paths are masked before writing; the system prompt is never stored.
- Teach it:
python3 pack_memory.py feedback last okconfirms a result,python3 pack_memory.py feedback last bad --fix "the right answer"corrects it. Confirmed and corrected runs are reused as validated examples when a similar input comes in. Unrated runs are never injected into a prompt. - Find similar cases:
python3 pack_memory.py similar "some text"andpython3 pack_memory.py clusters(groups of near-identical failures) work in plain Python, no model needed. Optional meaning-based matching uses an embedding model you already run locally:python3 pack_memory.py enable-semantic(Ollama on this machine only). - Share, only if you want:
python3 pack_memory.py exportwrites an anonymised summary (counts, statuses, size buckets, no text) for you to review. - Control:
PACK_MEMORY=0turns it off,PACK_MEMORY_STORE_TEXT=0keeps fingerprints only,python3 pack_memory.py purge --yeswipes it.
