Subject: [pgjdbc/pgjdbc] PR #4079: docs: spell out the proactive-security window in SECURITY.md

From: "vlsi (@vlsi)" <[email-masked]>
Date: Tue, 19 May 2026 13:48:57 +0000

Names the policy ("five years past the `.0` of the next minor"), adds a row for older-but-still-in-window 42.x lines, and links to the Compatibility page for the resolved per-line dates. Also cleans up a few typos in the prose.

---

Here's a quote from PostgreSQL versioning page:

> https://www.postgresql.org/support/versioning/
> The PostgreSQL Global Development Group supports a major version for 5 years after its initial release. After this, a final minor version will be released and the software will then be unsupported (end-of-life).

This hints "5 years" might be reasonable for pgjdbc as well.

---

## Why a proactive-security window?
This PR formalises a policy the project has effectively been operating under for a while: older release lines get proactive security backports, not "on request if we feel like it." The compatibility page surfaces the resolved per-line date as Security until YYYY-MM.

A proactive window matters because most pgJDBC users cannot respond to a CVE by jumping to the latest minor:

1. **Enterprises need to ship a security fix without taking a minor bump**. Regulated change-management makes "patch your CVE and upgrade two minors in the same window" a non-starter — users need to apply the security patch now on the line they're running, then plan the upgrade on their own schedule.
2. **Users stuck on older stacks** (legacy JVMs, old PostgreSQL servers, frozen application images) cannot freely move to a newer minor. A "just upgrade" answer doesn't reach them; they still need CVE coverage.
3. **The upgrade itself isn't always safe**. A newer minor occasionally carries a regression or a backward-incompatible behaviour change the user cannot work around inside a CVE deadline. Forcing the upgrade as the only path to a fix trades one fire for another.
4. **Downstream dependency policies block minor bumps**. Spring Boot, for instance, will not change the minor version of a transitive dependency inside a patch release. If they pin a pgJDBC line that has no security patch on its own minor, their downstream users cannot get the fix without violating Spring Boot's policy. A backport on the pinned line is the only path that works for the whole chain.

The intent is the same line the existing prose draws — to separate "we are eager fixing bugs" from "we can roll security releases" — but stated as a concrete, time-bounded commitment users and downstream packagers can plan against.

---

For the reference, here's how the compatibility page would look like under the updated conditions ("five years past the `.0` of the next minor"):
<img width="913" height="633" alt="updated compatibility page" src="https://github.com/user-attachments/assets/355d77c5-2964-45d8-b1c5-ed89ab44a7ff" />

---

From: "vlsi (@vlsi)" <[email-masked]>
Date: Tue, 19 May 2026 13:50:00 +0000

(on SECURITY.md)

We can arbitrary exclude 42.2.x from proactive-security window. I have no hard feelings about it.

---

From: "davecramer (@davecramer)" <[email-masked]>
Date: Wed, 20 May 2026 10:57:58 +0000

(on SECURITY.md)

Yeah, I'm not interested in supporting them

---

From: "davecramer (@davecramer)" <[email-masked]>
Date: Wed, 20 May 2026 10:58:44 +0000

So given that some of those minor versions were pushing to the old sonatype. We are going to have to fix the build systems before we can release

---

From: "sehrope (@sehrope)" <[email-masked]>
Date: Wed, 20 May 2026 11:35:30 +0000

If I'm reading this correctly, you're suggesting we publish 10 versions of the driver, continue to support all of them, and every time we release a new version, we commit to supporting it as a release line for 5 years?

That's way too many versions for something that is inherently supposed to be backwards compatible. We go out of our way to avoid breaking changes. And if there are any, unless there is a security reason why we cannot revert them, we consider breaking changes to be bugs that need to be fixed.

Users really should be using the latest version of the driver. For the 99.9999999% of people that are on JDK8+, there is no technical reason they cannot. It's purely bureaucratic.

