v0.7.5
What's new in v0.7.5
v0.7.5 is a bug fix on the Kepler integration that shipped in v0.7.4. The scraper targeted metric names and a label name that no released Kepler version ever published, so on any real Kepler v2 cluster every scrape returned HTTP 200 with zero matching samples, the kepler_ebpf co2.model tag never lit up, and carbon attribution silently fell through to the proxy chain. Cross-checked against sustainable-computing.io/kepler/design/metrics/ and the Kepler repo docs/user/metrics.md. The Redfish BMC integration is unchanged, the DMTF schema we target stayed stable. No CLI surface change, no daemon wire protocol change, no report JSON schema change.
Kepler scraper aligned to the Kepler v2 series
The container variant now reads kepler_container_cpu_joules_total (was kepler_container_joules_total) keyed by container_name (unchanged). The new Process variant reads kepler_process_cpu_joules_total keyed by comm (the kernel command-name label, was the non-existent command).
Removed metric_kind variants
metric_kind = "process_package" and metric_kind = "process_dram" are removed. Both targeted metrics that Kepler never published at any granularity. Operators with these values in .perf-sentinel.toml get a config-load error pointing at the replacement:
[green.kepler] metric_kind 'process_package' was removed in v0.7.5.
Kepler v2 only exposes per-process CPU joules, use 'process' instead.
The new accepted set is "container" (default) and "process", matched case-insensitively for parity with parse_daemon_environment on the same TOML surface.
Defensive hardening
So the same class of bug cannot ship silently again, the rest of the diff adds layered validation:
- Config-load pre-validation.
load_from_strnow parsesmetric_kindbefore the lossyConfig::fromconversion, mirroring the daemon-environment pattern. Invalid values surface asConfigError::Validationat startup instead of a buriedtracing::errorfollowed by silent Kepler disable. TASK_COMM_LENcap. Whenmetric_kind = "process",service_mappingslabel values are capped at 15 bytes to match the Linux kernel'scommtruncation. A label longer than 15 bytes can never match a real sample on the wire, so rejecting at config load saves a debugging round.- Cardinality cap.
service_mappingsis capped at 1024 entries, mirroringMAX_SERVICE_REGIONS. Bounds the config-load memory footprint against fat-finger or hostile configs. - Control-character rejection.
parse_kepler_metric_kindandvalidate_kepler_service_mappingsreject C0/C1 control characters in the operator-supplied strings before any error message interpolates them. Closes a low-severity ANSI-injection vector where a hostile.perf-sentinel.tomlcould embed escape sequences and have them rendered through stderr the first time an operator ran the binary on the file.
Zero-sample diagnostic
A new warn-once log fires after three consecutive HTTP-200 scrapes that yield zero matching samples, the exact failure mode of running v0.7.5 against a Kepler version older than v0.10:
Kepler endpoint replied HTTP 200 but no samples matched the configured
metric across the last 3 ticks. Most common cause: the cluster runs a
Kepler exporter older than v0.10 (legacy metric names without the
'_cpu_' infix). Other causes: metric_kind mismatched with the deployment
topology, or service_mappings label values that do not exist on the wire.
perf_sentinel_kepler_last_scrape_age_seconds keeps its existing semantics, it resets to 0 on every HTTP-200. The new docs note in docs/METRICS.md "Kepler scrape counters" calls out that alerts driven only by the gauge will not catch the zero-sample case and should be paired with rate(perf_sentinel_kepler_scrape_total{status="success"}[5m]) and the daemon-side co2.model tag presence.
Documentation
docs/CONFIGURATION.mdand its FR mirror:metric_kindreference table reduced tocontainer/process, examples updated.docs/LIMITATIONS.mdand its FR mirror: "Kepler precision bounds" updated,metric_kindreferences corrected.docs/METRICS.md: new sections "Kepler scrape counters" and "Redfish scrape counters" mirroring the existing Scaphandre section, with the zero-sample staleness note.crates/sentinel-core/src/score/kepler/config.rs:KeplerMetricKindenum doc warns explicitly that Kepler v1 / pre-0.10 deployments are not supported.crates/sentinel-core/src/score/kepler/parser.rs: module doc refreshed to reflect the Kepler v2 series.examples/perf-sentinel.toml: commented[green.kepler]block updated.
Tests
The diff adds 11 unit tests against the new code paths and renames the existing Kepler fixtures (26 hardcoded Prometheus literals) to the v2 series:
parse_kepler_metric_kindcase-insensitive matching, empty-string rejection, raw value preservation in error messages, control-character rejection, legacy migration round-trip throughload_from_str.validate_kepler_service_mappings15-byte boundary forProcess, cardinality cap.convert_kepler_section_with_envProcesshappy path.track_zero_sample_streakfour edges: under threshold, at threshold, latch persistence over a longer streak, reset on non-empty scrape.
Why this is a patch and not a minor
The bug fix removes two metric_kind enum values, which is technically a config-input breaking change, but neither value ever produced a working Kepler scraper in v0.7.4. No operator could be running v0.7.4 with these values and getting useful telemetry, so the surface area of compat impact is empty. The renamed container metric (kepler_container_joules_total → kepler_container_cpu_joules_total) is internal to the scraper, the operator-facing metric_kind = "container" config value is unchanged. The CLI subcommand surface, daemon HTTP routes, and report JSON schema are byte-for-byte identical to v0.7.4. The co2.model enum gains no new value, the existing kepler_ebpf tag is preserved.
Verifying this release
# Binary integrity via SLSA Build L3 attestation
gh attestation verify perf-sentinel-linux-amd64 \
--owner robintra --repo perf-sentinel
# A periodic disclosure produced by this binary
perf-sentinel verify-hash --report perf-sentinel-report.json \
--expected-identity "https://github.com/robintra/perf-sentinel/.github/workflows/release.yml@refs/tags/v0.7.5" \
--expected-issuer "https://token.actions.githubusercontent.com"gh CLI 2.49 or newer required for gh attestation verify (unchanged from v0.7.2).
Full Changelog: v0.7.4...v0.7.5