LabWired
Design circuits, run firmware, compare variants, and inspect the result in Playground. Open source. MCP for Claude or Codex. Free to try.
Self-hosted uptime monitoring bot (HTTP, TCP, Ping) integrated with Delta Chat. Features per-chat isolation, smart failure retry alerts, multi-transport SMTP failover, and responsive status pages.
Developer toolsPython · ShellGitHub @mrgluekOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
The maker's introduction consists of the line above. Read the repository README for more details. Repository README ↗
Loading the full README.
Read the full README ↗Delta Chat Uptime Bot is a self-hosted uptime monitoring bot (similar to Uptime Kuma) integrated directly with Delta Chat. It monitors resources (websites, APIs, mail servers, TCP ports, or ping targets) and alerts you inside Delta Chat if they go offline.
Additionally, it automatically generates a secure, beautiful web status dashboard for each chat.
/initadmin via private chat. Cryptographic fingerprint-based authentication protects administrative actions.200 OK responses (e.g. database connection errors, 502/503 wrapped in HTML, Cloudflare error screens)./add https://api.site.com Health "status:ok" or /keyword 1 "Welcome")./pause <id|url> [duration] (e.g. /pause 1 30m, /pause https://example.com 2h)./pause [duration] directly to incident alerts./resume.⚡ 124ms) and in /list./events (or /incidents) — View the chat's historical incident log, active outages, and total downtime durations./history [id] — Inspect recent downtime events for a specific monitor with failure reasons, error codes, and recovery timestamps.1.1.1.1, 8.8.8.8, 9.9.9.9, 1.0.0.1) before declaring any resource DOWN.2a00::1, [2a00::1]:443, https://[2a00::1]/). A node without IPv6 (no route, or Docker without enable_ipv6) does not report IPv6 targets as DOWN: its checks are skipped (⚪ in /ping, no alert, status unchanged) and other regions answer instead. Names with both A and AAAA records are checked over IPv4 there, so an unreachable IPv6 address no longer hides the real error.smtp://host[:port]): Checks a mail server the way a sending server does: it reads the 220 greeting, sends EHLO, requires STARTTLS in the reply, switches to TLS and verifies the certificate against trusted CAs and the target host name (what MTA-STS senders in mode: enforce require), then says QUIT. Port 25 is the default (smtp://chatmail.uk), smtp://host:587 works the same way, and port 465 uses TLS from the first byte. A plain host:25 TCP check only shows that the port is open; this one fails with the reason when the server stops offering STARTTLS (SMTP: STARTTLS not offered), its certificate expired or does not match the name (SSL: Hostname mismatch, ...), or it greets with an error. The session ends with QUIT, so the server logs an ordinary connection (ehlo=1 starttls=1 quit=1) that fail2ban does not count. The name sent in EHLO is the host's FQDN (or localhost); set SMTP_EHLO_NAME to change it.
/probeignore *:25 on that node (it covers smtp:// targets on port 25 too) so it abstains instead of voting DOWN.smtp:// targets for cross-checks (they announce no smtp_check capability) and do not vote on them.smtp://) targets:
/list and on the Web Status Dashboard./peers shows the number of paused chats./list and incident messages show it (🐢 checked every 15 min). Targets mirrored from peers slow down the same way and are checked once a day after 30 days, so a dead domain on one node does not keep the whole mesh busy./invitepeer on the other bot and send the /addpeer <link> token=<token> [node_name] command it prints to this bot; both bots then exchange telemetry./addpeer, or if it presents a single-use /invitepeer token (valid 24h). Telemetry and cross-check messages are only accepted from configured peers, in a 1:1 chat, end-to-end encrypted./peershare): By default peers only see monitors from chats the administrator is a member of (admin); all shares every chat, none shares no local monitors. Monitor names are never sent, and targets a peer asked this node to probe are only reported back to that peer, never relayed to others./alertmode quorum, default): When a target fails, this node and its live peers vote. If most nodes cannot reach it, it is 🔴 DOWN (incident). If only some cannot (e.g. 1–2 of 4, or only this node), it is 🟠 DEGRADED: one grouped notice in the chat, no incident and no downtime. Alerts show which nodes can and cannot reach the target and why they failed (🔴 DE, RU (Timeout) · 🟢 IT (196ms); with different reasons per node: 🔴 DE (Timeout), RU (SSL: self-signed certificate)). Nodes that are offline, cannot check the target (IPv6) or ignore it (/probeignore) abstain; with fewer than two voters this node decides alone. DEGRADED is entered and left after two consecutive votes. /alertmode local keeps the previous behaviour (this node decides).User-Agent header (e.g. Delta Chat Uptime Monitor/2.12.1 (+https://git.gluek.info/gluek/deltachat_uptime)) during HTTP checks so server administrators can easily identify monitoring requests in server logs. The name deliberately avoids the word "bot", because some services (e.g. Hiddify subscription links) answer 400 Bad Request to bot User-Agents.HEAD -> GET with Auto-Memorization): Zero body download (0 bytes) and 0 CPU decoding for standard HTTP checks via HEAD, with automatic 16 KB GET fallback on 405 or errors (not on a timeout or connection error, which a GET would only repeat), and 128 KB limit for keyword assertions. When a server responds with 405 Method Not Allowed to HEAD (e.g. GoToSocial, Mastodon, specialized APIs), the bot automatically memorizes GET for that monitor and database record so all subsequent checks use GET directly without generating repeated 405 log entries. The same happens when HEAD gets any other 4xx/5xx while the GET right after it succeeds (e.g. changedetection.io answers 500 to HEAD /login), so the server no longer logs a failing HEAD on every check; if both fail, the target is simply down and nothing is memorized.aiodns (AsyncResolver) support with 300s TTL cache prevents blocking DNS lookups and reduces repeated nameserver queries.aioping): Native in-process raw socket pinging eliminates subprocess creation overhead. Without raw socket permissions (hosted containers without CAP_NET_RAW) it uses an unprivileged ICMP datagram socket (allowed by net.ipv4.ping_group_range, the Docker default), then /bin/ping. A node with none of these skips ping checks (⚪ in /ping, abstains in votes) instead of reporting them as DOWN. So does a node in Docker Desktop (macOS / Windows, detected by its linuxkit kernel): its network proxy drops ICMP replies as soon as several echoes are in flight (68-89% lost in tests, 0% on a Linux host), which would be false DOWN and degraded votes. PING_ON_DOCKER_DESKTOP=1 checks them anyway; for path monitoring from a Mac run the bot natively instead.(r_id * 11) % interval) spreads checks across 5-second windows, preventing thundering-herd spikes on startup and interval boundaries._write_lock protects write transactions with a persistent connection (_writer_conn) while enabling concurrent lock-free reads for web dashboards and chat status commands.https://up.example.com/k8D2x9mPqL1a) hosting a modern dark-themed web status dashboard with active status, latency metrics, SSL countdowns, and recent incident logs for each chat.GET /health (and HEAD) answers {"status": "ok", "version": "X.Y.Z"} without touching the database or building a page, for monitors and other nodes. / is a static page cached in memory.
These commands are available to any member of a chat. They support suffixes (e.g. /add@up, /status@uptime) to route commands correctly if multiple bots exist in the same chat.
A plain /help sent in a group chat is answered in a private 1:1 chat with the sender, so several bots don't flood the group with help texts. Use /help@uptime to show the help in the group itself.
/ping [-4|-6] <target> ["keyword"] (or /check, /test) — Perform an immediate on-demand reachability and diagnostics test without adding to monitoring (rate-limited to 1 request per 15s per chat for non-admins; supports HTTP/HTTPS, SMTP STARTTLS (/ping smtp://chatmail.uk), SSL certificate inspection, keyword assertions, TCP port, ICMP Ping, and multi-region cross-checks). -6 / -4 check a name only over IPv6 / IPv4 (also on the other regions), e.g. /ping -6 chatmail.uk./add <target> [name] ["keyword"] — Add a monitor. Target formats:
• https://google.com Google (HTTP/HTTPS check)
• https://api.site.com Health "status:ok" (HTTP with keyword assertion)
• smtp://chatmail.uk Mail (SMTP: STARTTLS + certificate; smtp://host:465 for implicit TLS)
• google.com:443 Google TCP (TCP port check)
• google.com Google Ping (ICMP Ping check)
• 2a00::1 Server / [2a00::1]:22 SSH (IPv6 ping / TCP port; with a port the address goes in brackets, as in URLs)/remove <id|url> (or /delete, /rm, /del) — Stop monitoring a resource by ID or target URL. You can also reply /remove directly to any incident or outage alert message to remove the affected monitor without knowing its ID./pause <id|url> [dur] (or /mute, /maintenance) — Mute outage alerts during maintenance (e.g. /pause 1 30m, /pause https://example.com 2h, or reply /pause to an alert)./resume <id|url> (or /unpause) — Resume active monitoring after maintenance./keyword <id|url> [keyword|none] — Configure or remove expected keyword assertion./list — List monitored resources, status, latency, and SSL expiration in this chat./status — View monthly uptime statistics (online / degraded / offline, alert mode) and get the link to the chat's secure Web Status Page./alertmode [quorum|local] — How this chat is alerted: quorum (default) — DOWN when most nodes cannot reach a target, DEGRADED notice when some cannot; local — this node's checks only./events — View recent incidents and active outages for this chat./history [id] — View downtime history for monitors.These commands are only executable by the configured administrator.
/url — View current base external status URL./url <url> — Update the base external status URL (e.g., /url https://up.gluek.info) to generate correct status links./probeignore [url|host|pattern] — On this node, stop probing targets it cannot reach (e.g. blocked in its region or by its ISP) and abstain when peers vote on them. Accepts a target as shown in alerts (https://nine.testrun.org/, dnd.wb.ru:25), a host (nine.testrun.org: all its targets) or a pattern with * matched against the target, its host and host:port (*:25 — all SMTP port checks, *.testrun.org). Without arguments lists the entries; /probeunignore <entry> removes one./nodeignore [node_pattern] [target_pattern] — Centrally exclude targets from being checked on remote peer nodes matching node_pattern (wildcards * supported, e.g. /nodeignore RU* facebook.*, /nodeignore *home *:25, /nodeignore Node-1 https://site.org/). Cross-check requests for matching targets are not sent to those nodes (saving network messages), their telemetry reports for them are ignored in quorum, and any matching stale peer measurements are cleared. Without arguments lists all rules; with node_pattern lists matching rules./nodeunignore <node_pattern> [target_pattern] — Remove a node ignore rule (e.g. /nodeunignore RU* facebook.*), or remove all rules for a node pattern (e.g. /nodeunignore RU*)./nodename [name] — View or set the local probe node identifier (e.g. /nodename Frankfurt-DE). Peers (2.17.2+) pick up a new name from the next telemetry message; a name already used by another node is refused. Without DISPLAY_NAME the bot's display name follows it: Uptime Bot (<node name>)./invitepeer — Generate a SecureJoin E2E encrypted invite link (https://i.delta.chat/#...) with a single-use pairing token (valid 24h) for linking another bot./peers (or /probes) — List distributed monitoring peers, node names, key fingerprints, the relay address each one last used, their bot version (⚠️ when older than this node) and last seen activity./stats — Bot statistics and diagnostics: version and uptime, CPU of the bot and the Delta Chat core (last minutes and average), database sizes, monitors by type and state, checks per minute, mail received/sent in the last hour, outbox counters and queue, peers (online, unstable, which run an older version), telemetry interval, votes and held reports in the last hour, relays.Relay selection and failover are handled by the Delta Chat core (2.61+): it keeps sending through the relay that last worked and moves to the next one only if it cannot connect, so the relay used for sending can differ from the account's main address. /transports shows which relay that is, the core's send queue, and per relay the incoming status, the last received message and mailbox storage. The former /setprimary and /resilient commands are deprecated and only reply with this explanation.
[email protected]). Without one the bot creates its own chatmail account at first start.up.gluek.info).Clone the repository, enter the directory, and build the Docker container:
cd deltachat_uptime
docker compose build
Skip this step and the bot creates its account by itself at first start: the Delta Chat core (2.62+) picks the fastest chatmail relay from its built-in list and later adds further relays in the background.
To choose the account yourself, initialize it with your email and password, or with DCACCOUNT:server.tld for an account on a specific chatmail relay:
docker compose run --rm uptime_bot python bot.py init [email protected] "your_email_password"
docker compose run --rm uptime_bot python bot.py init DCACCOUNT:server.tld
Configure your admin email address and your cryptographic fingerprint on the server (or do it later inside the bot chat with the /initadmin command):
docker compose run --rm uptime_bot python set_admin.py --email [email protected]
docker compose run --rm uptime_bot python set_admin.py --fingerprint 1234ABCD1234ABCD1234ABCD1234ABCD1234ABCD
Start the bot daemon in background:
docker compose up -d
docker compose logs uptime_bot), open the root web dashboard (/) to scan the high-resolution vector QR code, or copy the invite link / email address into Delta Chat./initadmin to the bot in a private message.Tell the bot your public status domain so that it generates correct dashboard links:
/url https://up.gluek.info
Run ./update.sh (e.g. from cron) to deploy the newest release: the highest vX.Y.Z tag, from GitHub or, when GitHub is unreachable, from the Forgejo mirror; -f rebuilds the container without changes. Commits on main that are not released yet are not deployed. In .env.local, UPDATE_REF=v2.24.0 stays on that release (to roll back; remove it to follow new releases again), UPDATE_REF=main follows the branch, and MONITOR_URL pings a healthcheck on start, success and failure.
Releasing: merge into main, then git tag -a vX.Y.Z -m "..." <commit> && git push origin vX.Y.Z. Pushing the tag publishes the GitHub release with that version's CHANGELOG.md section (bot.py must have that VERSION) and sends the ntfy notification and the Home Assistant add-on sync.
You can customize the bot's name, avatar, and status text by passing environment variables (e.g. in your .env file or docker-compose.yml):
DISPLAY_NAME — Customize the display name of the bot (default: Uptime Bot (<node name>), updated by /nodename while the bot runs).STATUS_TEXT — Customize the status/about text of the bot.AVATAR_PATH — Path to an image file (PNG/JPG) to use as the bot's profile avatar (default: falls back to icon.png or icon.jpg in the project root).If you use Caddy on your host (like for your ntfy bot), you can expose the status pages by adding the following config to your /etc/caddy/Caddyfile:
up.gluek.info {
reverse_proxy 127.0.0.1:8080
}
Reload Caddy to apply changes:
sudo systemctl reload caddy
MIT, see LICENSE.
🟠 HOME (561 ms, loss 80%, usual 60 ms; trace: +589 ms between hop 6 171.75.9.210 and hop 7 213.19.194.214) · 🟢 DE (44ms)trace:traceroute⚪ RU (node network unstable, not counted)) and partial outage notices it alone caused are dropped, until it has met that threshold no more for 10 minutes. The admin gets one notice when this starts and one when it ends. Outages most nodes see are still reported. While a node is unstable, its telemetry reports of a target that a vote already found it alone failing are ignored for 30 minutes (10 minutes when the node itself answers UP to that vote: a short burst, a stale result or a bug of an older version; doubled each time in a row up to 2 hours, reset when it confirms a report; kept across restarts), so a noisy node does not make the others vote (and mail every peer) every two minutes. A peer also abstains, instead of voting DOWN, on a target whose name resolves to a private address there (split-horizon DNS at home, a VPN). A peer whose report names 10 or more targets that are UP here (broken DNS or network, which it confirms when asked again) is ignored altogether for 30 minutes, so it cannot make the node vote for dozens of targets every few minutes.IPv4: Timeout · IPv6: Connect call failed)./events and in /history <id> (not counted as downtime).caps in telemetry and check messages); peers that have not announced it yet get the classic one-target requests, so nodes can be updated one at a time.[📍 Frankfurt-DE: 18ms] [🛰️ RU-Moscow: 45ms]).synchronous=NORMAL): Uses SQLite WAL mode with PRAGMA synchronous = NORMAL, eliminating fsync overhead during commits and reducing continuous disk writes by >98%.CHECK_CONCURRENCY, connection pool 250, nofile 8192 in docker-compose.yml): 300 targets that accept connections but never answer finish in about 20 s instead of 130 s, so a dead data center does not delay the other checks.450 Too much mail above that; the core's retries count too, so a mass outage could delay alerts by minutes. Every message, edit and reaction therefore goes through one queue (outbox.py): 50 a minute with bursts of 5, sent by priority - alerts (new / resolved incidents, status changes, SSL, stale, partial outage, admin) → command replies → duration refreshes → peer protocol → telemetry. A queued update of the same incident message (or reaction, or telemetry to the same peer) is merged into the waiting one, so it goes out once with the latest text; peer requests and answers that waited past the peer's timeout are dropped. The minute summary in the log shows a backlog (outbox waiting 12 (alert 2, peer 10)).Checks in the last 60s: 148 (146 up, 2 down, 0 skipped), avg latency 87 ms, slowest …). With Docker's 30 MB log rotation the history then covers days instead of hours.uptime_db) and Delta Chat JSON-RPC / SMTP calls (uptime_rpc) prevent slow email delivery from stalling database operations./list, and /status./sync/donate — Support bot development ❤️/help — View available commands and system information./addpeer <email|link> [token=<token>] [node_name] — Link another Delta Chat Uptime bot as a remote probe using the command printed by its /invitepeer (e.g. /addpeer https://i.delta.chat/#... token=... RU). Without a token (e.g. /addpeer [email protected] RU) the other bot's admin must add this bot too./rmpeer <email|key> — Remove a remote peer probe by its current address or key fingerprint (the first 8+ hex digits shown by /peers are enough). Also stops probing targets it mirrored to this node./peershare [admin|all|none] — Choose which local monitors are disclosed to peers (default admin: only chats the administrator is a member of)./accounts — List active bot accounts./rmaccount <id> — Delete a bot account./transports — Show mail relays: relay used for sending, send queue, incoming status and storage./addtransport — Add backup mail relays (either chatmail URIs or address/password). Restricted to private 1:1 chat with the bot for security./rmtransport <addr> — Remove backup mail relay.