If we are going to have multiple concurrent supported versions, perhaps an "LTS" model is more appropriate (see https://nodejs.org/en/about/previous-releases). We could periodically cut a new release that we would commit to supporting for a known timeframe. Only CVEs would be backpatched and user's could choose to use those branches knowing they will never get anything added or improved.

We could have a fixed number of LTS release lines active at any given time (I like just one...). When one ends, we start a new one using the latest non-LTS release.  That could be a fixed schedule or whenever we as a group decide to do so. But it'd be well defined with specific off ramp.

And regarding the JDK 6/7 versions, I think we just scrap them entirely.

---

From: "vlsi (@vlsi)" <[email-masked]>
Date: Wed, 20 May 2026 12:35:07 +0000

Thanks for the careful read — let me untangle one thing up front, because I think the central claim in this PR reads stronger than it actually is.

**The unit of support is a release *line*, not an individual release.** "Within a line we keep N tags alive" is exactly the thing we *don't* do — only the latest tag on each line gets backports. When a CVE lands and N lines are still in the proactive window, we ship one patch *per line*, on that line's own minor (e.g. CVE-2024-1597 → `42.7.2`, `42.6.1`, `42.5.5`, `42.4.4`, `42.3.9`, `42.2.28`, `42.2.28.jre7`). That's not new behaviour the PR is asking for — it's the de-facto practice the project has been running for years; the PR just puts a number on "for how long".

Visually, "many concurrent supported lines" looks like this rather than the "10 versions in flight" framing:

```mermaid
%%{init: {'gantt': {'displayMode': 'compact'}}}%%
gantt
    title pgJDBC release lines under "5 years past successor's .0"
    dateFormat YYYY-MM-DD
    axisFormat %Y

    section 42.2.x
    42.2.x development  :done,   2018-01-17, 2021-10-18
    Security backports  :crit,   2021-10-18, 2026-10-18

    section 42.3.x
    42.3.x development  :done,   2021-10-18, 2022-06-09
    Security backports  :crit,   2022-06-09, 2027-06-09

    section 42.4.x
    42.4.x development  :done,   2022-06-09, 2022-08-24
    Security backports  :crit,   2022-08-24, 2027-08-24

    section 42.5.x
    42.5.x development  :done,   2022-08-24, 2023-03-17
    Security backports  :crit,   2023-03-17, 2028-03-17

    section 42.6.x
    42.6.x development  :done,   2023-03-17, 2023-11-20
    Security backports  :crit,   2023-11-20, 2028-11-20

    section 42.7.x (current)
    42.7.x development  :active, 2023-11-20, 2026-05-20
```

At any given moment **exactly one line is under active development**; the rest sit in a security-only tail. The matrix's nine rows are six lines (42.7.x carries two segments because of the PostgreSQL 9.1 floor bump, and 42.2.x carries two classifier rows — `.jre6` / `.jre7` — for the artefacts that already exist on Maven Central).

A few specific responses:

1. **"users really should be using the latest version"** — agreed for the development case, and we already say so on the compatibility page. The proactive window is for the cases where that advice doesn't reach: downstream packagers whose own dependency policy forbids minor bumps in a patch release (Spring Boot is the canonical example), enterprise CI/CD that needs a CVE patch on the pinned minor before a planned upgrade, etc. Telling those users "upgrade to the latest minor or wait" is the same answer as "no security fix".

2. **"5 years feels long"** — 5y is the same window [PostgreSQL itself](https://www.postgresql.org/support/versioning/) uses for each major release. A driver whose support is shorter than upstream's would systematically leave users in the gap where their server is still under PG community support but their JDBC client isn't — which contradicts what users reasonably expect from a database driver. Empirically pgJDBC has been operating in that ballpark already: 42.2.x was still receiving CVE patches in 2024, well past `.0 + 3y`.

3. **Node.js-style LTS with a single supported line** — interesting, but it's *more restrictive* than what we currently do, not less. Under "only one in-support line", the CVE-2024-1597 fan-out above would not have shipped: Spring Boot users on 42.6.x would have been told to upgrade, which their own policy disallows. I'm happy to discuss LTS as a separate proposal, but I'd want it as a deliberate tightening, not as a side-effect of this PR. (And if we do tighten, we should answer "what's the plan for Spring Boot users on a pinned older minor when the next CVE drops?" before we ship.)

4. **JDK 6/7** — the `.jre6` / `.jre7` rows in the matrix are descriptive, not prescriptive: they document that `42.2.29.jre7` and `42.2.27.jre6` already exist on Maven Central. Dropping the *artefacts* (i.e. unpublishing or marking them stale) is a separate decision from how the matrix renders, and I'm fine doing that in a follow-up if there's agreement.

---

From: "davecramer (@davecramer)" <[email-masked]>
Date: Thu, 21 May 2026 11:35:20 +0000

So there are some logistical problems, 1 being that the build system has changed over the years. oss.sonatype is no longer available. Will we run into issues trying to release old versions ?

---

From: "vlsi (@vlsi)" <[email-masked]>
Date: Thu, 21 May 2026 11:40:43 +0000

Technically, the all the 42.x branches use Gradle, even 42.2.x
Sure there might be issues. However, let's see if the issues are real first.

For example, Sonatype released a compatibility endpoint: https://central.sonatype.org/publish/publish-portal-ossrh-staging-api/#getting-started-for-maven-api-like-plugins, so even old build systems should be able to publish to Central after reconfiguring the oss.sonatype.org link to the compatibility URL.

---

From: "sehrope (@sehrope)" <[email-masked]>
Date: Thu, 21 May 2026 12:17:43 +0000

If we split the publication step from the artifact creation then this would be significantly more future proof.

Branches should only concern themselves with generating the target artifacts, i.e. the jar and source jar. The checksums, signatures, and publishing should have happen out of band. The only inputs into the publishing step are jars + version numbers and it publishes it to wherever / however we want.

No worrying about back branch publication going stale or Sonatype changes not being reflected. Just build the jar however we were building it at the time and handle publication of the artifacts separately.

And end-to-end build / test / sign / deliver flow is much more likely to break than just a build one to just produce the jars.

This approach also means that porting patches to back branches would only be the patches themselves. We should never have to touch the (at the time) build system.

---

From: "vlsi (@vlsi)" <[email-masked]>
Date: Thu, 21 May 2026 12:49:29 +0000

On the signing isolation point, agreed, that's a real, separable win. Today the GPG signing key ends up materialised into the Gradle build, which means every plugin and every test dependency on the classpath is in a position to read it. Pulling signing into an isolated CI job with a narrow, audited software bill of materials would meaningfully shrink that attack surface. I'd back a refactor on that ground alone.

Where I'd push back is on the claim that decoupling pre-empts the Sonatype-availability concern Dave raised. Whatever pipeline talks to Central still needs the same auth/URL change when OSSRH retires — a decoupled GHA + shell + publication-tool stack inherits that update just as the current Gradle config would. The refactor relocates the maintenance, it doesn't eliminate it. (And patches to back branches usually don't touch the build system anyway; the case where decoupling actually saves work is also the case where the decoupled pipeline itself needs updating.)

---

Stepping back, though. Back to the PR itself.

The policy isn't new: pgJDBC has been shipping multi-line CVE backports for years (CVE-2024-1597 landed on 42.7.2 / 42.6.1 / 42.5.5 / 42.4.4 / 42.3.9 / 42.2.28 / 42.2.28.jre7). What's been missing is an explicit answer to "for how long?". The PR just puts a number on it: 5 years past the next minor's .0, anchored to PostgreSQL's own 5-year support cycle so the driver doesn't lag the server.

Publishing and signing isolation, Sonatype migration, build/publish decoupling is real but separate: how we ship vs for how long. Move it to a follow-up issue? Happy to open one and link back.

Does 5y past next minor's .0 look right? Anything blocking on the formalisation itself?