惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

宝玉的分享
宝玉的分享
酷 壳 – CoolShell
酷 壳 – CoolShell
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
S
Security @ Cisco Blogs
小众软件
小众软件
D
Docker
博客园_首页
A
About on SuperTechFans
P
Privacy International News Feed
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
A
Arctic Wolf
Spread Privacy
Spread Privacy
有赞技术团队
有赞技术团队
T
Tailwind CSS Blog
Latest news
Latest news
WordPress大学
WordPress大学
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
T
The Exploit Database - CXSecurity.com
C
Cybersecurity and Infrastructure Security Agency CISA
大猫的无限游戏
大猫的无限游戏
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
K
Kaspersky official blog
V2EX - 技术
V2EX - 技术
SecWiki News
SecWiki News
U
Unit 42
GbyAI
GbyAI
H
Hackread – Cybersecurity News, Data Breaches, AI and More
L
LINUX DO - 热门话题
S
Security Affairs
Y
Y Combinator Blog
aimingoo的专栏
aimingoo的专栏
Blog — PlanetScale
Blog — PlanetScale
MongoDB | Blog
MongoDB | Blog
博客园 - 【当耐特】
The GitHub Blog
The GitHub Blog
T
Tenable Blog
W
WeLiveSecurity
Cloudbric
Cloudbric
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
G
Google Developers Blog
Application and Cybersecurity Blog
Application and Cybersecurity Blog
N
Netflix TechBlog - Medium
F
Full Disclosure
N
News and Events Feed by Topic
D
DataBreaches.Net
P
Proofpoint News Feed
B
Blog RSS Feed
B
Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org

Hacker News: Show HN

