Skip to content

Commit 1bfa9a2

Browse files
committed
Merge pull request 'v0.3.0 release' (#33) from v0.3.0 into master
Reviewed-on: https://git.cer.sh/Axodouble/QUptime/pulls/33
2 parents 77f31e7 + 5b16fbe commit 1bfa9a2

35 files changed

Lines changed: 2645 additions & 201 deletions

.gitea/workflows/release.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,5 +55,5 @@ jobs:
5555
files: |-
5656
dist/qu-*
5757
dist/SHA256SUMS
58-
pre_release: ${{ contains(github.ref_name, '-rc') }}
58+
pre_release: ${{ contains(github.ref_name, '-rc') || contains(github.ref_name, '-beta') }}
5959
api_key: "${{ secrets.GITHUB_TOKEN }}"

.github/workflows/release.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,5 +57,5 @@ jobs:
5757
dist/SHA256SUMS
5858
fail_on_unmatched_files: true
5959
generate_release_notes: true
60-
prerelease: ${{ contains(github.ref_name, '-rc') }}
60+
prerelease: ${{ contains(github.ref_name, '-rc') || contains(github.ref_name, '-beta') }}
6161
token: ${{ secrets.GITHUB_TOKEN }}

CHANGELOG.md

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,84 @@ All notable changes to this project are documented here. The format
44
follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and
55
this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [v0.3.0] — 2026-05-25
8+
9+
### Added
10+
11+
- New documented deployment methods for Tailscale and EdgeVPN, with example `docker-compose.yml` files and wrapper scripts in `docker/tailscale/` and `docker/edgevpn/`.
12+
- New builder command `qu builder`, which generates a standalone HTML alert-template builder. The builder ships with a rich expression palette (more logic operators, comparison helpers, and `with` blocks) so you can author multi-branch subject / body templates and copy the rendered Go `text/template` directly into `qu alert add … --body-file`.
13+
- **Custom DNS resolvers for check target resolution.** Every probe
14+
(HTTP / TCP / TLS / ICMP / DNS) can now bypass the host's stub
15+
resolver and the local cache by pointing at explicit DNS servers.
16+
Two new fields, both `omitempty`:
17+
- `cluster.yaml.resolvers` — cluster-wide default list, edited via
18+
`qu cluster resolvers set <r1> [<r2> …]` / `clear` / `show`.
19+
- `checks[].resolvers` — per-check override, set via the new
20+
`--resolvers` flag on `qu check add <type>` and `qu check edit`.
21+
22+
Lookup precedence on each probe: per-check → cluster default →
23+
(DNS checks only) legacy `dns_resolver` → host system resolver.
24+
Each entry is `host[:port]` — a bare host gets `:53` appended at
25+
use time. Lists are tried in order with **connection-level
26+
failover**, so `[1.1.1.1, 1.0.0.1]` rolls over to Cloudflare's
27+
secondary when the primary is unreachable. Literal IP targets
28+
skip the resolver entirely. ICMP only consults the resolver list
29+
when an override is configured; existing ICMP checks with no
30+
override behave unchanged.
31+
- **Pause checks and alerts without deleting them.** Both `Check` and
32+
`Alert` carry a new `disabled` (yaml `disabled,omitempty`) field.
33+
Disabled checks are skipped by the scheduler — their workers are
34+
cancelled on the next reconcile pass — so no probes run and the
35+
aggregator stops receiving fresh results. Disabled alerts are
36+
filtered out by `EffectiveAlertsFor` before the dispatcher sees
37+
them, so they neither fire on transitions nor count toward the
38+
`default: true` attachment set. Toggle from the CLI with
39+
`qu check enable|disable <id-or-name>` and
40+
`qu alert enable|disable <id-or-name>`; from the TUI, `x` on the
41+
Checks or Alerts tab. `qu check list` / `qu status` mark disabled
42+
checks with `(disabled)` in the STATE column, `qu alert list`
43+
gained an `ENABLED` column, and the TUI Checks / Alerts tabs gained
44+
an `ON` column. The field is stored as the negation of "enabled"
45+
so the zero value of new and existing entries stays enabled — no
46+
migration needed.
47+
48+
### Changed
49+
50+
- **TUI modals now scroll** when a form is taller than the terminal (e.g. the SMTP "Add alert" form on a short window). The view auto-centres on the focused field and shows `↑/↓ N more` indicators when content is clipped above or below. #24
51+
- TUI main page no longer overflows on very short terminals — the body shrinks all the way down to a single row instead of pinning to 5.
52+
- Release workflows on both Gitea and GitHub now treat `-beta` tags as pre-releases in addition to `-rc`, so a `v0.3.0-beta1` push lands as a pre-release without manual flagging.
53+
54+
### Upgrade notes
55+
56+
Existing clusters keep working with no operator action beyond rolling
57+
out the new binary — the three new fields (`checks[].disabled`,
58+
`checks[].resolvers`, `cluster.yaml.resolvers`) are all `omitempty`,
59+
and `alerts[].disabled` is the negation of "enabled" so unset entries
60+
stay enabled.
61+
62+
Things to know during the rollout:
63+
64+
- **Don't issue toggle or resolver edits mid-rollout.** A v0.2.x
65+
daemon doesn't know `MutationSetResolvers` and will reject it
66+
outright with "unknown mutation kind" if it holds master when the
67+
CLI submits. Worse: if a v0.2.x master applies any
68+
`MutationReplaceConfig` from the manual-edit watcher (an operator
69+
saving `cluster.yaml` on an upgraded follower), it will write the
70+
file back **without** the new fields — silently dropping any
71+
`disabled` flags, per-check `resolvers`, or `cluster.yaml.resolvers`
72+
that an upgraded node had just contributed. Finish the rolling
73+
upgrade everywhere, then start using `qu check enable|disable`,
74+
`qu alert enable|disable`, `qu cluster resolvers …`, and per-check
75+
`--resolvers`. Pause hand-edits to `cluster.yaml` during the
76+
upgrade window.
77+
- **Existing `dns_resolver` on DNS checks is preserved.** It now acts
78+
as a legacy single-entry fallback used only when both
79+
`checks[].resolvers` and `cluster.yaml.resolvers` are empty. No
80+
config change required to keep current DNS-check behaviour.
81+
- **ICMP behaviour is unchanged when no resolver override is set.**
82+
pro-bing still does its own lookup against the system resolver for
83+
checks that don't configure `resolvers`.
84+
785
## [v0.2.3] — 2026-05-19
886

987
### Changed
@@ -298,4 +376,5 @@ Initial public release.
298376
[v0.2.0]: https://git.cer.sh/axodouble/quptime/releases/tag/v0.2.0
299377
[v0.2.1]: https://git.cer.sh/axodouble/quptime/releases/tag/v0.2.1
300378
[v0.2.2]: https://git.cer.sh/axodouble/quptime/releases/tag/v0.2.2
301-
[v0.2.3]: https://git.cer.sh/axodouble/quptime/releases/tag/v0.2.3
379+
[v0.2.3]: https://git.cer.sh/axodouble/quptime/releases/tag/v0.2.3
380+
[v0.3.0]: https://git.cer.sh/axodouble/quptime/releases/tag/v0.3.0

README.md

Lines changed: 82 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ trust — no central CA, no shared secret.
2525
- [Set up a 3-node cluster](#set-up-a-3-node-cluster)
2626
- [Adding checks and alerts](#adding-checks-and-alerts)
2727
- [Default alerts (attach to every check)](#default-alerts-attach-to-every-check)
28+
- [Pause checks and alerts without deleting them](#pause-checks-and-alerts-without-deleting-them)
29+
- [Bypass the host's DNS cache (custom resolvers)](#bypass-the-hosts-dns-cache-custom-resolvers)
2830
- [Interactive TUI](#interactive-tui)
2931
- [Custom alert messages](#custom-alert-messages)
3032
- [Conditionals, pipelines, and worked examples](#conditionals-pipelines-and-worked-examples)
@@ -137,7 +139,7 @@ and the daemon will replicate the edit cluster-wide.
137139

138140
## Build
139141

140-
Requires Go 1.24.2 or newer.
142+
Requires Go 1.26.3 or newer.
141143

142144
```sh
143145
go build -o qu ./cmd/qu
@@ -269,11 +271,12 @@ were attached automatically vs explicitly listed on the check:
269271

270272
```
271273
CHECKS
272-
ID NAME STATE OK/TOTAL ALERTS DETAIL
273-
ddbd... homepage up 3/3 oncall,ops*
274-
0006... db down 1/3 ops* dial timeout
275-
24f4... gateway up 3/3 -
276-
(alerts marked * are attached as defaults)
274+
ID NAME STATE OK/TOTAL ALERTS DETAIL
275+
ddbd... homepage up 3/3 oncall,ops*
276+
0006... db down 1/3 ops* dial timeout
277+
24f4... gateway up 3/3 -
278+
b8e2... nightly (disabled) up 0/0 ops*
279+
(alerts marked * are attached as defaults; "(disabled)" checks are paused — see `qu check enable`)
277280
```
278281

279282
## Default alerts (attach to every check)
@@ -295,6 +298,59 @@ specific default by adding the alert's ID or name to its
295298
`suppress_alert_ids` list in `cluster.yaml` (see "Edit cluster.yaml
296299
directly" below).
297300

301+
## Pause checks and alerts without deleting them
302+
303+
Both checks and alerts carry a `disabled` flag. A disabled check is
304+
skipped by the scheduler (no probes are fired and no per-node results
305+
arrive at the aggregator) and a disabled alert is filtered out of the
306+
effective alert list (it neither fires on transitions nor counts as a
307+
default attachment). Useful for planned maintenance, hush-during-a-known-outage, or temporarily silencing a noisy channel without
308+
losing its configuration.
309+
310+
```sh
311+
qu check disable homepage # stop probing
312+
qu check enable homepage # resume
313+
314+
qu alert disable oncall # silence the channel
315+
qu alert enable oncall # bring it back
316+
317+
qu check list # disabled checks show "(disabled) <state>"
318+
qu alert list # ENABLED column shows true/false
319+
```
320+
321+
Toggling is a regular cluster mutation: it routes through the master
322+
and replicates like any other edit. In the TUI, `x` on the Checks or
323+
Alerts tab toggles the selected row.
324+
325+
## Bypass the host's DNS cache (custom resolvers)
326+
327+
By default each probe resolves its target through the host's system
328+
resolver — which means an `nscd` / `systemd-resolved` cache, or a
329+
sleepy local DNS server, can keep a check pointed at an IP that has
330+
since moved. To bypass that path, point `qu` at the resolvers you
331+
trust:
332+
333+
```sh
334+
# Cluster-wide default: every check that doesn't override uses these.
335+
# Tried in order with connection-level failover.
336+
qu cluster resolvers set 1.1.1.1 1.0.0.1
337+
qu cluster resolvers show
338+
qu cluster resolvers clear
339+
340+
# Per-check override (always wins over the cluster default):
341+
qu check add http homepage https://example.com --resolvers 1.1.1.1,1.0.0.1
342+
qu check edit homepage --resolvers 8.8.8.8,8.8.4.4
343+
qu check edit homepage --resolvers '' # clear; fall back to cluster default
344+
```
345+
346+
The resolver list applies to HTTP / TCP / TLS / ICMP target lookups
347+
and (for DNS checks) to the query itself. Each entry is a
348+
`host[:port]`; a bare host gets `:53` appended at use time. Literal
349+
IP targets skip the resolver entirely — there's nothing to look up.
350+
351+
Precedence on every probe is **check → cluster → legacy DNSResolver
352+
(DNS checks only) → host system resolver**.
353+
298354
## Interactive TUI
299355

300356
Prefer a dashboard over typing commands? `qu tui` opens a full-screen
@@ -307,12 +363,12 @@ every two seconds.
307363
┌─ QUptime ── node: 88a00af9 master: 3438fd6f (follower) ● quorum 3/2 term 4 ver 10 ──┐
308364
│ Peers (3) [2] Checks (3) [3] Alerts (1) │
309365
├──────────────────────────────────────────────────────────────────────────────────────────────┤
310-
│ ID NAME STATE OK/TOTAL ALERTS DETAIL
311-
│ ddbd... homepage ● up 3/3 oncall*
312-
│ 0006... db ● down 1/3 oncall* dial timeout
313-
│ 24f4... gateway ○ unknown 0/0 -
366+
│ ID NAME ON STATE OK/TOTAL ALERTS DETAIL │
367+
│ ddbd... homepage yes ● up 3/3 oncall* │
368+
│ 0006... db yes ● down 1/3 oncall* dial timeout │
369+
│ 24f4... gateway no ○ unknown 0/0 - │
314370
└──────────────────────────────────────────────────────────────────────────────────────────────┘
315-
↑↓ navigate ⇥ next tab 1/2/3 jump r refresh a add check d remove check q quit
371+
↑↓ navigate ⇥ next tab 1/2/3 jump r refresh a add d remove e edit t test x on/off q quit
316372
```
317373

318374
Keybindings:
@@ -326,6 +382,7 @@ Keybindings:
326382
| `a` | add (opens a picker on Checks/Alerts; node form on Peers) |
327383
| `d` | remove the selected row (confirmation prompt) |
328384
| `t` | fire a test transition: synthetic test message on Alerts; pick down/up/recovered on Checks |
385+
| `x` | toggle the selected check / alert on or off (pauses the row without deleting it) |
329386
| `D` | toggle the selected alert's `default` flag |
330387
| `q` / `Ctrl+C` | quit |
331388

@@ -559,18 +616,25 @@ qu enroll revoke <id-or-name> revoke an outstanding token
559616
qu enroll join <token> [--advertise …] redeem a token on a new host
560617
qu node list show peers + liveness
561618
qu node remove <node-id> remove from cluster + trust
562-
qu check add http <name> <url> [--expect 200] [--interval 30s] [--body-match str] [--alerts a,b]
563-
qu check add tcp <name> <host:port>
564-
qu check add icmp <name> <host>
565-
qu check add tls <name> <host[:port]> [--warn-days 14] [--sni name]
566-
qu check add dns <name> <hostname> [--record a|aaaa|cname|mx|txt|ns] [--resolver host:port] [--expect substr]
619+
qu check add http <name> <url> [--expect 200] [--interval 30s] [--body-match str] [--alerts a,b] [--resolvers 1.1.1.1,1.0.0.1]
620+
qu check add tcp <name> <host:port> [--resolvers 1.1.1.1,1.0.0.1]
621+
qu check add icmp <name> <host> [--resolvers 1.1.1.1,1.0.0.1]
622+
qu check add tls <name> <host[:port]> [--warn-days 14] [--sni name] [--resolvers 1.1.1.1,1.0.0.1]
623+
qu check add dns <name> <hostname> [--record a|aaaa|cname|mx|txt|ns] [--resolver host:port] [--expect substr] [--resolvers …]
567624
qu check list
568-
qu check remove <id-or-name>
569-
qu check test <id-or-name> [--state down|up|recovered] fire a synthetic transition to exercise alert templates
625+
qu check remove <id-or-name>
626+
qu check enable <id-or-name> resume probing a previously disabled check
627+
qu check disable <id-or-name> stop probing without deleting the check
628+
qu check test <id-or-name> [--state down|up|recovered] fire a synthetic transition to exercise alert templates
570629
qu alert add smtp <name> --host … --port … --from … --to … [--user --password --starttls] [--default] [--subject … --body …]
571630
qu alert add discord <name> --webhook … [--default] [--body …]
572631
qu alert list / remove / test <id-or-name>
632+
qu alert enable <id-or-name> resume firing a previously disabled alert
633+
qu alert disable <id-or-name> silence an alert without deleting it
573634
qu alert default <id-or-name> on|off toggle default attachment to every check
635+
qu cluster resolvers show print the cluster-wide default DNS resolver list
636+
qu cluster resolvers set <r1> [<r2> …] replace the cluster-wide resolver list (failover order)
637+
qu cluster resolvers clear drop the cluster-wide list; every check falls back to host resolver
574638
qu trust list / remove <node-id>
575639
qu update [--check] [--force] [--source gitea|github] [--beta] replace this binary with the latest release
576640
```

docker/docker-compose-tailscale.yml

Lines changed: 0 additions & 54 deletions
This file was deleted.

0 commit comments

Comments
 (0)