Skip to content

v0.5.11

Choose a tag to compare

@github-actions github-actions released this 27 Apr 22:34
· 588 commits to main since this release

What's new in v0.5.11

Electricity Maps published their v4 API, with v3 still served but considered legacy. v0.5.10 was hard-coded against /v3. The v4 doc reference also documents two query parameters that perf-sentinel was not surfacing as TOML knobs: emissionFactorType (lifecycle vs direct) and temporalGranularity (hourly, 5-minute, 15-minute). v0.5.11 closes the version migration and exposes the two knobs.

The default endpoint flips to https://api.electricitymaps.com/v4. The response schema on carbon-intensity/latest is byte-identical between v3 and v4 (verified manually with the sandbox token and locked in by a new wire-parity test), so the migration is transparent for downstream consumers. green_summary.regions[] rows look exactly the same regardless of which API path the daemon was configured against. Existing configs that pin endpoint = "https://api.electricitymaps.com/v3" keep loading and continue to work, but the daemon now logs a tracing::warn! once at startup pointing the operator at the v4 migration. The endpoint string is sanitized through sanitize_for_terminal before logging, mirroring the 0.5.10 pattern on intensity_estimation_method, so a hostile TOML cannot inject ANSI escape sequences into the log stream. Trailing slashes on the configured endpoint are also normalized at config load, so a copy-paste like endpoint = "https://api.electricitymaps.com/v4/" no longer produces a double-slash URL.

The two new TOML knobs map to the v4 query parameters one-to-one:

  • [green.electricity_maps] emission_factor_type = "lifecycle" | "direct" defaults to lifecycle, matching the API default. Set direct when the GHG Protocol Scope 2 Guidance applies and only the combustion-phase boundary is reportable.
  • [green.electricity_maps] temporal_granularity = "hourly" | "5_minutes" | "15_minutes" defaults to hourly. Sub-hour values require a paid Electricity Maps plan that exposes them, otherwise the API silently coarsens to hourly.

Both knobs use a fail-graceful parser: an unknown value (typo or unsupported) emits a tracing::warn! and falls back to the default. The unknown value is sanitized through sanitize_for_terminal before logging. The query parameters are only appended to the request URL when they differ from the API defaults, so users who do not opt into the knobs see byte-identical wire traffic relative to v0.5.10. The green_summary JSON, the GreenOps aggregates, the dashboard rendering, and the terminal output are all unchanged for default settings.

A small internal refactor splits the inline format! URL construction into a pure helper build_request_url(endpoint, zone, eft, tg) -> String that is unit-tested with all four combinations (default-only, each knob individually, both together). The EmissionFactorType and TemporalGranularity enums are public and re-exported from score::electricity_maps, with from_config(Option<&str>) -> Self (case-insensitive, graceful fallback) and as_query_value(self) -> &'static str (URL serialization).

Added

  • [green.electricity_maps] emission_factor_type TOML knob. Maps to the emissionFactorType v4 query parameter. Accepted: "lifecycle" (default) and "direct". Documented in docs/CONFIGURATION.md (EN+FR).
  • [green.electricity_maps] temporal_granularity TOML knob. Maps to the temporalGranularity v4 query parameter. Accepted: "hourly" (default), "5_minutes", "15_minutes". Same docs.
  • Public EmissionFactorType and TemporalGranularity enums in crates/sentinel-core/src/score/electricity_maps/config.rs, re-exported from the parent module. Both expose from_config (case-insensitive parsing with graceful fallback to default + sanitized warn on unknown) and as_query_value (URL serialization).
  • Public constant DEFAULT_ELECTRICITY_MAPS_ENDPOINT in the same module, single source of truth used by both the production fallback in convert_electricity_maps_section_with_env and the test fixtures.
  • Pure helper build_request_url in crates/sentinel-core/src/score/electricity_maps/scraper.rs. Composes the full request URL from endpoint, zone, and the two knobs. Query params only appended when non-default, so the wire stays byte-identical to pre-0.5.11 for users who do not opt into the knobs.

