What's new in v0.5.0
Major feature release: v0.5.0 introduces the report subcommand and its single-file HTML dashboard, a new GET /api/export/report daemon endpoint that pairs with it for live snapshots, a fourth pg_stat ranking (by_io_blocks), and a check-versions CI guard that refuses to publish on a tag / Cargo.toml mismatch. The Report tree also gains Deserialize so saved baselines round-trip back in. 1248 tests across the workspace, clippy strict (-D warnings, #![warn(clippy::pedantic)]) clean, zero open SonarCloud issues on the shipped code.
Added
reportsubcommand: single-file HTML dashboard.perf-sentinel report --input traces.json --output report.htmlproduces a self-contained HTML file with six possible tabs (Findings, Explain, GreenOps unconditionally, pg_stat / Diff / Correlations when the relevant data is present). Works offline fromfile://, zero CDN / fonts / external resources, no build step. The sink embeds findings-only traces and trims to a ~5 MB target via a linear-time prefix-sum scan that keeps the highest-IIS traces first.--inputaccepts a trace file, a pre-computedReportJSON, or-for stdin (auto-detects array-of-events vs Report object, BOM-tolerant). Security model: user-controlled data is injected inside a<script type="application/json">block, rendered exclusively viaElement.textContent, and guarded by a build-timeno_forbidden_apis_in_templatetest that rejectsinnerHTML,insertAdjacentHTML,outerHTML,eval,new Function,DOMParser,createContextualFragmentandsetAttribute("on*"). The</substring is escaped to<\/in the serialized payload so a user value cannot close the script block early. Full design rationale indocs/design/07-CLI-CONFIG-RELEASE.md§report subcommand. Implemented incrates/sentinel-core/src/report/html.rsandhtml_template.html.--pg-stat FILEand--pg-stat-prometheus URLflags onreport. Embed a PostgreSQLpg_stat_statementshotspot tab alongside the trace findings. Four rankings driven by a sub-switcher in the dashboard: by_total_time, by_calls, by_mean_time, by_io_blocks. The Prometheus path reuses the one-shotfetch_from_prometheushelper.--pg-statand--pg-stat-prometheusare mutually exclusive at the clap level.--before baseline.jsonflag onreport. Compare the current run against a baseline Report and light up a Diff tab with four sections: new findings (clickable, open Explain), resolved findings, severity changes and per-endpoint I/O op deltas. Identity matching is(finding_type, service, source_endpoint, pattern.template).- Daemon endpoint
GET /api/export/report. Returns the daemon's current state as aReportJSON snapshot, shape-identical toanalyze --format json. Pipecurl -s http://daemon:4318/api/export/report | perf-sentinel report --input -for a browser dashboard of live production state. Cold-start behavior: HTTP 503 with{"error": "daemon has not yet processed any events"}until the first OTLP batch has been processed, so a fresh daemon never renders misleading zero-counter views. Snapshot is not atomic acrossfindingsandcorrelations, the two collections can be one batch apart. Documented indocs/QUERY-API.md+ FR anddocs/design/06-INGESTION-AND-DAEMON.md. - Dashboard ergonomics. Per-tab Export CSV button on Findings, pg_stat, Diff and Correlations, with RFC 4180 escaping plus an OWASP formula-injection guard that prefixes an apostrophe on cells starting with
=,+,-,@, or tab. Deep-link hash encodes the active tab, search term, pg_stat ranking and Findings severity / service chips, so a shared URL likereport.html#pgstat&ranking=mean_time&search=paymentrestores the exact filtered view. sessionStorage persistence for theme and last-active pg_stat ranking, tab-scoped to avoidfile://origin collisions. Native<dialog>cheatsheet modal opened via?with focus trap managed by the browser, vim-styleg f/g e/g p/g d/g c/g rtab-switch shortcuts with a 1000ms timeout on the pendinggand autorepeat guarded. Four-tier Esc ladder: close cheatsheet -> close search -> back from Explain -> clear active filter chips. Findings pagination replaces a hard 500-row cap with aShow N more findings (remaining M)button; every filter, search or hash apply resets the visible count. - Fourth pg_stat ranking:
by_io_blocks(shared_blks_hit + shared_blks_read). Cache-pressure signal that complementsby_total_time: flags queries that touch the most shared-buffer pages regardless of whether they were hit or miss. The stable ranking order is[by_total_time, by_calls, by_mean_time, by_io_blocks], new rankings are appended, existing indices never reassign so the HTML sub-switcher and any other position-indexed consumer stay stable. Documented indocs/design/06-INGESTION-AND-DAEMON.md§ "Four-ranking output". - CI:
check-versionsrelease workflow guard. A new job runs first on anyv*tag push and refuses to build or publish anything if the tag does not matchworkspace.package.versionin the rootCargo.tomland everycrates/*/Cargo.toml(hardcoded versions honored,version.workspace = trueresolves to the workspace). Logic lives inscripts/check-tag-version.shso the same guard runs locally before tagging:./scripts/check-tag-version.sh v0.5.0. Every downstream job transitively depends on it vianeeds: check-versionsonbuild, so a version mismatch aborts the workflow in ~10 seconds before any GitHub Release is created, any binary is uploaded, or any crate is pushed to crates.io.
Changed
Reporttree now derivesDeserialize. Cascade acrossReport,Analysis,GreenSummary,QualityGate,QualityRule,TopOffender,CarbonReport,CarbonEstimate,RegionBreakdown,IntensitySourceso a saved Report JSON can be fed back intoperf-sentinel report --before baseline.jsonfor diff mode. Three&'static strfields (CarbonEstimate::model,CarbonEstimate::methodology,RegionBreakdown::status) becomeStringfor serde round-trip. A module-level invariant comment incrates/sentinel-core/src/report/mod.rspins the "every new field must beOption<T>or carry#[serde(default)]" rule so stored baselines keep parsing across future minor versions.deny_unknown_fieldsdeliberately not applied, the trade-off is documented.
Docs
- New documentation for the v0.5.0 surface across EN + FR mirrors.
docs/INTEGRATION.md+ FR gain a dedicated HTML dashboard section with the keyboard ladder, pagination and sharing semantics.docs/CONFIGURATION.md+ FR subcommand table addsreport, plus thetempoandcalibraterows that were missing.docs/QUERY-API.md+ FR documentGET /api/export/reportand its cold-start contract.docs/RUNBOOK.md+ FR add a/api/export/report returns 503 or an empty reporttroubleshooting entry.docs/LIMITATIONS.md+ FR document the CSV formula-injection guard.docs/design/06-INGESTION-AND-DAEMON.md+ FR add the four-ranking section, the--pg-stat-prometheusintegration paragraph, and the/api/export/reportsnapshot semantics.docs/design/07-CLI-CONFIG-RELEASE.md+ FR describe thereportsubcommand and its client-side ergonomics. Mermaid diagramscli-commands.mmdandquery-api.mmdupdated to depict the new subcommands and endpoint (SVGs regenerated). SECURITY.mdgrows six HTML-dashboard-specific bullets coveringtextContent-only rendering, the</script-tag escape, prototype-pollution hardening viaObject.create(null), CSV formula-injection guard, deep-link hash allowlist, and offline self-contained output with zero CDN/font fetches. Supported-versions table bumped to0.5.x.CONTRIBUTING.mdgrows aRelease processsection pointing atscripts/check-tag-version.shand listing every non-Cargo.toml file that also takes a version bump.- README + README-FR gain dedicated
HTML dashboard reportandPR regression diffsubsections with representative command-line examples. CHANGELOG.mdintroduced at the repo root in Keep-a-Changelog format, seeded with the[0.5.0]section that mirrors this release body.
Install
Prebuilt binaries (Linux amd64 / arm64, macOS arm64, Windows amd64):
curl -LO https://github.com/robintra/perf-sentinel/releases/download/v0.5.0/perf-sentinel-linux-amd64
chmod +x perf-sentinel-linux-amd64
sudo mv perf-sentinel-linux-amd64 /usr/local/bin/perf-sentinelLinux 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-sentinelDocker:
docker run --rm -p 4317:4317 -p 4318:4318 \
ghcr.io/robintra/perf-sentinel:0.5.0 watch --listen-address 0.0.0.0Also available on Docker Hub: robintrassard/perf-sentinel:0.5.0.
Verify the binary against SHA256SUMS.txt:
curl -LO https://github.com/robintra/perf-sentinel/releases/download/v0.5.0/SHA256SUMS.txt
sha256sum -c SHA256SUMS.txt --ignore-missingFull diff: v0.4.8...v0.5.0