PurrrrrFocus: Pomodoro Timer App - App Store Workflow Engine — Multi-Step Orchestration for Bun RapidPhoto: Pro Photo Editor App - App Store GitHub - DheerG/swarms: Achieve extraordinary results with claude code across a variety of tasks SPICE simulation → oscilloscope → verification with Claude Code — Lucas Gerads Show HN: VCoding – A 5 MB native Windows IDE with no dynamic dependencies Show HN: LLMs don't hallucinate because they're bad at math, it's the format GitHub - Agent-FM/agentfm-core: AgentFM is a peer-to-peer network that turns everyday computers into a decentralized AI supercomputer. AgentFM lets you run massive AI workloads directly across a global mesh of idle CPUs and GPUs. Show HN: Tracking Top US Science Olympiad Alumni over Last 25 Years GitHub - Potarix/agent-hub: One place to talk to all your agents Show HN: Runtime security for AI agents(injection,tool abuse, data exfiltration) GitHub - dubeyKartikay/lazyspotify: Terminal Spotify client for macOS and Linux GitHub - the-banana-tool/king-louie: Easy to use GUI Personal AI Assistant. Win/Linux/Mac. Show HN I made my vacation rental bookable by AI agents–no Airbnb, 0% commission GitHub - basteez/jsf-autoreload: maven plugin to enable hot reload on jsf projects uvm32/hosts/host-gdbstub at main · ringtailsoftware/uvm32 GitHub - labsai/EDDI: Config-driven engine that turns JSON into production-grade AI agents. Multi-agent orchestration, 12+ LLM providers, MCP/A2A protocols, RAG, persistent memory, and enterprise compliance (EU AI Act, GDPR, HIPAA). Built on Quarkus. GitHub - glitchnsec/fortyone-oss: AI Executive Assistant Platform Quickstart | Alien GitHub - muxshed/shed: One stream in, or many. Every destination, simultaneously. No cloud middleman, no per-channel fees, no limits. GitHub - ocrbase-hq/ocrbase: 📄 PDF/IMG ->.MD/JSON Document OCR API for PaddleOCR and GLMOCR. Self-hostable. GitHub - impactjo/home-memory: MCP server that lets your AI assistant remember everything about your home. GitHub - Sets88/dbcls: DbCls is a powerful terminal database client that supports various databases GitHub - neptun2000/heor-agent-mcp GitHub - SeanFDZ/macmind: Single-layer transformer in HyperTalk for the classic Macintosh RollQuation: Math Puzzles - Apps on Google Play GitHub - dropbox/witchcraft Show HN: Agent-cache – Multi-tier LLM/tool/session caching for Valkey and Redis GitHub - opentalon/opentalon: OpenTalon is an open-source platform built from the ground up in Go as a robust alternative to OpenClaw LinkedIn™ 职位抓取工具 - Chrome 应用商店 GitHub - EdoardoBambini/Agent-Armor-Iaga: AI agents are getting tool access — shell, file system, databases, APIs, secrets. But **nobody is governing what they actually do with it**. Frameworks like LangChain, CrewAI, AutoGen, and Claude Code give agents the power to execute. Agent Armor gives you the power to control, audit, and approve every single action before it happens. HN Vibes — Week 15, Apr 7–13 2026 GitHub - chojs23/ec: Easy terminal-native 3-way git mergetool vim-like workflow GitHub - SethPyle376/hiraeth: Local AWS emulator focused on fast integration testing, with SQS support, SQLite-backed state, and a debug-friendly web UI. GitHub - JakOb-dotcom/cloud-sandbox-security-analysis: Technical analysis and Proof of Concept (PoC) regarding environment variable exfiltration in containerized cloud sandboxes via side-channel data leaks. Springboards - Flint Alpha Show HN: A simpler coding agent harness GitHub - audiodude/sudomake-friends GitHub - 256thFission/mini-mythos: OSS clone of Anthropic’s Mythos harness to locate C/C++ memory vulnerabilities Show HN: OpenParallax: OS-level privilege separation for AI agent execution Hacker News Sorted - Chrome 应用商店 Show HN: How to Install Docker on Ubuntu 24.04 LTS: Complete 2026 Guide GitHub - himanshudongre/smriti GitHub - sverrirsig/claude-control: macOS desktop dashboard for monitoring and managing multiple Claude Code sessions GitHub - ory/dockertest: Write better integration tests! Dockertest helps you boot up ephermal docker images for your Go tests with minimal work. Chiral - Chrome 应用商店 Show HN: Two Claudes collaborating through shared memory on a $100 mini-PC GitHub - pmichaillat/latex-cv: Minimalist LaTeX template for academic CVs GitHub - oguzbilgic/posse: A web UI for Anthropic Managed Agents. GitHub - sshiraz/depsly: Dependency risk analysis tool for npm packages ABI Add safari/agent-harness — Safari browser automation via safari-mcp by achiya-automation · Pull Request #212 · HKUDS/CLI-Anything GitHub - Halfblood-Prince/trustcheck: Verify PyPI package attestations and improve Python supply-chain security GitHub - oguzbilgic/kern-ai: Agents that do the work and show it. GitHub - bruits/satteri: High-performance Markdown and MDX processing for the JavaScript ecosystem GitHub - tylergibbs1/feedstock: High-performance web crawler and scraper for TypeScript, powered by Bun and Playwright GitHub - Grimm67123/grimmbot: The self-improving sandboxed and open-source AI agent. With persistent memory and scheduling. GitHub - whitevanillaskies/whitebloom: Local whiteboard that blooms. GitHub - hwdsl2/docker-whisper: Docker image for a self-hosted Whisper speech-to-text server with speaker diarization and OpenAI-compatible transcription and translation APIs. Powered by faster-whisper. Supports all Whisper models, NVIDIA GPU (CUDA) acceleration, JSON/SRT/VTT output, SSE streaming, offline mode, and multi-arch (amd64, arm64). GitHub - yisding/reviewwiggum GitHub - MarwanAlsoltany/serrors: Structured errors for Go: sentinel hierarchies, typed data, custom formatting, and slog integration. GitHub - soatok/age-php GitHub - Luthiraa/markitme GitHub - stagas/rtdiff: realtime git diff gui and AI-assisted commits GitHub - tombedor/excalicharts GitHub - wh1le/excalidraw-edit: Open and edit .excalidraw files from the terminal. Offline, auto-saves to disk. MalExt Sentry - Malicious Extension Scanner - Chrome 应用商店 GitHub - syi0808/asciianimesvg: Generate animated ASCII art SVGs from text. CLI, Rust library, WASM, and web editor. GitHub - zaina-ml/ml_forge: A visual-based graph node editor for training computer vision models. GitHub - anakin87/llm-rl-environments-lil-course: 🌱 A little course on Reinforcement Learning Environments for evaluating and training Language Models GitHub - takaakit/superpowers-uml: Superpowers-UML modifies Superpowers to ensure a software development workflow in which AI agents design through UML modeling. AdriByte Studio - Sviluppo Web e Soluzioni Digitali GitHub - chouligi/angel-copilot: Your personalized Angel Investment Advisor Show HN: MoodSense AI (ML and FastAPI and Gradio, Deployed on Hugging Face) Moodsense Ai - a Hugging Face Space by aman179102 GitHub - agenteractai/lodmem: Level Of Detail Context Management for Agents GitHub - ostefani/subnetlens: A fast, concurrent network scanner with a TUI and plain-text CLI, built in Go. It discovers live hosts on your network, scans their open ports, resolves hostnames, and fingerprints operating systems—delivered. Cyber Pulse: Agentic Intel - Apps on Google Play Whisper API: Self-Hostable Speech to Text Transcription The Agent-Web Protocol Stack: A Research Thesis GitHub - msmarkgu/RelayFreeLLM: A restful API designed to route user prompts to various AI model providers. Show HN: Provepy – A Python decorator that proves your code using Lean and LLMs Show HN: Pardonned.com – A searchable database of US Pardons GitHub - patrickdappollonio/dux: Dux is a terminal UI that lets you run multiple AI coding agents side by side, each in its own git worktree, with full companion terminals, macros, commit generation, and a command palette that knows more tricks than you do. kMC Crystal Simulator Show HN: HyperFlow – A self-improving agent framework built on LangGraph GitHub - stef41/vibescore: 🎵 Grade your vibe-coded project. One command, instant letter grade across security, quality, dependencies, and testing. GitHub - stef41/lmscan: 🔍 Detect AI-generated text and fingerprint which LLM wrote it. Open-source GPTZero alternative. Zero dependencies, works offline. imgur.com GitHub - visionscaper/collabmem: Enabling long-term collaboration with Agentic AI - building up episodic and world model memory over time with in-context awareness 在 Steam 上购买 FriedrichAI: Offline AI 立省 10% GitHub - atripati/ark: AI Runtime Kernel — a context operating system for AI agents. Eliminates tool bloat, loads only what’s needed, and gives LLMs their reasoning space back. GitHub - nowork-studio/toprank: Open-source Claude Code skills for SEO, SEM, Google Ads GitHub - tacomanator/sash: Lightweight macOS menu bar app for reliably cycling through windows of the current application. Appents | Social Media Management for Product-First Teams GitHub - pnhoang/youtube-spam-blocker: Automatically detects and hides spam messages in YouTube Live chat. Set rate limits, keyword filters, and block repeat offenders. GitHub - decisionnode/DecisionNode: CLI + Local MCP - A shared structured memory store across Claude Code, Cursor, Windsurf, Antigravity, and every MCP client. Semantically queryable. GitHub - AvaCodeSolutions/django-email-learning: An open source Django app for creating email-based learning platforms with IMAP integration and React frontend components. The $100K Gap in Kubernetes Security Tooling Function Calling Harness: From 6.75% to 100%
GitHub - Axodouble/QUptime: QUptime (Quorum Uptime) is a decentralized uptime & alerting monitor, allowing for alerting of different nodes going down, even if one node fails.
Axodouble · 2026-05-15 · via Hacker News: Show HN

