Duck Stats — Changelog

What's new on stats.iduck.xyz.

2026-06-19 v2.2.0 Feature
Public changelog page

This page itself. The changelog is now sourced from a single markdown file and rendered live so customers can see what's shipping without poking us on Discord. Admin-only editor at /admin/changelog/edit saves a timestamped backup on every write.

2026-06-18 v2.1.3 Improvement
Probe agent v2.2 — exponential backoff on push failures

Probe agents now back off when the push endpoint returns 5xx or times out (1s → 2s → 4s … capped at 60s) instead of hammering the API every interval. Cuts gateway log noise during Duck Stats restart windows by ~95%.

2026-06-17 v2.1.2 Fix
Gateway 502s during watchtower restarts

Nginx in front of the API was caching upstream DNS at startup, so when watchtower restarted the api container and Docker handed it a new IP, the gateway kept routing to the dead address until manually reloaded. Switched to resolver 127.0.0.11 valid=10s with per-location set $upstream variables. No more manual gateway restarts after upgrades.

2026-06-15 v2.1.1 Security
Probe config tokens are now per-location

Each probe location gets its own config_token for the public /api/probe-config/<token> endpoint. Rotating one location no longer disrupts any other. Old shared-token configs were migrated automatically; no action needed.

2026-06-14 v2.1.0 Feature
Probe locations & targets — distributed checks

You can now register multiple probe locations (e.g. frankfurt, oracle-eu, home-lab) and assign HTTP/HTTPS/TCP/Ping targets to a subset of them. Each location pulls its config from a public endpoint and pushes results back as Duck Stats push monitors. Status page shows one row per (target × location) so you can see exactly where a check is failing from.

2026-06-12 v2.0.4 Improvement
Server sparklines — batched query

The Servers page was doing one DB query per card to fetch the 60-minute CPU sparkline (N+1 — 18 monitors = 18 queries). Replaced with a single SELECT ... WHERE monitor_id IN (...) and bucketed in Python. Page load on the fleet view dropped from ~1.4s to ~110ms.

2026-06-10 v2.0.3 Fix
Discord status board no longer double-posts on flap

duck-discord-notifier was racing against Duck Stats's own notification channel when a monitor flapped DOWN→UP→DOWN within the 90s discord update window. Unlinked individual monitors from the Discord notification channel in Duck Stats; the status board embed is now the single source of truth for public-facing alerts. Email + Pushover still fire as before.

2026-06-08 v2.0.2 Improvement
Heartbeat retention — table-swap prune

Switched the 7-day heartbeat prune from DELETE WHERE time < ... to a table-swap (rename old → new, swap, drop). The old DELETE was holding row locks long enough to delay live writes from the ingester during the 3am window. Prune now completes in ~4s instead of ~90s.

2026-06-05 v2.0.1 Fix
Stats period column off-by-one

The period column on the stats table was being written as 0/1/2 instead of the documented 1/2/3 (minutely/hourly/daily). Backfilled historical rows; reads now correctly join across all three resolutions. Daily rollups for May 2026 are now visible on the status page.

2026-06-01 v2.0.0 Feature
MySQL HeatWave migration complete

Stats and heartbeats have been migrated off MongoDB onto MySQL HeatWave running on Oracle Cloud (10.0.90.123). Queries against the 7-day heartbeat window are 5-12× faster. The MongoDB stats repository is kept as a fallback shim for one release and will be removed in v2.1.

2026-05-29 v1.9.7 Improvement
Status page — batch uptime query

FindUptimeSumByMonitorIDs now folds the per-monitor uptime sum into one batched query. The public status page used to issue one query per monitor (N+1 again) — for the ducktv page that's 38 queries on every load. Cold-load TTFB dropped from 1.8s to 240ms.

2026-05-26 v1.9.6 Security
Admin session secret regenerated on every restart by default

If SECRET_KEY is not set in the env, mon-admin used to fall back to a hardcoded dev key. Now it generates a random secret on startup (secrets.token_hex(32)) so a forgotten env var doesn't leak sessions across restarts. Set SECRET_KEY explicitly if you want sessions to survive container restarts.

2026-05-24 v1.9.5 Fix
Worker → Hetzner egress via SNAT

Outgoing checks to 95.216.0.0/16 (Hetzner node fleet) were getting rate-limited because they were exiting from Frost's secondary IP, which Hetzner had on a fail2ban list. Added iptables -t nat -A POSTROUTING -d 95.216.0.0/16 -j SNAT --to-source 129.146.92.139 and saved via netfilter-persistent. No more 30-second TCP check timeouts.

2026-05-22 v1.9.4 Feature
Tabler light theme

mon-admin has been restyled with Tabler. Cards, badges, sidebar nav, breadcrumbs — all matching the rest of the Duck dashboard. Dark mode support is queued for v2.3.

2026-05-20 v1.9.3 Improvement
Health agent v1.4 — disk usage in push payload

The host health agent now appends a Disks=root=42%,storage=71% chunk to each push message. mon-admin parses it and shows the worst-disk bar on the server card. Existing agents will be auto-upgraded over the next 48h via the deploy script; nothing to do.