perf-sentinel v0.4.0
Phase 6 release: turns the daemon from a pattern detector into an insight engine. Four headline features, plus hardening.
Highlights
- Cross-trace temporal correlation (daemon mode). New
detect/correlate_cross.rsmodule with aCrossTraceCorrelatorthat detects recurring co-occurrences between findings from different services within a rolling window (default 10 minutes). Uses Algorithm R reservoir sampling (capped at 256 samples per pair) seeded by a deterministicSplitMix64+ FNV-1a endpoint hash so sampled lags stay reproducible across runs. Incrementalsource_totalsandselect_nth_unstable_by_keybounded eviction keep the per-tick cost O(occurrences) instead of rebuilding every tick. Opt-in via[daemon.correlation] enabled = truewithwindow_minutes,lag_threshold_ms,min_co_occurrences,min_confidence, andmax_tracked_pairsknobs. - OTel source code attributes. Findings now carry a
code_locationfield populated fromcode.function,code.filepath,code.lineno, andcode.namespacespan attributes. Supported across OTLP (gRPC + HTTP), Jaeger, and Zipkin ingesters. The CLI renders a newSource:line on findings (namespace.function (filepath:lineno), omitting absent parts), and SARIF v2.1.0 output gainsphysicalLocationentries whenfilepathis present, enabling inline annotations in GitHub and GitLab code scanning views. Hostile filepath values (literal and percent-encoded..traversal, absolute paths, URL schemes, overlong UTF-8, BiDi / invisible Unicode) are rejected in the SARIF sanitizer to close Trojan Source (CVE-2021-42574) vectors. - Automated pg_stat ingestion from Prometheus. New
perf-sentinel pg-stat --prometheus http://prometheus:9090scrapespg_stat_statements_seconds_totalvia the Prometheus HTTP API and produces the samePgStatReportas the existing file-based path. Zero new dependencies, reuses thehttp_clientmodule introduced in v0.3.0. Endpoints are validated at config load (scheme must behttp/https, userinfo rejected) and redacted in error messages. File-based--input traces.csvcontinues to work unchanged. - Daemon query API and
querysubcommand. The daemon exposes its internal state via five HTTP endpoints on the existing port 4318 (alongside/v1/tracesand/metrics):GET /api/findings(filterable byservice,type,severity,limit, capped at 1000),GET /api/findings/{trace_id},GET /api/explain/{trace_id}(tree with findings inline, served from the in-memory trace window),GET /api/correlations(active cross-trace correlations), andGET /api/status(uptime, active traces, stored findings count, version). A newFindingsStorering buffer (default 10000, configurable via[daemon] max_retained_findings) retains recent findings for querying. A newperf-sentinel query --daemon http://localhost:4318 <action>CLI subcommand queries these endpoints with five sub-actions (findings,explain,inspect,correlations,status), rendering colored terminal output by default and--format jsonwhen scripting.inspectfetches explain trees in parallel viatokio::task::JoinSet(concurrency 16) so the TUI opens in ~300 ms on 100 traces instead of ~5 s sequentially. Gated by[daemon] api_enabled(defaulttrue); seedocs/LIMITATIONS.mdfor the no-auth threat model.
Breaking changes
DaemonError::TlsConfig(Box<dyn std::error::Error>)replaced by a typedTlsConfigErrorenum with five concrete variants (ReadCert,ReadKey,ParseCerts,ParseKey,ServerConfig). Callers that matched on the boxed error must now match on the enum. Source chains are preserved via#[source].- All public error enums are now
#[non_exhaustive](DaemonError,TlsConfigError,ConfigError,PgStatError,JsonIngestError,JaegerIngestError,ZipkinIngestError,TempoError,FetchError,CalibrationError,SarifError). Externalmatchexpressions on these types must include a catch-all arm going forward; this lets subsequent minor releases add variants without a major bump. SpanEventgains four optional fields (code_function,code_filepath,code_lineno,code_namespace).Findinggains an optionalcode_locationfield. All marked#[serde(default, skip_serializing_if = "Option::is_none")], so JSON consumers that ignore unknown fields are unaffected.
Observability, performance and hardening
- Daemon query API performance. Endpoint responses cap at 1000 items to bound payload size under pathological queries. The findings store clones outside the lock to minimize hold time, short-circuits on
max_size == 0, and sizes the initialVecDequecapacity atmin(max_size, 4096). The/api/explain/{trace_id}endpoint reads from the in-memory trace window and runsdetect::detect()inline, with no per-request disk I/O. - Runtime reuse.
perf-sentinel queryandpg-stat --prometheususe the parent#[tokio::main]runtime directly instead of constructing a nestedRuntime(which would have panicked at runtime). - Cognitive complexity reduction. Four functions flagged by SonarCloud's
rust:S3776rule were split into named helpers without behavioral change:print_findings(33 → under 15, extracted intoprint_finding_entry,print_finding_impact,format_code_location, and severity helpers),cmd_query(62 → under 15, one helper per action plusbuild_findings_pathandprint_pretty_json),daemon::run(19 → under 15, extractedingest_event_batch,evict_expired_traces,flush_evicted,shutdown_listeners, and aServiceMeterstruct),CrossTraceCorrelator::ingest(18 → under 15, extractedevict_stale,record_co_occurrences,enforce_pair_cap). - TUI Source line. The
inspectTUI detail panel now renders the sameSource:line as the CLI text output when findings carry acode_location.
Docs and assets
- Design docs updated EN + FR:
04-DETECTION(cross-trace correlation algorithm),06-INGESTION-AND-DAEMON(daemon query API),07-CLI-CONFIG-RELEASE(query subcommand, pg-stat Prometheus flag). New Mermaid diagramquery-api.mmdwith light + dark SVG exports, wired intodocs/ARCHITECTURE.mdanddocs/design/06-INGESTION-AND-DAEMON.md(and their FR counterparts). docs/CONFIGURATION.md,docs/LIMITATIONS.md,GUIDED-TOUR.md,ENTERPRISE-JAVA-INTEGRATION-FR.mdupdated with Phase 6 content.docs/img/analyze/*anddocs/img/inspect/*regenerated so the newSource:line shows on every n+1, redundant, and slow finding. Demo fixturetests/fixtures/demo.jsongained plausiblecode.*attributes per trace (repository / client classes, Java filepaths, line numbers).
Install
# Prebuilt binaries (Linux amd64 / arm64, macOS arm64, Windows amd64)
curl -LO https://github.com/robintra/perf-sentinel/releases/download/v0.4.0/perf-sentinel-linux-amd64
chmod +x perf-sentinel-linux-amd64
sudo mv perf-sentinel-linux-amd64 /usr/local/bin/perf-sentinel# From crates.io
cargo install perf-sentinel# Docker
docker pull robintrassard/perf-sentinel:0.4.0Also available on GHCR: ghcr.io/robintra/perf-sentinel:0.4.0
Verify the binary against SHA256SUMS.txt:
curl -LO https://github.com/robintra/perf-sentinel/releases/download/v0.4.0/SHA256SUMS.txt
sha256sum -c SHA256SUMS.txt --ignore-missingFull changelog
Diff against the previous release: v0.3.2...v0.4.0. 4 commits, covering Phase 6 (cross-trace correlation, source causality, automated pg_stat, daemon query API) plus the SonarCloud cognitive-complexity refactor.