qu — quorum-based uptime monitor

qu is a small Linux daemon that watches HTTP, TCP, and ICMP endpoints from several cooperating nodes. The nodes form a quorum cluster; one is elected master and owns alert dispatch. A check is only reported as DOWN when the majority of nodes agree, which keeps a single node's flaky uplink from paging anyone at 3am.

A single static binary contains the daemon, the CLI, and everything in between. Inter-node traffic is mutual TLS with SSH-style fingerprint trust — no central CA, no shared secret.

Installation

From pre-built binary

The canonical home is Gitea; the repo is push-mirrored to GitHub on every tag. Releases and multi-arch container images are published to both.

Source Releases Container image
Gitea (primary) https://git.cer.sh/axodouble/quptime/releases git.cer.sh/axodouble/quptime
GitHub (mirror) https://github.com/Axodouble/QUptime/releases ghcr.io/axodouble/quptime

One-step install — tries Gitea first, falls back to GitHub automatically:

curl -fsSL https://git.cer.sh/Axodouble/QUptime/raw/branch/master/install.sh | sudo bash
# or, via the GitHub mirror:
# curl -fsSL https://raw.githubusercontent.com/Axodouble/QUptime/master/install.sh | sudo bash

The script verifies the binary against the published SHA256SUMS before installing and refuses to proceed on a mismatch.

