Repository navigation
Releases: seitzbg/heliograph
Release list
v2.4.0
Feature and security release: a y-axis fit option for the graphs, and Go and Caddy toolchain updates for new net/http and crypto/tls advisories.
Upgrade notes
- No configuration changes are needed. Graphs keep their current scaling (Peaks) until a viewer picks Typical on the Graphs toolbar, or you set
SMOKED_Y_FIT=typical(or-y-fit typical) to make it the default. An unknown value stopssmokedat startup. - Bundled Caddy: if you run the root
docker-compose.ymlwith thefederationprofile, rebuild its Caddy image (docker compose --profile federation build caddy) to pick up Go 1.27.2 and Caddy v2.11.7. Theexamples/federationstack uses the stock upstreamcaddy:2.11-alpineimage, which this release doesn't change.
Added
- A y-axis fit option for the graphs. The Graphs toolbar has a new Fit control. Peaks
(the default, unchanged) scales each graph so every median spike fits. Typical scales to the
98th percentile of the median line and ignores the smoke, the way SmokePing scales to its median:
a few rare spikes clip at the top frame instead of squeezing the usual latency into the bottom of
the plot. This matters most on a short step (60 s by default), which can catch more brief stalls
as median spikes than SmokePing's 300 s rounds. The choice applies to the grid, the four-range view,
zoom and the unison scale, and each browser remembers it.-y-fit/SMOKED_Y_FIT=typicalsets
the default for viewers who haven't chosen; an unknown value stopssmokedat startup.
/api/versionreports it asy_fit.
Security
- Go toolchain bumped 1.26.6 → 1.26.9 and
golang.org/x/netv0.58.0 → v0.60.0 for the
standard-library fixes in GO-2026-6603, GO-2026-6605, GO-2026-6607 to GO-2026-6613 and GO-2026-6617
(net/http,net/textproto,crypto/tls; five of them are also fixed inx/net), which govulncheck
flagged as reachable from the collector's HTTP server and API, the agent client, the HTTP and DNS
probes, the alert notifiers andsmoked mcp. The collector image's Go build stage moves to the
matchinggolang:1.26-alpinedigest. No source changes. - Bundled Caddy image rebuilt on Go 1.27.2 and Caddy v2.11.7 (from Go 1.26.6 and v2.11.4), clearing
the samenet/http/crypto/tlsCVEs in the Caddy binary (CVE-2026-78667, CVE-2026-78669,
CVE-2026-97031). Caddy 2.11.7 already requires the patchedx/text,x/cryptoandgrpcversions, so
those build-time floors are dropped; thegolang.org/x/netfloor moves to v0.60.0.
v2.3.0
Backward-compatible fix release: everything from the 2026-10-07 whole-repo review.
Upgrade notes
- Vantage certificates are now tracked by serial. On first start the hub records that existing vantages are legacy registrations: their deployed certificates keep working with no action needed. Revoking a vantage and re-adding it now really retires its earlier certificates; re-add a legacy vantage once to bring it under serial checking. See Certificate serial tracking in
docs/federation.md. - FreeBSD rc.d: the extra-flags setting is renamed from
smoked_flags/smoke_agent_flagstosmoked_args/smoke_agent_args. A leftover_flagsvalue still works and logs a warning. The v2.2.0 scripts could not start their services. - SmokePing
--history: rows are now stored under the configured target's stable id. Pass--configif your targets live in YAML.
Fixed
- Fresh container image builds work again. The collector
DockerfileandCaddy.Dockerfilepinned
security-patched Alpine packages (libcrypto3/libssl3, and in the Caddy imagecurl/libcurl/
c-ares) to exact revisions. Alpine's repository keeps only the newest revision of each package, so
once a newer revision was published (libcrypto3/libssl33.5.9-r0,curl8.22.0-r0), the pinned ones
disappeared and every fresh build failed in theapkstep. Those pins are now>=minimum
versions: the CVE fixes still can't regress, and newer revisions install without breaking the build. - A delayed NTP round no longer brings back an obsolete clock offset after it was cleared. When a
remote vantage reports an unsynchronized round, the hub clears that vantage's clock stat for the
target, but the clear also dropped the stat's freshness timestamp. A store-and-forward replay of an
older synchronized round could then re-set the offset, so/api/targets?vantage=…and the
dashboard showed an out-of-date offset next to the newer round. The clear now keeps its timestamp,
so only a round newer than it can set the stat again. - A config reload no longer pre-fills a redefined target's alert window with its old measurement.
On a reload (SIGHUP or a config apply), alert windows are warm-started from stored history, which
was matched only by host, probe, metric and step. Changing what a target measures without changing
those, such as aTCPConnectport from443to22, re-imported the old port's rounds, so a
CheckLoss(x=3)alert fired on the first failure of the new port. A target whose measurement
identity changed in a reload is no longer warm-started: its alerts wait forxrounds of the new
measurement. A target that only gains an alert on reload is still warm-started. On a cold start,
smoked has no record of a target's previous definition, so a target redefined while smoked was
stopped can still be warm-started from its recent old rounds. smoked import smokeping --historynow stores history where the target's graph reads it.
--applygives every imported target a new stable id, but--historystored each RRD's rows
under the SmokePing path, so the backfilled history never appeared on the configured target's
graph or in/api/series.--historynow looks each target up by path in the DB config and, when
given, the YAML config passed with--config, and stores its rows under that target's id. A target
that matches no configured target, or more than one, is named on stderr, counted asunresolved
in the summary, and not imported; the run then exits3, the same as other partial failures.
With no--configand no targets in the DB config, rows are still keyed by SmokePing path, which
matches a YAML target that has noid, and the run says so. History already imported under
SmokePing paths is not moved; re-run--historyto store it under the target ids.- The FreeBSD
rc.dscripts now actually start their services. The v2.2.0 scripts in
contrib/freebsd/built adaemon(8)command line that could never run, for
three independent reasons: the unquoted two-word title (-t Heliograph hub) split, sodaemon(8)
tried to runhub/vantageas the program; rc.subr hands${name}_flagsto$command, so
smoked_flags(default-downsample) went todaemon(8), which rejected it; and rc.subr started
daemon(8)throughsuassmoke, which then could not create/var/run/<name>.pid(and was also
given-u smoke). The scripts now follow the FreeBSD ports convention: rc.subr runsdaemon(8)as
${name}_userwith no-u, the pidfile lives in a/var/run/<name>/directory that user owns,
and the process title and syslog tag are the service name, so output is logged assmoked/
smoke_agentrather thandaemon. Knob rename: extra flags move fromsmoked_flags/
smoke_agent_flagstosmoked_args/smoke_agent_args. A leftover_flagsvalue is still used,
with a warning. Also fixed:smoke_agent_spool=""now disables the on-disk spool as documented
(the old default assignment replaced an empty value, so the agent always spooled), and
smoked_args=""now drops the default-downsample. Thefreebsd-testCI job now installs both
scripts in its FreeBSD VM and drives start/status/restart/stop throughservice(8), checking the
service user, argv, environment and pidfile, plus smoked's API. heliograph_triagecovers the hub's ownlocalvantage and works on a single-host hub. It
took its vantage list from the remote-vantage registry (GET /api/admin/vantages), which never
includeslocal. With a remote vantage registered, triage never read local measurements, so a
target at 100% loss from the hub but healthy from the remote was reported healthy, and
vantage=localwas rejected as unknown. On a hub without the registry (no database or no admin
password, e.g. the memory-store demo), triage failed outright with a 404. It now reads the
vantages from each target's own vantage set in/api/targets, counts a target only at the
vantages that measure it, and uses the registry only for stale-collector detection when the hub
has one. Errors other than an absent registry (a 5xx, an auth failure) still fail the call.
heliograph_vantageson a hub without the registry now explains that instead of returning
"404 page not found".config_stage_edit_targetno longer deletes a target moved into its own subtree. Moving a
target to a group path at or below itself (e.g.g/aintog/a/sub) detached it into an
unreachable cycle, and the target vanished from the staged config with no error. The move is
now rejected and the staged config is left unchanged.- MCP config change summaries report group-level changes. The
config_stage_*results and
config_reviewcompared only host-bearing nodes, so changing a group's own settings (itsstep,
probe params,vantages,alerts) was summarized as no change at all, although it changes every
target beneath it. Summaries now list groups added, removed, or with changed settings, and count
a target as changed when a group above it changes a setting it inherits. - MCP config staging validates against the hub's real file config. Local validation composed
the staged DB fragment onto a defaults-only config, so a DB target that inherits its probe from
the hub's file config (valid on the hub, and allowed by the dashboard) failed with "no probe set",
and because every edit revalidates the whole fragment, one such target blocked all staging.
Staging now derives the hub's file config from its effective config and composes onto it as the
hub does. When that can't be reproduced locally, problems are reported aswarningsinstead of
blocking, andconfig_apply's server-side validation stays authoritative.
Security
- Revoking and re-adding a vantage now retires its earlier certificates. Agent authorization
checked only the client certificate's CommonName against the registry, so after
smoked vantage revoke <name>followed byvantage add <name>— the documented way to retire a
credential — every certificate ever issued for that name (CA-signed, valid for ten years)
authenticated again. The hub now records the serial of every certificate it issues and accepts a
certificate only if its serial was issued for the vantage's current registration; revoking drops
the recorded serials, so a re-added name accepts only certificates issued after the re-add.
Regenerate (the dashboard's Regenerate, orvantage addfor a registered name) is unchanged:
earlier certificates keep working alongside the new one. Upgrade: vantages registered before
this release have no recorded serials, so on the first start they become legacy registrations
that keep accepting any hub-CA certificate bearing their name — deployed agents keep reporting
with no action — until they are revoked. Revoke and re-add such a vantage once (and redeploy its
new bundle) to bring it under serial checking. Certificates minted by an oldersmokedbinary
against an upgraded database are not recorded and are refused for vantages registered after the
upgrade. See Certificate serial tracking indocs/federation.md. golang.org/x/textbumped from v0.39.0 to v0.42.0 for GO-2026-6629
(a panic inx/text/secure/precis, fixed in v0.41.0).govulncheckflagged it throughpgx's
SCRAM password handling. That path runs the operator-supplied database password through a
different precis profile from the one the advisory names, so no remote trigger is known; the bump
clears the scan. Caddy's--with golang.org/x/textfloor is raised to match.smoked vantage add -outwrites the onboarding bundle owner-only. The.tar.gzembeds the
vantage's client private key inagent.yaml, but was created with the default0666-minus-umask
mode, w...
v2.2.0
Heliograph v2.2.0
Backward-compatible minor release: first-class FreeBSD support.
Added
- FreeBSD support. Both binaries are pure Go and run natively on FreeBSD (
amd64/arm64). This release attaches native FreeBSD binaries —heliograph_2.2.0_freebsd_amd64.tar.gzandheliograph_2.2.0_freebsd_arm64.tar.gz(each verified againstSHA256SUMS), containingsmoked+smoke-agent+web/+config.example.yaml+contrib/+LICENSE. A newfreebsd-testCI job runs the fullgo test ./...suite inside a real FreeBSD VM on every push. Addedcontrib/freebsd/withdaemon(8)-supervisedrc.dscripts forsmoke-agentandsmoked, and a Native binary (FreeBSD & other Unix) README section covering the ICMP privilege model (nativePingneeds root;FPingworks viapkg install fping).
Changed
- The native
Pingprobe's "no ICMP listener available" error is now OS-aware — it spells out the remedy per OS instead of naming only Linux'snet.ipv4.ping_group_range, including FreeBSD (no unprivileged ICMP datagram socket: run as root withmode: privileged, or use theFPingprobe).
Container image: ghcr.io/seitzbg/heliograph:v2.2.0 (Linux). FreeBSD: download a tarball below, or see contrib/freebsd/README.md.
Full changelog: v2.1.0...v2.2.0
v2.1.0
Adds the MCP server (smoked mcp) — a stdio Model Context Protocol server wrapping Heliograph's HTTP API, so an MCP-aware assistant can diagnose the network and manage the monitoring config. Backward compatible; no breaking changes.
Added
- MCP server (
smoked mcp) — 13 tools: read-only diagnosis (heliograph_status,heliograph_sla,heliograph_series,heliograph_triage,heliograph_vantages), config read (heliograph_config_get), and a local stage → review → apply config-write flow (config_stage_add_target/edit_target/remove_target/replace,config_review,config_apply,config_discard). - Config edits are staged and validated locally with the daemon's own config parser;
config_applyis the only tool that writes to the live hub (PUT /api/admin/config, optimistic concurrency — the hub's own validation stays authoritative). - Credentials are sent only over HTTPS — a plaintext
http://hub URL is refused when Basic Auth or an admin password is set (loopback excepted).
The MCP server is a client-side subcommand (runs on your machine over stdio, talks to the hub over HTTPS), so no hub redeploy is required to use it — pull the :v2.1.0 image (or build the binary) and run smoked mcp. See the README's MCP server section and examples/mcp/.
Full changelog: v2.0.0...v2.1.0
v2.0.0
⚠️ Breaking change — re-onboard your vantages
The per-vantage API key is gone, replaced by mutual-TLS federation. smoke-agent's -key flag and the config key: field no longer exist; agents now authenticate with a CA-signed client certificate and connect straight to the hub's -agent-addr mTLS listener (the reverse proxy's /agent/v1/* path is removed). Every existing federated vantage must be re-onboarded with a certificate bundle: run smoked vantage add <name> (or use the dashboard's Add vantage) and redeploy the agent with the new agent.yaml.
Added
- Opt-in mutual-TLS federation — a dedicated mTLS listener (
-agent-addr :8443 -agent-hostname <domain>), self-bootstrapped CA, per-vantage client certs whose CommonName is the vantage identity (revoke takes effect on the next request). - One-click vantage onboarding — Add vantage downloads a ready-to-run
<name>-vantage.tar.gz(agent.yaml + docker-compose.yml + README); CLI equivalentsmoked vantage add <name> -out …. - Copyable example stacks under
examples/(standalone/andfederation/).
Security
- Minting a vantage requires HTTPS —
POST /api/admin/vantagesrefuses to mint over plaintext (403) so the embedded private key never crosses the wire in the clear (readsX-Forwarded-Proto: httpsfrom the proxy; loopback exempt; CLI is the local escape hatch). - Open config reads redact credentials embedded anywhere in a probe URL (userinfo, query, and path).
Changed
- The Graphs vantage selector caps at 4 simultaneous overlaid vantages (palette size).
Fixed
- Overview no longer reports a target down from a vantage that doesn't measure it; a batch of Graphs correctness/robustness fixes — peak clipping, blank-on-refresh, stalled-body aborts, overlay color collisions, narrow-viewport overflow, and refusing to mint an onboarding bundle when no agent listener is configured.
Full changelog: CHANGELOG.md · compare v1.0.16...v2.0.0
Image: ghcr.io/seitzbg/heliograph:v2.0.0
v1.0.16
Changed
- The Config tab now defaults to your Effective config, not the DB fragment. The Effective | DB
source toggle now applies to both the Tree and YAML views (previously YAML-only), and the tab opens
on Effective — the file+DB merged config the collector actually runs — so you see your real
config instead of an empty database fragment. Effective is read-only (you can't edit file-defined
targets from the browser); switch the source to DB to add or edit database-backed targets. New
read-only endpointGET /api/admin/config?source=effective(open read, same non-secret data as the
effective YAML) backs the effective tree.
v1.0.15
Added
- View the config as YAML in the Config tab. A Tree | YAML toggle sits beside the target tree; the
YAML view is read-only and offers two sources — DB (just the targets stored in the database) and
Effective (your YAML files merged with the DB targets, exactly as the collector runs it). It reads
like a hand-written config file: durations render as60s, and unset/inherited fields are omitted
rather than shown asnull. Served by a new open readGET /api/admin/config.yaml?source=db|effective
(alongsideGET /api/admin/config); the config holds no secrets, so it needs no admin login, and every
write endpoint stays gated.
Fixed
- The dashboard no longer serves stale JavaScript after an upgrade. Static assets (
index.html,
dashboard.js,smoke.js, CSS) were served with only aLast-Modifiedheader, so a browser could
keep running the pre-upgrade dashboard against a freshly-deployed server until a hard refresh — which
could leave panels stuck on "loading" after switching vantages. They now carryCache-Control: no-cache, forcing the browser to revalidate (a cheap 304 when unchanged, a fresh copy the moment the
file changes). Assets are still cached; they're just never served stale.
v1.0.14
Read-only admin pages and a cleaner Graphs toolbar.
Changed
- Vantages + Config pages are viewable without logging in. Anyone who can reach the dashboard (behind the reverse proxy's Basic Auth) can now see the vantage list and the DB config tree read-only; adding/revoking a vantage and editing/importing config still require an admin login. The vantage list never includes keys, and the config doc holds no secrets (notifiers are referenced by name; the real credentials come from the environment), so nothing sensitive is exposed.
- Tidier Graphs toolbar. The reference key (jitter + loss) and the interactive controls (vantages/cols/unison) are separated into two zones with a divider, and the 8-chip loss legend is now a single compact green→red strip (each bucket's threshold on hover). It scales down cleanly — the zones wrap and the divider drops on narrow screens.
Ships as ghcr.io/seitzbg/heliograph:v1.0.14. See the CHANGELOG.
v1.0.13
Multi-vantage overview polish.
Changed
- Graphs grid hides targets a selected vantage doesn't measure. Viewing the grid from a vantage that doesn't probe some targets (e.g. a Comcast vantage that doesn't measure the hub's loopback or internal resolvers) no longer draws blank "collecting…" panels — those are hidden with an "N targets not measured — hidden" note, and each shown panel's band follows the first selected vantage that actually measures it, so a shown panel never blanks either. Hidden targets stay in the nav tree and detail view.
Ships as ghcr.io/seitzbg/heliograph:v1.0.13. See the CHANGELOG.
v1.0.12
Multi-vantage reaches the overview.
Highlights
- Vantages on the Graphs page. A vantage control on the Graphs toolbar (shown only when a deployment measures from more than one vantage) picks which vantages draw on every panel: the focused one owns the min–max band + median, the rest overlay as median lines — so selecting one vantage is a clean single view and selecting all is a full fiber-vs-vantage overlay. The nav and panel status dots now reflect the worst vantage (even one you've hidden), so a target healthy locally but losing packets from another vantage still flags on the overview. Defaults to
local; the selection is remembered per browser. - Docs:
config.example.yamlnow shows avantages:example, so the reference config demonstrates measuring a subtree from both the hub and a remote vantage.
See the CHANGELOG. Ships as ghcr.io/seitzbg/heliograph:v1.0.12.