Repository navigation
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, which is world-readable (0644) under the usual022umask. It is now created0600
regardless of umask, and an existing file that-outoverwrites is tightened to0600before any
key material is written to it.smoked mcpno longer follows a redirect off the hub's origin. The client already refused a
plaintexthttp://hub URL when Basic Auth or an admin password is set, but it followed
redirects: a307from the HTTPS login URL replayed the admin-password body to the redirect
target, and Go forwards the Basic Auth header to the same hostname even across an
https://→http://downgrade. Any redirect that leaves the configured scheme, host and port is
now refused for every request (login, retries, ordinary reads). The hub API never redirects.
Full changelog: v2.2.0...v2.3.0