From Docker

docker pull git.cer.sh/axodouble/quptime:latest
# or, via the GitHub mirror:
# docker pull ghcr.io/axodouble/quptime:latest

See docs/deployment/docker.md for compose recipes.

Why

Most uptime monitors are either a SaaS or a single box that, by definition, can't tell you when it's the one that's down. qu solves both: run it on a few cheap hosts in different networks and they vote on truth. If one of them loses its uplink, the rest keep alerting.

Documentation

This README is the quick-start. For production use, the longer guides live under docs/:

If you want to… Read
understand the consensus / replication model docs/architecture.md
reference every field in node.yaml / cluster.yaml docs/configuration.md
deploy on Linux with systemd hardening docs/deployment/systemd.md
deploy with Docker / docker-compose docs/deployment/docker.md
deploy over Tailscale or WireGuard docs/deployment/tailscale.md
expose qu on the open internet safely docs/deployment/public-internet.md
upgrade, back up, or recover from failures docs/operations.md
understand the trust model and rotate identities docs/security.md
diagnose a misbehaving cluster docs/troubleshooting.md

Architecture

   +-------------- node A ---------------+
   | qu serve                            |
   | ├─ transport server (mTLS :9901)    |
   | ├─ quorum manager  (heartbeats)     |
   | ├─ replicator      (cluster.yaml)   |
   | ├─ scheduler       (HTTP/TCP/ICMP)  |  <─── probes targets
   | ├─ aggregator      (master-only)    |
   | ├─ alerts          (master-only)    |
   | └─ control socket  (unix, for CLI)  |
   +-------------------------------------+
              │  ▲   mTLS, pinned by fingerprint
              ▼  │
        node B   node C   …

Every node runs every probe. Results are shipped to the elected master, which folds them into a per-check sliding window. A state flips (UP↔DOWN) only after two consecutive aggregate evaluations agree — that's the hysteresis that absorbs network blips.

Master election is deterministic: among the live members of the quorum, the node with the lexicographically smallest NodeID wins. No negotiation, no split-brain window.

cluster.yaml is the single replicated source of truth (peers, checks, alerts). Mutations from the CLI route through the master, which bumps a monotonic version and broadcasts the result. The same file is also watched on disk, so an operator can sudoedit cluster.yaml on any node and the daemon will replicate the edit cluster-wide.

Build

Requires Go 1.24.2 or newer.

go build -o qu ./cmd/qu

To stamp the version into the binary:

go build -ldflags "-X main.version=v0.0.1" -o qu ./cmd/qu
qu --version

Releases

Pushing a tag matching v* triggers .gitea/workflows/release.yaml, which runs the test suite, cross-compiles static Linux binaries for amd64 and arm64, and publishes them as a Gitea release with a SHA256SUMS file alongside.

git tag v0.0.1
git push --tags

Set up a 3-node cluster

On the first host:

qu init --advertise alpha.example.com:9901

That prints a random cluster secret. Copy it.

On every other host, pass that secret via --secret:

qu init --advertise bravo.example.com:9901  --secret <paste>
qu init --advertise charlie.example.com:9901 --secret <paste>

Without the matching secret a node cannot join, so random hosts that can reach :9901 are safely ignored.

Start the daemon on every host (foreground; wire into systemd for prod):

qu serve

Then on one node — usually alpha — invite the others. The CLI prints each remote's fingerprint and asks for confirmation SSH-style:

qu node add bravo.example.com:9901
qu node add charlie.example.com:9901

After the first invite, give it a few seconds for heartbeats to bring the new peer into the live set before inviting the next one — otherwise the local node's "needs ≥2 live to mutate" check will reject the second add.

You only need to invite from one node. Peer certs ride along with the replicated cluster.yaml, so every peer auto-trusts every other peer without N×(N-1) invites.

That's it — the master broadcasts the new cluster config to every trusting peer. qu status from any node should now show all three:

node       a7f3...
term       2
master     a7f3...
quorum     true (need 2)
config ver 4

