Changelog¶
All notable changes to this project are documented in this file. The format loosely follows Keep a Changelog; the category names are this project's own (matching the type/scope table in CONTRIBUTING.md) rather than Keep a Changelog's stricter Added/Changed/Fixed vocabulary, since a flat categorization loses the thematic grouping that makes these notes readable.
How this file is maintained: every PR that ships a user-facing feat,
fix, or perf change adds its own newsfragment under changelog.d/
(format documented in CONTRIBUTING.md,
enforced by .github/workflows/changelog-fragment-check.yml). Before
cutting a release tag, the maintainer runs towncrier build --version
vX.Y.Z --yes, which compiles every fragment currently in changelog.d/
into a new dated section here and deletes the consumed fragments —
.github/workflows/release.yml's release-notes job then extracts that
section verbatim as the GitHub Release body. See towncrier.toml for the
category configuration.
Scope note on v1.0.0-rc1 below: that section was a one-time historical
backfill — generated with git-cliff from git log
v0.1.0..main (47 commits) and hand-reorganized into the thematic sections
below — covering everything that shipped before the newsfragment process
above existed. Since it predates any real changelog.d/ fragments, it was
hand-labeled with this release's version rather than produced by towncrier
build (there was nothing for that command to consume yet). git-cliff and
cliff.toml are retired and not part of the ongoing process; every future
entry in this file comes from changelog.d/ fragments via towncrier
build as described above.
v1.7.0 - 2026-11-09¶
Features¶
- Added GET /api/v1/queries/{queryId}/samples for per-query sample history, enabled CORS on every dashboard API route, and changed queryId to a JSON string across the API so large pg_stat_statements ids no longer get silently rounded by JavaScript clients. (#162)
- Added
internal/telemetry/promsource, an opt-incorrelation.SampleSourcebacked by PromQL queries against a Prometheus HTTP API, for fleets that already run an OpenTelemetry Collector and would rather not run a secondpg_stat_statementsscraper against the same database. Not yet wired intocmd/manager; that's a follow-up PR. (#163) - Wired
internal/telemetry/promsourceinto the CRD-drivencmd/managerpath:PostgresWatch.spec.sampleSource.type: prometheusnow selects it as an opt-in alternative tointernal/collector.Collector, skippingdsn/dsnSecretRefentirely and rejectingcapturePlans: true(which needs a direct database connection this mode doesn't have) with a clear error instead of ignoring it. (#164) - Completed multi-cluster (fleet) support: a new reconciler proactively health-checks every registered cluster's kubeconfig,
PostgresWatch.spec.cnpgClusterRefvalidates a remote CloudNativePGClusterbefore a watch starts, and both are wired into the Helm chart and a new real two-kind-cluster end-to-end CI workflow. (#174) - Added a
backport release-X.Ypull request label: a maintainer can say exactly which supported tool versions a merged fix should also reach, and the fix is automatically cherry-picked onto eachrelease-X.Ymaintenance branch (opening a draft pull request for manual resolution on a conflict). Every pull request's branch is also now deleted automatically once it closes. (#176)
Fixes¶
- Fixed a self-feedback loop where the collector's own EXPLAIN plan-capture queries were recorded as new pg_stat_statements entries, which could grow into repeated capture failures or a false-positive regression of their own; these ghost entries are now excluded from both sampling and plan capture, with a new metric tracking how often it happens. (#161)
- Fixed PostgreSQL collection for unprivileged roles by skipping statistics rows with hidden query IDs, allowing their own queries to be scraped in single-cluster and remote fleet deployments. (#175)
v1.6.0 - 2026-09-23¶
Features¶
- Added a read-only dashboard HTTP API (
GET /api/v1/regressions,/deploys,/queries,/watches), enabled with the new--dashboard-addrflag on the operator and manager, so the observability dashboard can read regression, deploy, query-sample, and watch data without inventing any metric the correlation engine doesn't already compute. (#159)
v1.5.0 - 2026-08-16¶
Features¶
-
Alerting destinations can now be governed by a configurable
destinationPolicy. Three modes are available: -
permissive(default): unchanged behaviour — any URL that passes the built-in SSRF blocklist is accepted. Fully backward-compatible. allowlist: the webhook URL's host must appear in--alerting-allowed-destinations. Reconciliation fails fast with a clear error when the host is outside the list. Intended for multi-tenant clusters where only a pre-approved set of receivers should be reachable.relay-only: per-watchspec.alerting.urlis ignored entirely; all alerts are forwarded to the fixed relay endpoint in--alerting-destination-policy-relay-url. The manager and operator both fail at startup if the relay URL is missing, surfacing misconfiguration before any watch is reconciled.
The policy is configured via two new flags shared by both the manager and the standalone operator:
--alerting-destination-policy=<permissive|allowlist|relay-only>
--alerting-destination-policy-relay-url=<url> # required for relay-only
PostgresWatch.spec.alerting.destinationPolicy mirrors these values at the per-watch level and is honoured when no manager-level policy is set.
See Alerting: destination policies and Security model for configuration examples and deployment guidance.
(#136)
- Introduces the PostgresRadarCluster CRD so administrators can pre-register remote clusters; PostgresWatch now references them by name via spec.remoteClusterRef, eliminating the confused-deputy risk of direct kubeconfig Secret access. (#138)
- Operators can now cryptographically verify every released artifact. All five container images and the Helm OCI chart are signed with Cosign keyless signing; each image carries an attached SPDX SBOM attestation. A new docs/supply-chain.md page documents the trust model and step-by-step cosign verify commands. A companion scripts/verify-release.sh automates the checks. (#139)
- Completed the final security-roadmap deployment gaps by adding Helm controls for alerting destination policies and ServiceAccount token automount, plus release provenance attestations for published images and the Helm OCI chart. (#141)
v1.4.0 - 2026-08-15¶
Features¶
- Added a sample Kyverno
ClusterPolicyfor restricting who can apply thepg-regression-radar.io/allow-postgreswatch-accessconsent label, and documented it as a recommended hardening control for multi-tenant clusters. (#122)
Fixes¶
- Replaced bare
http.ListenAndServein the collector metrics server with a timeout-aware*http.Server(settingReadHeaderTimeout,ReadTimeout,WriteTimeout, andIdleTimeout) and added graceful shutdown on context cancellation, resolving gosec G114 / CWE-676. Also added the same timeout fields tohttpRunnablein the operator manager, resolving the companion G112 / CWE-400 finding. (#108) CaptureGenericPlanandCapturePlannow accept aNormalizedQueryTextvalue instead of a barestring, enforcing at compile time that only text originating frompg_stat_statementsreaches theEXPLAINstatement. This closes the SQL-injection path (CWE-089 / CodeQLgo/sql-injectionalert #3) reported againstinternal/planner/genericplan.go. (#109)- The Helm chart Secret template now includes the
pg-regression-radar.io/allow-postgreswatch-access: "true"consent label, fixing out-of-the-box breakage where the controller rejected the chart-created Secret on every reconciliation loop. (#110) - Added the
pg-regression-radar.io/allow-postgreswatch-access: "true"consent label to the Helm chart's Secret template so a defaultmode: managerinstall works without manual post-install patching. (#115) - Added an opt-in alerting destination allowlist for operator and manager mode, and clarified that the built-in webhook URL validation is only a best-effort SSRF mitigation that should be paired with stricter network or admission controls in multi-tenant clusters. (#119)
- BuildNotifier now treats an empty webhook URL as alerting disabled, returning a no-op notifier and rejecting empty URLs during direct webhook validation. (#125)
v1.3.1 - 2026-08-15¶
Fixes¶
- PostgresWatch now requires Secrets referenced by dsnSecretRef or remoteClusterSecretRef to carry an explicit consent label before the manager will read them, closing a confused-deputy path. Remote kubeconfigs using exec-based or auth-provider credential plugins, or setting proxy-url, are now rejected outright instead of being trusted. (#94)
- Alert webhook URLs are now validated against SSRF-prone destinations, deploy-event HTTP servers enforce request-size and timeout limits, the standalone ingester can gate /events behind the same webhook secret, the Helm chart runs pods with hardened non-root security defaults, and the Kubernetes-native watcher no longer drops events when multiple DeploySources target the same workload. (#96)
v1.3.0 - 2026-08-14¶
Features¶
- Added Prometheus counters for detected regressions and webhook notification successes or failures, so operators can alert on broken delivery paths and chart alerting reliability from
/metrics. (#87)
Fixes¶
- Webhook notifications now drain response bodies before closing them so Go can reuse keep-alive HTTP connections between alerts. (#89)
- Malformed PostgreSQL DSNs that look like redacted or broken connection URIs are now rejected at operator startup with a clear parse error instead of silently falling back to localhost until scrape time. (#90)
- Collector scrapes now cap
pg_stat_statementsquery text at the configured--max-query-text-lenbudget instead of always asking PostgreSQL for up to 500 characters per row. (#91) - Manager startup failures that prevent controller informers from reaching the Kubernetes API now best-effort mark existing PostgresWatch and DeploySource resources with a non-empty degraded status instead of leaving them blank. (#92)
- Periodic regressions now carry the configured namespace, so alerts, queries, and Kubernetes resources stay scoped to the same namespace as the PostgresWatch or operator configuration. (#93)
v1.2.0 - 2026-08-14¶
Features¶
- Alerting now supports more than Slack:
--alert-format/spec.alerting.formatselects betweenslack(default, unchanged),teams(Microsoft Teams Incoming Webhook),pagerduty(Events API v2, opens/updates an incident), orcustom(a user-supplied Gotext/template, for any destination that isn't one of the built-in three).slackWebhookUrl/--slack-urlkeep working exactly as before. See Alerting for the full field/flag reference and the custom template's available fields. (#71) - Regression detection no longer requires a tracked deploy.
--periodic-detection/spec.periodicDetection.enabledruns the same E-divisive/Welch's-t-test analysis on a rolling schedule, per query, independent of anyDeployEvent— catching regressions with no deploy behind them (autovacuum lag, index bloat, stale planner stats, organic growth) that were previously invisible to this tool. A re-arm state machine suppresses repeat alerts for an ongoing episode until the query recovers. Deploy-triggered detection is unaffected either way; the two paths run side by side. See Periodic Detection for configuration and the false-positive caveat, and ADR-0001 for the design rationale. (#73)
v1.1.0 - 2026-08-13¶
Features¶
- Make deploy-event retry analysis observable: the operator and manager now log when a deploy event is registered for retry and when its analysis window elapses, and expose a
pg_regression_radar_pending_deploy_eventsgauge, so an operator can tell "still waiting for data" apart from a silent failure and confirm memory stays bounded over long uptimes. (#65) - Add a native Kubernetes watch deploy source: setting
sourceType: kuberneteson aDeploySourceCR watches a Deployment or StatefulSet directly and emits a deploy event once its rollout completes, so clusters with no ArgoCD, Argo Rollouts, or Flux installed can still be correlated against — no webhook required. (#66) - Add opt-in Argo Rollouts auto-abort: setting
autoAbort.enabledon aPostgresWatchnow aborts the Argo Rollouts canary behind a sufficiently confident detected regression automatically, instead of only alerting and waiting for a human to act, with its own confidence threshold and RBAC gate, and the outcome recorded on the resultingPerformanceRegression. (#67)
v1.0.2 - 2026-08-13¶
Fixes¶
- Fix regression detection silently missing real regressions: both
operatorandmanagerused to analyse a deploy event exactly once, immediately on arrival, well before enough post-deploy data could exist — now analysis retries every poll tick until the deploy's analysis window closes, deduplicating notifications so a detected regression is still only ever reported once. (#64)
v1.0.1 - 2026-08-12¶
Fixes¶
- Add a separate
docker/login-actionstep to the release workflow's Helm chart job, socosigncan find registry credentials it needs to sign the chart —helm registry loginalone writes to a config filecosignnever reads, which made the chart-signing step fail with an authentication error on every release. (#60)
v1.0.0 - 2026-08-12¶
Features¶
- Add
spec.capturePlanstoPostgresWatch, so the CRD-drivenmanagermode can attach a plan-diff summary to a detectedPerformanceRegression'sstatus.planDiffSummary, matching the standaloneoperatorCLI's existing--capture-plansbehavior. (#51) - Add
spec.remoteNamespacetoPostgresWatch, letting a fleet's remote-cluster DSN Secret live in a differently-named namespace than the hub-sidePostgresWatch, instead of requiring matching namespace names on both sides. (#58) - Fleet mode now evicts a remote cluster's cached client immediately after it fails a real request, so a transient network blip or a genuinely rotated kubeconfig no longer keeps a broken connection in circulation until a future reconcile happens to notice. (#59)
Fixes¶
- Evict entries from the manager's remote-cluster client cache after an hour of disuse, so a rotated-away kubeconfig or a deleted fleet member's cached client no longer stays in memory for the life of the manager process. (#52)
v1.0.0-rc1 - 2026-08-12¶
Detection & Correlation¶
- Deduplicate
PerformanceRegressionfindings across aqueryidrotation, which previously produced one duplicate finding per rotatedqueryidfor what was really a single ongoing regression (fix(correlation),4c62488). - Add
EXPLAIN (FORMAT JSON, GENERIC_PLAN)-based plan-diff correlation (PostgreSQL 16+): captures and diffs a query's execution plan around a detected regression, an estimate-based fallback source (feat, PR #31,84dc8e8). - Add
pg_store_plansas the preferred plan-diff capture source over theGENERIC_PLANestimate above, when the extension is installed — it records the real, previously-executed plan with real parameter values instead of the planner's generic estimate (feat(planner),b611f50). - Backfill
cmd/manager's in-memoryCollector/Enginestate from the Postgres persistence backend on restart, so a manager restart no longer silently resets detection state to empty — landed iteratively across two commits plus the merges that reconciled the feature branch withmainmid-flight (feat(operator),14c3574and5007b66; merge commitseaca170,a1b5340).
Multi-cluster (Fleet)¶
- Add
spec.remoteClusterSecretRefsupport:PostgresWatchReconcilercan now resolve aPostgresWatch's DSN Secret from a different Kubernetes cluster than the manager itself, via a kubeconfig Secret in the hub cluster — closing the gap where the only prior option was manually copying the Secret between clusters (feat(controller),bc0ab5f).
Security¶
- Add
X-Webhook-Tokenbearer-token authentication to the ingester's webhook endpoint, closing the gap where any network path to the ingester could post a deploy-event webhook with no authentication at all (fix(ingester),faf94e4).
Performance & Scalability¶
- Replace the O(n³) inner loops of the E-Divisive change-point algorithm
with an O(n²) formulation
(
perf,627d5cb). - Replace
Store.All()-based O(N) poll loops with a cursor-basedStore.Since()across the collector/ingester poll path (perf, PR #27,9a80538). - Cursor-based poll draining and binary-search
EventsInRangein the ingester, replacing a linear scan (perf,6dd4659). - Replace an O(N×M)
latestSampleTimecomputation with an O(1)Collector.LastScrapeTime()(perf,0b27f86). - Halve
Engine.Analyse'sSamplesInRangelock acquisitions (fix(correlation),af9d21e). - Pre-allocate slices in
ingester.Storeand the in-memory stores to reduce GC pressure under load (perf,0226995).
CLI & Developer Experience¶
- Add a unified
pg-regression-radarCLI sogo installsets up all subcommands from a single binary instead of four separatego installtargets (feat(cli),6cac0e9). - Add
--versionand--dry-runflags to all four binaries (feat(cli),d973795).
CI/CD & Quality¶
- Add a real
kind+ CloudNativePG cluster e2e validation forcmd/manager(.github/workflows/e2e-kind.yml): a real cluster, a real CloudNativePG operator andCluster, the Helm chart, and aPostgresWatchreading its DSN from the CloudNativePG-generated Secret (ci(e2e), PR #37,66261fb). - Document and enforce a PostgreSQL 16/17/18 + EDB Postgres Advanced/Extended
Server support matrix in CI, including a matrixed integration-test job
(
docs(support-matrix),ae93e47). - Expand e2e coverage for recently-landed features against a real
PostgreSQL instance — landed iteratively across two commits
(
test(e2e),30b63feand PR #42,2e57051). - Use structurally distinct probe queries in e2e tests so
pg_stat_statementscan't merge them into onequeryid(fix(e2e), PR #29,a6e82a4). - Resolve
golangci-lintfindings and pin a patched Go toolchain version in CI (fix(ci), PR #28,2c50107).
Documentation¶
- Add a structured
docs/site built with MkDocs Material, slim downREADME.mdto a quick-start pointer at it (docs,4a22bbc), and follow-on styling/nav fixes: true black/white palette with backdrop blur (c673b27), transparent tabs bar (fffe907), thinner active-tab underline (c02fffe), an Installation page plus active-tab contrast fix (068eff5), collapsing overflowing nav tabs into a real hover mega-menu across four follow-up fixes (a4f86d5,fd60fe9,a5f1d79,c9454b1). - Move
.github/BRANCH_PROTECTION.md's content into the docs site and delete the standalone file (docs(ci),e474939; housekeeping merge/delete6175a0f,05d4f40). - Document least-privilege PostgreSQL role guidance for
--dsn(docs,2dbc330). - Document the
PostgresWatch/DeploySource/PerformanceRegressionv1alpha1CRD compatibility policy (docs(api),5a4a744; review-nit follow-updccabcd). - Fix the Flux Provider authentication example in the webhooks doc to use
the
headersfield, matching the new webhook-token auth above (docs(webhooks),1196f20). - Fix a typo (
fix(doc),b749609— originally committed asfiz(doc), a typo in the commit type itself).
Dependencies & Chores¶
- Weekly Dependabot bumps to five
github-actionsdependencies:actions/setup-python5→7 (5304ae3),actions/upload-pages-artifact3→5 (73c5383),actions/deploy-pages4→5 (bf75348),actions/upload-artifact4→7 (e52fab1),actions/download-artifact4→8 (9ea1bab). - Fix
dependabot.ymlformatting (fix,edcb2eb; merged back in viac1e8543).
[0.1.0]¶
MVP: Collector + Ingester + Correlation Engine + Slack alerting + Helm
chart, plus Argo Rollouts/Flux deploy-source support and the real
PostgresWatch/DeploySource/PerformanceRegression CRD-driven
controller-runtime mode — see docs/roadmap.md
for what's shipped since. Predates this file; not reconstructed here since
git-cliff's useful range starts at this tag.