Skip to content

Releases: develeap/hyperping-exporter

v1.8.2

09 Jun 06:30
16a7f4b

Choose a tag to compare

What this ships

Bundle of three fixes addressing user-reported issues against v1.8.1 on cgk8s:

  1. hyperping-go v0.7.1 brings in the MCP client correctness fixes (canonical arguments: {} serialization for nil-args calls plus AlertHistory / TeamMember / StatusSummary response-shape corrections).
  2. Warm 24h MTTA emit-suppression — the warm-tier per-UUID GetMonitorMtta call was storing the aggregate report.Mtta field as the per-UUID value, which silently emitted misleading zero-valued series for projects with no acknowledged alerts.
  3. MTTA precondition documentation — README and CHANGELOG now state that hyperping_monitor_mtta_seconds requires acknowledged alerts in the upstream Hyperping project.

Operator-facing behavior changes at rollout

Surface Before (v1.8.1) After (v1.8.2)
hyperping_alerts stuck at 0 since v1.8.0 (silent decode of AlertHistory against a non-existent shape) reflects real alert count
hyperping_monitor_mtta_seconds{period="24h"} (warm, projects with no acks) 47 misleading zero-valued series for hyp_core absent
list_recent_alerts warm-tick warnings 78 failures / 12.7h fleet-wide ("invalid character 'M'") gone
hyperping_monitor_mtta_seconds{period in {7d,30d,90d,365d}} (cold) absent for projects with no acks still absent until acks happen upstream

hyperping_alerts was the bug the user reported — the warm-tick "invalid character 'M'" warning logged on every refresh. v0.7.1's nil-args + AlertHistory fixes unstick that path.

The MTTA series-set change is the operationally interesting one. Any absent()-based alert rule on hyperping_monitor_mtta_seconds needs review:

  • Before: absent(hyperping_monitor_mtta_seconds{period="24h"}) always returned empty (47 series were emitted with value=0)
  • After: the same query fires for projects with no ack data
  • Operator workaround if you want to keep "no MTTA = ok" semantic: predicate on the upstream condition explicitly (e.g., a manual hyperping_project_has_acks indicator or just leave the metric optional in dashboards)

Why MTTA may still be absent after v1.8.2

This release fixes the wiring around MTTA emission. It does NOT manufacture MTTA values out of thin air. hyperping_monitor_mtta_seconds requires acknowledged alerts in the configured Hyperping project. If list_recent_alerts.rawAlerts[*].acknowledgedAt is null for every alert (no on-call / ack flow configured on the Hyperping side), MTTA will remain absent across every period regardless of any client-side change.

Self-check (against the live MCP):

# probe list_recent_alerts (now decodes correctly with v0.7.1)
# any rawAlert with acknowledgedAt != null means MTTA can populate

Or equivalent: hyperping_alerts > 0 unless hyperping_monitor_mtta_seconds.

Tests

  • 3 new warm MTTA regression tests including an explicit aggregate-leak guard (mock asymmetric per-monitor=100, aggregate=9999 → assert emitted value is 100, proving the aggregate field was not used).
  • 318 total Go tests pass with -race -count=1 (up from 315).
  • Chart render harness all pass.
  • 5 logical commits: deps bump → TDD failing tests → fix → chart bump → docs.

Backward compatibility

Patch release. No public API change. Two intentional behavior changes (alert count unsticks; warm MTTA zeros become absences) are documented above and were the target of this PR.

Chart

Chart 1.8.2 carries appVersion: "1.8.2". Values schema unchanged. No values.yaml change required for operators upgrading from chart 1.8.1.

Verification

Check Result
go vet ./... clean
go build ./... clean
go test -race -count=1 ./... 318 passed
golangci-lint run ./... clean
helm lint deploy/helm/hyperping-exporter clean
python3 deploy/helm/hyperping-exporter/tests/render_test.py all pass

Upstream

Consumes develeap/hyperping-go v0.7.1, which adds output-shape pinning to its schema contract test so this bug class is caught at PR time on the SDK side going forward.

PR

Full diff and review: #79.

Chart chart-v1.8.2

09 Jun 06:31
16a7f4b

Choose a tag to compare

