threepp
A cross-platform C++20 3D library with Python bindings and the high-level API of three.js.
When a service goes down, the server collects evidence, an LLM diagnoses it, and I get one message with the cause and the fix.
Developer toolsPython · ShellGitHub @moutaz121493OnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
A home server that diagnoses its own outages: Uptime Kuma alert → read-only evidence → redaction → LLM diagnosis (no tools) → incident page + one notification. Never executes fixes.
Taken from the repository README
Loading the full README.
Read the full README ↗When a service on my Raspberry Pi 5 home server goes down, this agent collects evidence, has an AI explain what happened, writes an incident page and sends one phone notification with the likely cause and the commands to fix it. It never runs anything itself: it only diagnoses, and a human decides.
It has run on the Pi since October 2026, watching about 30 services (Docker containers, systemd services, DNS, backups, disks, a Cloudflare tunnel).
http, dig, cert, backup_status) and lists
container/unit names, which are checked against a strict pattern. A config file can never add a command.rm -r, prune, fsck, mkfs, dd, umount, reading .env files, ...) are dropped
before anyone sees them.key=value secrets, notification topic
URLs, URL query strings, e-mail addresses, long hex strings and base64-like blobs.--max-budget-usd) and optionally per
month through a shared budget module. Plus: one diagnosis per monitor per 30 minutes, 10 per day.ProtectSystem=strict, so the whole filesystem is read-only for the
agent except its state and the incident pages.Real false alarms that changed the code:
ProtectSystem=strict sandbox every path
looks read-only. The collector now reads the host's mount table (findmnt --task 1, marked
"host view"), and the prompt only allows calling a disk read-only if those options start with ro,.known-issues.txt gives the model the server's history (e.g. "the HDD used to drop on the old power
supply", "get_throttled=0x80000 is a past soft temperature limit, not a power problem"), which made the
diagnoses noticeably more specific.jobwatch/ closes a gap Uptime Kuma has: is every scheduled job still running?
jobrun NAME MAXAGE command... wraps each cron job and records its last success in ~/.jobs/.jobwatch.py (cron, every 5 min) pushes up/down to a Kuma push monitor "Scheduled jobs", naming every
job whose last success is older than its MAXAGE and every watched systemd timer whose last run failed for
20+ minutes. If jobwatch itself stops, the push monitor goes down on its own.*/30 * * * * jobrun my-sync 7200 /usr/bin/python3 /opt/my-sync/sync.py
*/5 * * * * /usr/bin/python3 /usr/local/bin/jobwatch.py
When you remove a cron job, also delete its ~/.jobs/<name>.ok file, or jobwatch keeps reporting it as late.
Requirements: Python 3.11+ (standard library only), systemd, Docker (for container checks), Uptime Kuma with a status page that lists the monitors (the agent reads its public JSON API), and at least one of:
gemini-ask: any command that takes the system prompt as its last argument and the input on stdin,
accepts --tag, --timeout, --effort and --schema, and prints the JSON answer (mine runs the
Gemini CLI as a locked-down user), orclaude -p works for that user).sudo install -m 755 diag-agent.py /usr/local/bin/diag-agent.py
sudo mkdir -p /etc/diag /var/lib/diag /var/www/incidents
sudo cp examples/monitors.json examples/known-issues.txt /etc/diag/ # then edit both
sudo cp systemd/diag-watch.* /etc/systemd/system/
sudo systemctl daemon-reload && sudo systemctl enable --now diag-watch.timer
sudo diag-agent.py list # monitors from the config
sudo diag-agent.py collect "Paperless" # print the redacted evidence, no AI
sudo diag-agent.py test "Paperless" # evidence + AI diagnosis, printed, no notification
python -m unittest discover -s tests -v # standard library only; runs in CI on Python 3.11 – 3.13
They cover the parts that keep the agent safe: redaction of secrets, the JSON-schema validation and the command blocklist, config values that try to inject commands, HTML escaping and pruning of incident pages, and jobwatch.
Monitor names in monitors.json must match the monitor names in Kuma. A monitor that is not in the config
still gets the generic evidence.
| Variable | Default | |
|---|---|---|
KUMA_URL | http://127.0.0.1:3001 | Uptime Kuma |
KUMA_STATUS_PAGE | home | slug of the status page with the monitors |
DIAG_CONFIG / DIAG_KNOWN | /etc/diag/monitors.json / known-issues.txt | |
DIAG_SITE | /var/www/incidents | where incident pages are written (kept 90 days) |
DIAG_PUBLIC_URL | http://localhost/incidents | link in the notification |
DIAG_NOTIFY | /usr/local/bin/notify | notify <channel> <prio> <title> <msg> <tags> <url>, e.g. an ntfy wrapper |
DIAG_ENGINE / DIAG_FALLBACK | gemini / claude | claude / none |
DIAG_GEMINI_ASK | /usr/local/bin/gemini-ask | |
DIAG_CLAUDE_USER / DIAG_CLAUDE_BIN | claude-runner / ~/.local/bin/claude | |
DIAG_MODEL | sonnet | Claude model for the fallback |
DIAG_MONTHLY_USD | 3 | monthly cap if ai_budget.py is present in /usr/local/lib |
DIAG_BACKUP_STATUS | /var/backups/STATUS | file read by the backup_status check |
MIT