Changed

  • Default Electricity Maps endpoint flipped from v3 to v4. New configs (no explicit endpoint) target https://api.electricitymaps.com/v4.
  • api_endpoint is normalized at config load: trailing slashes are trimmed, so endpoint = "https://api.electricitymaps.com/v4/" no longer produces a double-slash URL when the scraper appends /carbon-intensity/latest?zone=....
  • BREAKING (perf-sentinel-core, pre-1.0 so minor-bump allowed): ElectricityMapsConfig gains two new public fields emission_factor_type: EmissionFactorType and temporal_granularity: TemporalGranularity. External consumers constructing the struct directly (no in-tree consumers do today) must add the two fields.

Deprecated

  • Electricity Maps API v3 endpoint. Configs that pin endpoint = "https://api.electricitymaps.com/v3" keep loading and continue to work, but the daemon logs a tracing::warn! once at startup pointing the operator at the v4 migration. To silence the warning, switch to the v4 endpoint. To deliberately stay on v3 (for example to A/B-validate against v4), keep the v3 URL and acknowledge the warning. Electricity Maps has not announced a v3 retirement date, this is forward-defense.

Security

  • Endpoint string sanitized at log time. The deprecation warn passes the endpoint through sanitize_for_terminal before logging, so a hostile TOML config carrying ANSI / OSC 8 / control bytes in the URL cannot inject terminal escape sequences into the daemon's log stream. Mirrors the 0.5.10 fix on intensity_estimation_method.
  • Unknown knob values sanitized at log time. Same hardening applied to the fail-graceful warn path on emission_factor_type and temporal_granularity. A typo like temporal_granularity = "WIPE\x1b[2J" is logged with control bytes replaced by ?.

Behavior

  • Wire format unchanged for users not opting into the new knobs. URL shape is byte-identical to pre-0.5.11.
  • green_summary.regions[] rows are byte-identical between v3 and v4 (locked in by a new fetch_intensity_v3_and_v4_responses_parse_identically regression test).
  • GreenOps aggregates unchanged. avoidable_io_ops, IIS, waste ratio, top_offenders are not touched. The new knobs change which carbon intensity value the API returns, but perf-sentinel treats the value as opaque and propagates it without branching.
  • Existing scraper integration tests (mock HTTP servers on 127.0.0.1:NNNN without /vN suffix) are unaffected by the new deprecation detection.
  • Backward compatible with pre-0.5.11 JSON reports: the new fields are absent from older reports, the dashboard and terminal handle absence cleanly.

Install

Prebuilt binaries (Linux amd64 / arm64, macOS arm64, Windows amd64):

curl -LO https://github.com/robintra/perf-sentinel/releases/download/v0.5.11/perf-sentinel-linux-amd64
chmod +x perf-sentinel-linux-amd64
sudo mv perf-sentinel-linux-amd64 /usr/local/bin/perf-sentinel

Linux binaries are statically linked against musl and run on any distribution (Alpine, Debian, RHEL, Ubuntu any version) regardless of glibc version, and inside FROM scratch images.

From crates.io:

cargo install perf-sentinel

Docker:

docker run --rm -p 4317:4317 -p 4318:4318 \
  ghcr.io/robintra/perf-sentinel:0.5.11 watch --listen-address 0.0.0.0

Also available on Docker Hub: robintrassard/perf-sentinel:0.5.11.

Helm (chart 0.2.14 ships 0.5.11 as its appVersion default):

helm install perf-sentinel oci://ghcr.io/robintra/charts/perf-sentinel \
  --version 0.2.14 \
  --namespace observability --create-namespace

Verify the binary against SHA256SUMS.txt:

curl -LO https://github.com/robintra/perf-sentinel/releases/download/v0.5.11/SHA256SUMS.txt
sha256sum -c SHA256SUMS.txt --ignore-missing

Full diff: v0.5.10...v0.5.11