Fixed

  • hyperping_alerts was stuck at 0 across every project regardless of
    the real upstream count. The hyperping-go v0.7.0 AlertHistory
    struct declared {Alerts, Total} while the live MCP server response
    shape is {timeGroups, totalAlerts, downAlerts, upAlerts, rawAlerts}.
    The Go decoder silently produced an all-zero struct, so the exporter
    faithfully reported total = 0 while alerts were happening upstream.
    v1.8.2 bumps to hyperping-go v0.7.1 which corrects the shape and
    exposes a nil-safe Total() accessor; both exporter callsites
    (legacy collector.go and tiered tiered.go warm-tier) switch to
    the method form. Behaviour change at rollout: hyperping_alerts
    starts moving for projects with non-zero alert history. Alert rules
    that treated this gauge as effectively-constant should be reviewed.
  • The MCP transport "invalid character 'M' looking for beginning of
    value" warning on every warm tick stops. hyperping-go v0.7.1 fixes
    CallTool to send arguments: {} when the caller passes nil args
    (the server rejected the malformed request with a textual
    MCP error -32602: Input validation ... reply that the client tried
    to JSON-decode, hence the noisy warning).
  • Warm-tier 24h hyperping_monitor_mtta_seconds series stop emitting
    misleading zeros for projects without acknowledged alerts. The
    warm-tier per-UUID get_monitor_mtta call was reading the response's
    top-level mtta aggregate (which is 0 when the project has no acks
    in the window) into res.mtta[uuid], producing one zero-valued
    period="24h" series per monitor. The cold tier already handled this
    shape correctly. v1.8.2 mirrors the cold-tier semantic in the warm
    path: only record an entry when the response's monitors array
    carries a matching uuid, and read the per-monitor value from that
    entry rather than the aggregate. Behaviour change at rollout: the
    warm 24h MTTA series disappear for any project whose Hyperping
    account does not acknowledge alerts. They reappear automatically
    once acks start flowing upstream.

Changed

  • Chart version + appVersion bumped to 1.8.2.

Verification

  • All existing tests pass under -race -count=1. Three new tests pin
    the warm-tier emit-suppression contract (empty Monitors -> no entry;
    populated Monitors -> per-monitor value, never aggregate; mixed
    fixture -> only populated uuids end up in the snapshot).
  • Helm render tests pass with version pins moved to 1.8.2.
  • See README "MTTA precondition" for why this metric may still be
    absent across every period after the fix lands (Layer 3 upstream
    precondition: the project must actually acknowledge alerts).

Install

helm repo add develeap https://develeap.github.io/hyperping-exporter
helm repo update
helm install hyperping-exporter develeap/hyperping-exporter \
  --version 1.8.2

v1.8.1

08 Jun 16:40
1ecfbdf

Choose a tag to compare

What this fixes

Two bugs surfaced after the v1.8.0 rollout on cgk8s, both confirmed against the live exporter behavior and (for bug 1) the live MCP server response shape.

Bug 1: cold-tier MTTA fan-out was silently empty

The v1.8.0 release notes claimed cold-tier MTTA fan-out worked via the hyperping-go v0.7.0 empty-uuids semantic. That claim was wrong: the MCP server's get_monitor_mtta tool returns project-level aggregate only (monitors: [], plus a project mean) when called with empty monitor_uuids. Per-monitor data requires explicit UUIDs in the request body.

The exporter's cold MTTA path iterated the empty Monitors[] slice, populated an empty per-period map, and emitted zero series for every (uuid, cold-period) tuple. Operators with multi-period configs saw cold MTTR / SLA / downtime / outages fan out normally, but MTTA only emitted the 24h warm series.