PEERS
NODE_ID  ADVERTISE                 LIVE  LAST_SEEN
a7f3...  alpha.example.com:9901    true  2026-05-12T15:01:32Z
b21c...  bravo.example.com:9901    true  2026-05-12T15:01:32Z
c0d4...  charlie.example.com:9901  true  2026-05-12T15:01:32Z

Adding checks and alerts

⚠️ Alert credentials are replicated cluster-wide. SMTP passwords and Discord webhook URLs live in cluster.yaml, which is mirrored to every node. Any node that can read its own data directory can read every alert secret. Treat compromising one node as compromising every alert credential, and restrict who can reach $QUPTIME_DIR on each host (the hardened systemd unit and the Docker image both default to 0700/0750). See docs/security.md for the full threat model.

# alerts first so checks can reference them
qu alert add discord oncall --webhook https://discord.com/api/webhooks/...
qu alert add smtp   ops    --host smtp.example.com --port 587 \
                            --from monitor@example.com --to ops@example.com \
                            --user mailbot --password '****' --starttls=true

# checks
qu check add http  homepage https://example.com  --expect 200  --alerts oncall,ops
qu check add tcp   db       db.internal:5432     --interval 15s
qu check add icmp  gateway  10.0.0.1             --interval 5s

Mutations always route to the master, which bumps a monotonic version and pushes the new cluster.yaml to every peer. If quorum is lost, mutating commands fail loudly.

qu status shows the effective alert list for each check. Default alerts are suffixed with * so you can tell at a glance which alerts were attached automatically vs explicitly listed on the check:

CHECKS
ID        NAME      STATE  OK/TOTAL  ALERTS         DETAIL
ddbd...   homepage  up     3/3       oncall,ops*    
0006...   db        down   1/3       ops*           dial timeout
24f4...   gateway   up     3/3       -              
(alerts marked * are attached as defaults)

Default alerts (attach to every check)

Rather than listing the same --alerts on every check add, mark an alert as default and it fires for every check automatically:

# at creation
qu alert add discord oncall --webhook https://... --default

# or toggle later
qu alert default oncall on
qu alert default oncall off

qu alert list shows a DEFAULT column. A check can opt out of a specific default by adding the alert's ID or name to its suppress_alert_ids list in cluster.yaml (see "Edit cluster.yaml directly" below).

Interactive TUI

Prefer a dashboard over typing commands? qu tui opens a full-screen bubbletea UI over the local daemon socket. The header shows quorum, master, term, and config version; three tabs hold peers, checks, and alerts with auto-refresh every two seconds.

┌─ QUptime ── node: 88a00af9   master: 3438fd6f   (follower)  ● quorum 3/2  term 4   ver 10 ──┐
│ Peers (3)   [2] Checks (3)   [3] Alerts (1)                                                  │
├──────────────────────────────────────────────────────────────────────────────────────────────┤
│ ID         NAME      STATE     OK/TOTAL  ALERTS    DETAIL                                    │
│ ddbd...    homepage  ● up      3/3       oncall*                                             │
│ 0006...    db        ● down    1/3       oncall*   dial timeout                              │
│ 24f4...    gateway   ○ unknown 0/0       -                                                   │
└──────────────────────────────────────────────────────────────────────────────────────────────┘
↑↓ navigate   ⇥ next tab   1/2/3 jump   r refresh   a add check  d remove check   q quit

Keybindings:

Key Action
/ move cursor within a tab
Tab / Shift+Tab next / previous tab
1 / 2 / 3 jump to Peers / Checks / Alerts
r force-refresh
a add (opens a picker on Checks/Alerts; node form on Peers)
d remove the selected row (confirmation prompt)
t send a test message to the selected alert
D toggle the selected alert's default flag
q / Ctrl+C quit

Forms run the same control-plane methods the CLI does, so any side effect (a mutation, a node add, an alert test) ends up routed through the master exactly like qu … from the shell.

Custom alert messages

Each alert can carry its own subject_template and body_template (Go text/template syntax). When set, they override the built-in formatting for that one alert; the default renderer is used otherwise. Discord ignores the subject template (it has no subject line).

qu alert add discord oncall --webhook https://... \
    --body ':rotating_light: **{{.Check.Name}}** is now {{.Verb}}
target: `{{.Check.Target}}`
detail: {{.Snapshot.Detail}}'