Fix: Cold MTTA call now sources monitor UUIDs from the HOT-tier snapshot (always populated after HOT's eager startup refresh) and passes them explicitly. Still one MCP call per cold period. The misleading in-source comment is corrected.

Bug 2: WARM and COLD tiers had no eager startup refresh

HOT ran an eager in-band refresh at startup (preserves /readyz semantics). WARM and COLD waited for their first ticker fire at +warmTTL and +coldTTL respectively. On the cgk8s deploy (warmTTL=30m, coldTTL=1h) that meant dashboards were dark for 24h SLA up to 30m after every restart, and dark for 7d/30d/90d/365d up to 1h. For an executive SLA surface, that gap was conspicuous after every deploy / OOM / node drain / chart bump.

Fix: Each enabled tier's goroutine now runs one eager refresh before installing its ticker. HOT remains in-band (no /readyz semantic change). WARM and COLD eager refreshes are non-blocking on the caller — they run inside the existing tier goroutines so process startup is unaffected. Per-tier mutex (already present) covers the edge case where a slow eager refresh overlaps with the first ticker fire. Disabled tiers do NOT run eager refreshes (zero API calls; existing tier-disabled semantic preserved).

Behavior change at rollout

Operators with config.periods set to include 7d / 30d / 90d / 365d will see those hyperping_monitor_mtta_seconds series populate for the first time. Alert rules using absent() to tolerate the v1.8.0 silent absence will silently flip on this rollout and need to be reviewed before deploy.

No other operator-facing change. Default-config (no periods set) is unaffected.

Tests

  • 7 new tests (3 cold-MTTA + 4 eager-prefetch); pinned mocks reflect real MCP semantic (empty uuids return empty monitors[]; explicit uuids return populated monitors[]).
  • TestColdMtta_FansOutAcrossPeriods — per-period series populated for every (uuid, cold-period) tuple.
  • TestColdMtta_PassesExplicitUUIDs — regression guard: cold MTTA request monitor_uuids is non-empty.
  • TestColdMtta_SkipsWhenHotEmpty — no panic, no MCP call, when HOT snapshot is empty.
  • TestTieredRefresher_EagerWarmPrefetchrefreshWarm invoked within a window much shorter than warmTTL.
  • TestTieredRefresher_EagerColdPrefetch — same for cold.
  • TestTieredRefresher_DisabledTierNoEagerRefresh — disabled warm/cold issues zero API calls.
  • 2 existing isolation tests updated for the new eager-prefetch behavior; pinned contracts unchanged.
  • Total suite: 315 passed with -race -count=1.

Backward compatibility

Patch release. No public API change. Default-config Desc snapshot guard (added in v1.8.0) still passes unchanged. Existing PromQL queries continue to work; new cold MTTA series simply start populating where they were absent in v1.8.0.

Verification

Check Result
go vet ./... clean
go build ./... clean
go test -race -count=1 ./... 315 passed
golangci-lint run ./... clean
helm lint deploy/helm/hyperping-exporter clean
python3 deploy/helm/hyperping-exporter/tests/render_test.py all pass

Chart

Chart 1.8.1 carries appVersion: "1.8.1". Values schema unchanged. Operators upgrading from chart 1.8.0 see no values-file change required.

PR

Full diff and review: #78.

v1.8.0

08 Jun 13:31
878464c

Choose a tag to compare

What this ships

Two things in one release:

  1. Silent-zero MTTA bug fix. Pre-v1.8.0 the exporter's hyperping_monitor_mtta_seconds series have been emitting all-zero values for every monitor on every scrape since the multi-project rewrite. The pre-hyperping-go v0.7.0 MCP client sent the wrong request-arg shape and decoded into a struct the server never returned. Bumping the upstream client restores real windowed MTTA values. Operators will see this metric flip from 0 to its real value at rollout. Any alert rule or recording rule whose threshold was tuned against the bogus 0 must be re-baselined before this image goes live in front of pagers.

  2. Multi-period metric emission. New per-project config.periods field fans the seven period-bearing metrics across configured SLA report windows. Allowed tokens: 24h, 7d, 30d, 90d, 365d. Absent or empty list defaults to ["24h"], which keeps a chart 1.7.x values.yaml byte-identical on the wire. Period -> tier mapping is fixed: 24h is warm; 7d, 30d, 90d, 365d are cold. Cold-tier MTTA fan-out uses the new v0.7.0 empty-uuids semantic so adding a period costs +1 MCP call per cold tick, not +N.

Breaking changes (read these before rollout)

1. hyperping_monitor_mtta_seconds always carries a period label

Default-config series identity changes from:

hyperping_monitor_mtta_seconds{name=...,project=...,tenant=...,tier=...,uuid=...}

to:

hyperping_monitor_mtta_seconds{name=...,period="24h",project=...,tenant=...,tier=...,uuid=...}

Recording rules, alert expressions, and Grafana panels referencing the unlabelled form need a one-time update.

2. hyperping_monitor_mtta_seconds values flip from 0 to real

This is a value-level change, not a label-level one. See the headline. Re-baseline any thresholds.

3. hyperping_data_age_seconds gains a period label, single-emit per (tier, period)

HOT tier (no window) emits one series with period="". WARM and COLD tiers emit one series per configured period mapped to them (24h -> warm; 7d/30d/90d/365d -> cold). No empty-period legacy series is emitted for warm/cold. PromQL migration:

# before (chart 1.7.x)
sum(data_age_seconds{tier="warm"})

# after (chart 1.8.0): still works; counts the period(s) mapped to warm
sum(data_age_seconds{tier="warm"})

# explicit per-period query (new)
data_age_seconds{tier="warm", period="24h"}

For the default periods=["24h"] the scalar value of sum(data_age_seconds{tier="warm"}) is identical to pre-1.8.0.

Added

  • config.periods per-project knob. Allowed tokens: "24h", "7d", "30d", "90d", "365d". Absent or empty list defaults to ["24h"].
  • Period -> tier mapping (fixed): 24h -> warm; 7d, 30d, 90d, 365d -> cold. A period whose mapped tier is disabled on the project (via cache.warmEnabled: false or cache.coldEnabled: false) emits no series for that window; hyperping_exporter_tier_disabled covers the absence semantic.
  • Period fan-out on seven period-bearing metrics: hyperping_monitor_sla_ratio, _downtime_seconds, _outages, _longest_outage_seconds, _mttr_seconds, _mtta_seconds, and hyperping_tenant_avg_sla_ratio.
  • Startup WARN log per (project, period) whose mapped tier is disabled. Saves operators a debug round-trip when they ask "why is my 7d data missing?"
  • README section "Multi-period metric emission" with a copy-pastable YAML example.

Chart

  • deploy/helm/hyperping-exporter bumped 1.7.0 -> 1.8.0; appVersion 1.7.0 -> 1.8.0.
  • config.periods exposed in values.yaml with allowed tokens, default, and docs.
  • helm template passes the field through verbatim into the rendered projects.yaml.
  • Three new render-test cases: MP1 (passthrough), MP2 (absent-key byte-identical compat with chart 1.7.x), MP3 (invalid-token chart-level passthrough; binary rejects at startup).

Backward compatibility (no config.periods set)

Surface Behaviour
Chart values.yaml without config.periods Renders projects.yaml byte-identical to chart 1.7.x (asserted by render test MP2)
Series count for sla_ratio, downtime_seconds, outages, longest_outage_seconds, mttr_seconds, tenant_avg_sla_ratio Unchanged; period="24h" only
hyperping_monitor_mtta_seconds period="24h" label now ALWAYS present (documented breaking change); value flips from 0 to real
hyperping_data_age_seconds Now {tier="hot", period=""} + {tier="warm", period="24h"}; sum() per tier is scalar-preserved for the default
API call volume per scrape Unchanged when config.periods is absent or ["24h"]

Rate-limit math

For hyp_core at 136 monitors, default tier TTLs (hot=60s, warm=5min, cold=15min). Units are explicit per row to avoid mixing instantaneous and smoothed numbers:

Before (chart 1.7.x) After 1.8.0 default ["24h"] After 1.8.0 full ["24h","7d","30d","90d","365d"]
REST calls/hr/project (smoothed) 240 hot + 12 warm + 8 cold = 260 identical 240 hot + 12 warm + 16 cold = 268
MCP calls/hr/project (smoothed) ~4 908 warm identical 4 908 warm + 16 cold = 4 924
MCP burst per cold tick (instantaneous) 0 0 4 (one all-monitors MTTA call per cold period)
MCP burst per warm tick (instantaneous) ~408 (3 calls/monitor × 136) identical identical

Limits: 800/hr REST per project, 215/60s MCP burst. Full 5-period opt-in uses 33% of the REST budget and ~14% of the MCP burst budget. The new long-window MCP fetches use v0.7.0's empty-uuids "all monitors in one call" path so adding a period costs +1 MCP call per cold tick, not +N.

Tests

  • 308/308 Go unit tests pass with -race -count=1
  • 12 new unit tests in multi_period_test.go (main package): resolvePeriods defaulting, allowed tokens, invalid-token error wrapping, duplicates, periodToTier mapping, loadProjectsFile periods parsing, plus 2 tests covering the dead-period WARN log
  • 6 new unit tests in internal/collector/multi_period_test.go: MTTA period label, period-label-presence regression guard, data_age_seconds single-emit semantics, warm/cold orphan empty-period regression guard, disabled cold tier + mapped periods no-orphan-series contract, and the v1.8.0 default-config registry Desc-set parity pin
  • 3 chart render-test cases (MP1, MP2, MP3)
  • Race-detector clean
  • helm lint, golangci-lint, go vet, go build: all clean

Followups not in this release

  • Per-period MTTA on the warm tier (only cold currently fans MTTA across cold-mapped windows; warm 24h MTTA still uses the legacy per-monitor loop because that same loop covers per-monitor anomaly_count / anomaly_score the all-monitors MTTA call does not).
  • Tenant SLA average fan-out is implicit (the cold-tier loop emits one tenant series per cold period via r.MTTR) but lacks a dedicated regression test asserting the per-period series count for arbitrary periods lists.

Upstream

This release consumes develeap/hyperping-go v0.7.0, which fixes four MCP client methods that were silently returning all-zero values: GetMonitorMtta, GetMonitorMttr, GetMonitorResponseTime, GetMonitorUptime. The exporter calls Mtta and ResponseTime; only MTTA had observable broken behaviour because the response_time path also pulls per-monitor anomalies via a separate working call.

PR

Full diff and review history: #77.

Chart chart-v1.8.1

08 Jun 16:40
1ecfbdf

Choose a tag to compare

Fixed

  • Cold-tier MTTA fan-out (hyperping_monitor_mtta_seconds with
    period in 7d/30d/90d/365d) was silently empty in v1.8.0.
    The cold refresher called the MCP get_monitor_mtta tool with an
    empty monitor_uuids slice on the assumption that "empty meant
    every monitor". The live server semantic is the opposite: empty
    monitor_uuids returns the project-level aggregate only
    (monitors: [], totalAcknowledged: 0, mtta: 0). The exporter
    iterated the empty array and published an empty per-period map, so
    no cold MTTA series were ever emitted. v1.8.1 sources monitor UUIDs
    from the HOT snapshot and passes them explicitly as the
    monitor_uuids variadic argument. Behaviour change at rollout:
    operators with multi-period configs (e.g.
    periods: ["24h", "7d", "30d"]) will start seeing populated
    hyperping_monitor_mtta_seconds series for the cold-mapped windows.
    Alert rules that used absent() on the cold MTTA series to tolerate
    the v1.8.0 silent absence should be reviewed.
  • Tiered-mode warm and cold tiers now perform an eager refresh at
    startup so dashboards are not dark for warmTTL/coldTTL after a
    pod restart. Pre-1.8.1 each tier built its time.Ticker(d) and
    waited +d for the first refresh; on a deploy with warmTTL=30m
    and coldTTL=1h that meant no 24h SLA series for 30m after every
    restart and no 7d/30d/90d/365d series for 1h. The eager refresh
    runs inside the per-tier goroutine (not on the caller path) so HOT
    remains the only tier that gates /readyz. Per-tier mutexes defend
    the unlikely overlap between a slow eager refresh and the first
    scheduled tick. Disabled tiers continue to launch no goroutine and
    therefore do not run an eager refresh.

Changed

  • Chart version + appVersion bumped to 1.8.1.

Verification

  • All existing tests pass under -race -count=1. New tests added for
    the cold MTTA fan-out contract (3) and the eager warm/cold prefetch
    contract (4).
  • README "Multi-period metric emission" section corrected: the prior
    wording claimed the empty-uuids MCP call returned per-monitor
    entries, which was the source of the v1.8.0 silent-empty bug.

Install

helm repo add develeap https://develeap.github.io/hyperping-exporter
helm repo update
helm install hyperping-exporter develeap/hyperping-exporter \
  --version 1.8.1

Chart chart-v1.8.0

08 Jun 14:19
878464c

Choose a tag to compare

Fixed

  • BREAKING (silent-zero bug fix): hyperping_monitor_mtta_seconds was
    emitting all-zero values for every monitor on every scrape because
    the pre-v0.7.0 hyperping-go MCP client sent the wrong request-arg
    shape and decoded into a struct the server never returned. Bumping
    to hyperping-go v0.7.0 (canonical MCP signatures) restores real
    windowed MTTA values. Operators upgrading from chart 1.7.x will see
    this metric flip from 0 to its actual value at rollout; alert
    thresholds that were tuned around the buggy 0 must be re-baselined.
  • The same v0.7.0 client correctness fix repairs three other windowed
    MCP methods (GetMonitorMttr, GetMonitorResponseTime,
    GetMonitorUptime) which were affected by the same shape mismatch.
    The exporter only calls Mtta and ResponseTime today, so the
    observable behaviour change is MTTA only.

Added

  • config.periods field on each projects: entry. Allowed tokens:
    "24h", "7d", "30d", "90d", "365d". Absent or empty list
    defaults to ["24h"] so a chart 1.7.x values.yaml renders
    byte-identical projects.yaml on the wire. Period -> tier mapping is
    fixed: 24h is warm; 7d, 30d, 90d, 365d are cold. A period
    whose mapped tier is disabled on the project emits no series for
    that window (existing hyperping_exporter_tier_disabled self-metric
    covers the absence semantic).
  • Period fan-out on the seven period-bearing metrics
    (hyperping_monitor_sla_ratio, _downtime_seconds, _outages,
    _longest_outage_seconds, _mttr_seconds, _mtta_seconds, and
    hyperping_tenant_avg_sla_ratio). For configured periods mapped to
    cold tier (7d/30d/90d/365d) the cold-tier refresher issues one
    all-monitors MCP call per period (via v0.7.0's empty-uuids semantic)
    rather than N-monitor fan-out, keeping the new long-window fetches
    inside the MCP burst budget.
  • README section "Multi-period metric emission" with a copy-pastable
    YAML example showing the full opt-in across all five periods.
  • Chart render tests covering both the present-and-set and
    absent-key paths for config.periods so the byte-identical
    backward-compat claim is mechanically enforced.

Changed

  • BREAKING (series identity): hyperping_monitor_mtta_seconds now
    ALWAYS carries a period label, defaulting to period="24h" for
    projects that do not opt into additional windows. PromQL selectors
    and recording rules that referenced the unlabelled form must add
    period="24h" (or omit the explicit period to match the full
    fan-out) during this rollout. Grafana panels keyed on this metric
    should be reviewed.

  • BREAKING (label scheme): hyperping_data_age_seconds gains a
    period label alongside the existing tier label and switches to a
    single-emit scheme per (tier, period) pair. The HOT tier carries no
    window and continues to emit one series with period="". The WARM
    and COLD tiers now emit one series per configured period that maps
    to them (24h -> warm; 7d/30d/90d/365d -> cold) and NO empty-period
    legacy series. PromQL migration:

    # before (chart 1.7.x)
    sum(data_age_seconds{tier="warm"})
    # after (chart 1.8.0): still works; counts the period(s) mapped to warm
    sum(data_age_seconds{tier="warm"})
    # explicit per-period query
    data_age_seconds{tier="warm", period="24h"}
    

    An aggregation like sum(data_age_seconds{tier="warm"}) returns the
    same scalar as pre-1.8 for the default periods=["24h"] (one warm
    series), and the sum across configured periods for multi-period
    configs (which is the natural "tier-level age" answer). Prior to
    this release the v1.8.0 draft dual-emitted a legacy empty-period
    series alongside the new per-period series, which silently
    double-counted under sum; that behaviour has been removed.

  • github.com/develeap/hyperping-go pinned to v0.7.0 (was v0.6.3).
    Beyond the silent-zero MTTA fix, this release adds canonical
    windowed signatures func (c *MCPClient) GetMonitorXxx(ctx, from, to, uuids ...string) (*MonitorXxxResponse, error) and renames the
    response types (MttaReport -> MonitorMttaResponse, etc).

  • Chart bumped to 1.8.0; appVersion 1.8.0 (binary image tag follows).

Install

helm repo add develeap https://develeap.github.io/hyperping-exporter
helm repo update
helm install hyperping-exporter develeap/hyperping-exporter \
  --version 1.8.0

v1.7.1

07 Jun 12:37

Choose a tag to compare

What this fixes

Patch release unblocking production usage of v1.7.0.

The HTTP/2 ALPN bug from hyperping-go v0.6.2

v1.7.0 pinned hyperping-go v0.6.2, which had a critical HTTP/2 ALPN regression: every HTTPS API call to api.hyperping.io (or any non-localhost host) failed immediately with errors like

Unsolicited response received on idle HTTP channel starting with
  "\x00\x00\x12\x04\x00\x00\x00\x00\x00..."

or

net/http: HTTP/1.x transport connection broken:
  malformed HTTP response "\x00\x00\x12\x04..."

The byte pattern is an HTTP/2 SETTINGS frame being misread by Go's HTTP/1 parser. Affected both the REST cache refresh path and the MCP client.

v1.7.1 bumps to hyperping-go v0.6.3, which is the upstream fix. Full root-cause analysis: develeap/hyperping-go#37.

If you've been running v1.7.0 with GODEBUG=http2client=0 as a workaround, you can remove that env var after upgrading to v1.7.1.

Go stdlib CVE fix

Toolchain bumped from 1.26.3 to 1.26.4, resolving two stdlib vulnerabilities disclosed shortly before this release:

  • GO-2026-5039 — arbitrary inputs unescaped in net/textproto errors (reached via the MCP transport's MIME header parsing).
  • GO-2026-5037 / CVE-2026-42504 — inefficient candidate hostname parsing in crypto/x509 (reached via TLS hostname verification).

Both fixed in stdlib 1.26.4.

What stayed the same

No behavior changes in the exporter binary itself. Same flags, same metrics, same multi-project fan-out as v1.7.0. Drop-in upgrade.

Upgrade

docker pull khaledsalhabdeveleap/hyperping-exporter:1.7.1

Or in Helm:

image:
  tag: "1.7.1"

Chart version is unchanged (1.6.0); only the image tag needs to flip.

v1.7.0 — Multi-project exporter

02 Jun 14:08
588a44a

Choose a tag to compare

Multi-project exporter (binary 1.7.0)

One exporter process now scrapes multiple Hyperping projects concurrently.

Added

  • --projects-file (env HYPERPING_PROJECTS_FILE): a YAML list of {id, apiKey|apiKeyFile, mcpUrl?, excludeNamePattern?} entries. The binary fans out one hyperping.Client + transport + *collector.Collector per project, each with its own refresh loop, so a rate-limit pause on one project does not stall the others.
  • A project constLabel (value = the project id) is injected on every hyperping_*, hyperping_client_*, and hyperping_mcp_* series, so multi-project series coexist on one registry without collision.
  • /readyz is OR across all configured projects; per-project readiness is exposed via hyperping_project_ready{project=<id>}.

Compatibility / cutover

  • The legacy single-key path (--api-key / --api-key-file / HYPERPING_API_KEY) is preserved and synthesises one project with id default (emitting project="default").
  • Downstream alerts/dashboards that match series without a project=... matcher must add one. During cutover use transitional selectors like project=~"hyp_core|", and deploy in projects mode (not legacy) so series carry real ids (hyp_core, hyp_infra) rather than default.

Pairs with Helm chart 1.6.0 (tag chart-v1.6.0).

Chart 1.6.0 — Multi-project

02 Jun 14:04
588a44a

Choose a tag to compare

Helm chart 1.6.0 — multi-project

Adds config.projects support for the multi-project exporter. appVersion 1.5.1 -> 1.7.0; chart 1.5.4 -> 1.6.0.

Added

  • config.projects (list): when non-empty, the chart renders --projects-file=/etc/hyperping/projects.yaml and mounts a projected volume composing the chart-managed Secret with each project's key.
  • A single ExternalSecret materialises one Secret <release>-projects carrying one api-key-<id> data key per project.
  • validateProjects helper: enforces unique ids matching [a-zA-Z0-9._-]{1,64}, exactly one secret source per project, and mutual exclusion with the legacy top-level apiKey / existingSecret / externalSecret.remoteRef.

Compatibility

  • Legacy single-key deploys (config.apiKey / existingSecret) render unchanged.

Cutover notes (downstream alerting)

  • Deploy via config.projects, NOT the legacy config.apiKey: the legacy path emits project="default", which the transitional project=~"hyp_core|" alert selectors do not match (fleet-wide alert dead-spot).
  • Set config.cacheMode: tiered if the per-tier HyperpingDataStale alerts are expected to fire; the default legacy emits only tier="hot", so warm/cold staleness rules stay inert.

Install

helm repo add hyp-exporter https://develeap.github.io/hyperping-exporter
helm repo update
helm install hyperping-exporter hyp-exporter/hyperping-exporter --version 1.6.0

v1.6.0

31 May 13:13
c95f226

Choose a tag to compare

What's New

Security hardening

This release lands the result of an independent security audit. Highlights:

  • Go toolchain bumped to 1.26.3 (from 1.26.2), clearing eight stdlib CVEs (GO-2026-4918 HTTP/2 SETTINGS_MAX_FRAME_SIZE infinite loop, GO-2026-4971/4980/4981/4982, and friends). The release workflow now hard-gates on govulncheck v1.3.0, so a vulnerable transitive dependency cannot ship to operators in a future release.
  • Label-bomb mitigation: every metric-label value (monitor name, healthcheck name) is truncated to 256 bytes at emit time via a shared, UTF-8-safe helper. A compromised tenant or operator with rename rights can no longer force the Prometheus side to ingest multi-kilobyte label values multiplied across every per-monitor series.
  • Tenant label validation: extractTenant now rejects any character outside [a-zA-Z0-9._-] and bounds the resulting tag at 64 bytes. Tags with colons, slashes, spaces, or unicode characters now collapse to the empty string rather than flowing verbatim into the label set.
  • /metrics startup warning when binding any-interface (:port, 0.0.0.0:port, [::]:port) without --web.config.file. The default bind is unchanged; this is a hint, not a hard fail, but the word "unauthenticated" appears in the log line so it is grep-able during incident response.
  • HTTP server hardening: IdleTimeout (120s) and MaxHeaderBytes (1 MiB) explicitly set on the metrics http.Server to guard against keep-alive idle-connection DoS and header-bomb DoS.
  • SDK upgrade to hyperping-go v0.6.2, which broadens the credential redactor to cover Authorization (every scheme, not just Bearer), Cookie, Set-Cookie, Proxy-Authorization, and the X-Api-Key family. Closes the path by which a server-side error message could leak any of those headers through APIError.Error().

API key sourcing

  • --api-key-file <path>: read the Hyperping API key from a file rather than from argv or the environment. Recommended source for container deployments; the file can be owned by a dedicated user and is not visible via ps or /proc/<pid>/cmdline. Trailing CR/LF combinations are stripped (LF, CRLF, CR, and multi-newline tails all yield the same key).
  • --api-key CLI flag is deprecated and emits a stderr warning at startup. It still works for one cycle. Prefer HYPERPING_API_KEY or --api-key-file; the CLI form leaks the secret into /proc/<pid>/cmdline.

Breaking changes for Prometheus operators

Three of the security fixes silently change emitted label values. If you have alerts or dashboards keyed on these, please audit them before upgrading.

Metric-label values capped at 256 bytes

Affects every series carrying a name label: hyperping_monitor_up, hyperping_monitor_response_time_seconds, hyperping_monitor_sla_ratio, hyperping_healthcheck_*. If any of your monitor or healthcheck names exceeds 256 bytes, the emitted series will no longer carry the full value. Audit selectors of the form:

hyperping_monitor_up{name="The full literal name longer than 256 bytes..."}

Either shorten the upstream name, or migrate to a prefix regex against the truncated form.

Tenant label rejects non-token characters

tenant label now matches ^[a-zA-Z0-9._-]{1,64}$ or is empty. Existing tags containing :, /, spaces, or unicode collapse to tenant="". Queries like:

sum by (tenant) (hyperping_monitor_up)
hyperping_tenant_health_score{tenant="ops:edge"}

bucket affected monitors under the empty-tenant series. Rename upstream tags to use only [a-zA-Z0-9._-] to restore the previous behaviour.

SLA name-label now stable across renames

hyperping_monitor_sla_ratio now derives its name label from the live monitor record rather than the per-report snapshot. Mid-window renames no longer fragment SLA series against base monitor series for the same uuid. If you joined SLA and base series on (uuid, name), switch the join to uuid only to keep the previous behaviour.

Container image

Multi-arch image built for linux/amd64 and linux/arm64, pushed to both registries:

  • docker.io/khaledsalhabdeveleap/hyperping-exporter:1.6.0
  • ghcr.io/develeap/hyperping-exporter:1.6.0

Image digest: sha256:1fbdbedb5d92ced35d01e5d2df271f877c43d61a3f848b86b985c99ab8adb9d6

Both registry pushes are keyless-signed with cosign; transparency-log entries are in Sigstore Rekor (logIndex 1682206081 for Docker Hub, 1682206783 for GHCR). Verify with:

cosign verify \
  --certificate-identity-regexp "https://github.com/develeap/hyperping-exporter/.github/workflows/release.yml@refs/tags/v1.6.0" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
  ghcr.io/develeap/hyperping-exporter@sha256:1fbdbedb5d92ced35d01e5d2df271f877c43d61a3f848b86b985c99ab8adb9d6

SBOMs for all 8 archive variants are attached to this release as *.sbom.json.

Upgrade

# Helm
image:
  repository: ghcr.io/develeap/hyperping-exporter
  tag: "1.6.0"
# Docker Compose
image: ghcr.io/develeap/hyperping-exporter:1.6.0

If you currently pass --api-key, plan to migrate to HYPERPING_API_KEY or --api-key-file before the deprecation lands as a removal in a future release.

Full Changelog

See CHANGELOG.md for the complete diff.