# multi-line templates are easier from a file
qu alert add smtp ops --host ... --from ... --to ... \
    --subject-file /etc/quptime/templates/ops.subject \
    --body-file    /etc/quptime/templates/ops.body

Available template variables:

Variable Meaning
{{.Check.Name}} check name
{{.Check.Type}} http / tcp / icmp
{{.Check.Target}} URL or host:port being probed
{{.Check.ID}} UUID
{{.From}} previous state (up / down / unknown)
{{.To}} new state
{{.Verb}} UP / DOWN / RECOVERED
{{.VerbLower}} lowercase form (up / down / recovered)
{{.Snapshot.Reports}} total per-node reports counted
{{.Snapshot.OKCount}} how many reported OK
{{.Snapshot.NotOK}} how many reported failure
{{.Snapshot.Detail}} first failure detail string
{{.NodeID}} master that dispatched
{{.When}} RFC3339 timestamp

The same variable list is surfaced in-app: qu alert add smtp --help, qu alert add discord --help, and qu alert edit --help each print it under their flag table, and qu tui shows a compact reminder of the supported variables as a hint when the cursor lands on a Subject or Body template field in the add/edit alert forms.

qu alert test <name> exercises the template against a synthetic "homepage going DOWN" transition, so you can verify rendering before production traffic depends on it. A template parse or execution error falls back to the built-in format and is logged.

Conditionals, pipelines, and worked examples

Templates use Go's text/template syntax, so you have if/else if/ else/end, comparison helpers (eq, ne, lt, gt), printf pipelines, and with blocks. The default rendering — the one used when no custom template is set — lives in internal/alerts/message.go inside the Render function; tweak it there if you want to change what every alert without an override produces.

A few progressively richer examples:

1. State-specific Discord copy — different tone for DOWN, RECOVERED, and first-time UP:

body_template: |
  {{if eq .Verb "DOWN"}}:rotating_light: **{{.Check.Name}}** is DOWN
  We're investigating. Last detail: `{{.Snapshot.Detail}}`
  {{else if eq .Verb "RECOVERED"}}:white_check_mark: **{{.Check.Name}}** is back UP after a {{.From}} blip.
  {{else}}:information_source: **{{.Check.Name}}** is online ({{.VerbLower}}).{{end}}

2. SMTP subject with severity prefix and run-length detail — pipes Verb through printf for padding and only mentions the report count when it actually matters:

subject_template: '[{{printf "%-9s" .Verb}}] {{.Check.Name}} — {{.Check.Target}}'
body_template: |
  Check:    {{.Check.Name}} ({{.Check.Type}})
  Target:   {{.Check.Target}}
  Status:   {{.Verb}} (was {{.From}})
  Reporter: {{.NodeID}}
  At:       {{.When}}
  {{if gt .Snapshot.Reports 1}}
  Quorum:   {{.Snapshot.OKCount}} ok / {{.Snapshot.NotOK}} failing across {{.Snapshot.Reports}} reports.
  {{end}}{{with .Snapshot.Detail}}
  Detail:   {{.}}
  {{end}}

3. PagerDuty-style severity routing — nest if/else if so a single template can produce three different first lines without duplicating the rest of the body:

subject_template: >-
  {{if eq .Verb "DOWN"}}P1: {{.Check.Name}} hard down
  {{else if eq .Verb "RECOVERED"}}P3: {{.Check.Name}} recovered
  {{else}}P4: {{.Check.Name}} {{.VerbLower}}{{end}}
body_template: |
  {{/* Header line — uses .VerbLower so the prose reads naturally */}}
  {{.Check.Name}} ({{.Check.Target}}) is now {{.VerbLower}}.

  {{if eq .Verb "DOWN"-}}
  This is a real outage. Quorum: {{.Snapshot.NotOK}}/{{.Snapshot.Reports}} reporters see it failing.
  Detail from the first failing probe: {{.Snapshot.Detail}}
  Acknowledge in the runbook before paging on-call.
  {{- else if eq .Verb "RECOVERED" -}}
  Recovered after a {{.From}} period. No action needed; this is informational.
  {{- else -}}
  First successful probe after {{.From}}. Marking healthy.
  {{- end}}

  — {{.NodeID}} at {{.When}}

The {{- / -}} trim adjacent whitespace, which keeps the rendered output tidy even when the template itself is indented for readability.

If a template fails to parse or panics at execute time, the dispatcher falls back to the default Render output for that field and logs the error — your alert still ships, you just lose the custom formatting until you fix the template.

Edit cluster.yaml directly

Anything you can do through the CLI you can also do by editing $QUPTIME_DIR/cluster.yaml on any node. The daemon polls the file every few seconds; when it sees a hash that differs from what it last wrote, it parses the YAML and forwards the change through the master, which bumps the version and broadcasts the result everywhere — so a hand-edit on bravo propagates to alpha and charlie automatically.

sudoedit /etc/quptime/cluster.yaml
# add `default: true` to an alert, or `suppress_alert_ids: [oncall]`
# on a check, then save and quit

You'll see a manual-edit: cluster.yaml changed externally — replicating via master line in the daemon log when it picks the change up. Invalid YAML is logged and ignored until you save a valid file.

The replicated fields are peers, checks, and alerts. version, updated_at, and updated_by are server-controlled — the master overwrites them on commit.

Test an alert without waiting for a real outage

qu alert test oncall

File layout

A node's state lives under $QUPTIME_DIR (defaults to /etc/quptime when root, ~/.config/quptime otherwise):

node.yaml      identity (NodeID, bind addr, port). Never replicated.
cluster.yaml   replicated state: peers, checks, alerts, version.
trust.yaml     local fingerprint trust store.
keys/          RSA private + public + self-signed cert.

The CLI talks to the local daemon over a unix socket at $QUPTIME_SOCKET (defaults to /var/run/quptime/quptime.sock when root, $XDG_RUNTIME_DIR/quptime/quptime.sock otherwise) — filesystem permissions guard it; no TLS on the local socket.

ICMP and capabilities

ICMP checks default to unprivileged UDP-mode pings so the daemon does not need root or CAP_NET_RAW. If you want classic raw ICMP, either run the daemon as root or grant the capability:

sudo setcap cap_net_raw=+ep ./qu

CLI reference

qu init                                       generate identity + keys
qu serve                                      run the daemon
qu status                                     quorum, master, check states
qu tui                                        interactive dashboard
qu node add    <host:port>                    TOFU-add a peer
qu node list                                  show peers + liveness
qu node remove <node-id>                      remove from cluster + trust
qu check add http  <name> <url>  [--expect 200] [--interval 30s] [--body-match str] [--alerts a,b]
qu check add tcp   <name> <host:port>
qu check add icmp  <name> <host>
qu check list
qu check remove <id-or-name>
qu alert add smtp    <name> --host … --port … --from … --to … [--user --password --starttls] [--default] [--subject … --body …]
qu alert add discord <name> --webhook …                                                        [--default] [--body …]
qu alert list / remove / test <id-or-name>
qu alert default <id-or-name> on|off            toggle default attachment to every check
qu trust list / remove <node-id>

All --interval and --timeout flags accept Go duration syntax: 5s, 1m30s, 2h, etc.

Tests

go test ./...
go test -race ./...

Each internal package has unit tests; coverage hovers around 60–90 % on the meaningful packages. The transport tests bring up real mTLS listeners over loopback, which exercises the cert pinning end-to-end.

What's intentionally not here (v1)

  • No web UI. The CLI is the only operator surface.
  • No historical metrics or SLA reports — only the current aggregate state is kept in memory. Add SQLite later if you need graphs.
  • No automatic key rotation. Re-init a node and re-trust if you need to roll its identity.
  • No multi-tenant isolation. One cluster = one set of checks.

Layout

cmd/qu/                    entry point
internal/config/           on-disk file layout, ClusterConfig, NodeConfig
internal/crypto/           RSA keypair + self-signed cert + SPKI fingerprints
internal/trust/            fingerprint trust store
internal/transport/        mTLS listener/dialer, framed JSON-RPC
internal/quorum/           heartbeats + deterministic master election
internal/replicate/        master-routed mutations, version-gated replication
internal/checks/           HTTP/TCP/ICMP probers, scheduler, aggregator
internal/alerts/           SMTP + Discord dispatchers, message rendering
internal/daemon/           glue: wires every component + control socket
internal/cli/              cobra commands, the user-facing surface
internal/tui/              bubbletea dashboard (qu tui)