v2.12.14

nats-io/nats-serverv2.12.14Jul 30, 2026by github-actions[bot]

AI Summary

A maintenance release for the 2.12 branch that updates the Go runtime to 1.25.12. It introduces JetStream performance optimizations and security patches, including fixes for authentication bypasses and JWT validation crashes.

Key Highlights

  • Raft transport layer decoupling for better code structure and testing.
  • JetStream performance improvements: Faster AVL sequence sets and reduced memory usage for subject tracking.
  • Security fixes: Resolved authentication bypass with TLS `verify_and_map` and JWT validation crash on whitespace-only permissions.
  • Monitoring updates: `healthz` endpoint now correctly handles expired JWT accounts.

New Features

  • JetStream disk concurrency semaphore configuration option `max_concurrent_io`

Full Release Notes

## Changelog

### Go Version

- 1.25.12 (#8435)

### Dependencies

- github.com/klauspost/compress v1.19.0 (#8385)
- golang.org/x/crypto v0.54.0 (#8385)
- golang.org/x/sys v0.47.0 (#8385)
- github.com/antithesishq/antithesis-sdk-go v0.7.2-default-no-op (#8385)

### Improved

JetStream

- The Raft transport layer has been decoupled, improves testing but does not change server behaviour (#8181)
- The disk concurrency semaphore has been increased to 4096 slots, up from the previous CPU-scaled count (#8336)
- The disk concurrency semaphore can now be configured with the `max_concurrent_io` option in the `jetstream` config block (#8336)
- Filestore underlying block cache buffers are now recycled to the pool when the weak reference is collected by the GC, which should smooth out memory usage with some usage patterns (#8395)
- Inserts, iterations and deletes in AVL sequence sets are now faster in many cases, which speeds up the tracking of interior deletes (#8406)
- Stream snapshots now attempt to determine the correct encode buffer size up front, avoiding many unnecessary allocations on streams with large numbers of interior deletes (#8405)
- Reduced memory usage of the structure that is used to track subjects within a stream (#8412)

### Fixed

General

- `allow_non_tls` will no longer log an incorrect message at startup claiming that TLS is required (#8420)
- Combining `no_auth_user` with auth callouts will no longer skip authentication checks when no `CONNECT` message is sent
- JWT validation no longer crashes the server with whitespace-only permissions
- Several paths that enforce the permissions of queue subscriptions no longer treat the whole permission as a subject literal
- Several JetStream and MQTT endpoints now correctly guard against null values in JSON
- Fixed an authentication bypass with TLS `verify_and_map` authenticating users with blank passwords

Monitoring

- The `healthz` endpoint will now skip and no longer report on expired JWT accounts (#8379, thanks to @ByapakSigdel)
- The `varz` endpoint will now correctly report JetStream limits after they were changed via a config reload (#8394)

JetStream

- Malformed cluster replicated acks or delivered updates are now correctly rejected by the decoder (#8284, thanks to @uwezkhan)
- Malformed cluster replicated skip or reset updates are now correctly rejected by the decoder (#8345, thanks to @uwezkhan)
- Empty cluster replicated entries are now correctly ignored (#8347, thanks to @uwezkhan)
- Decoded AVL sequence set node counts are now validated correctly on 32-bit systems (#8355, thanks to @uwezkhan)
- Oversized cluster replicated messages are now validated correctly on 32-bit systems (#8357, thanks to @uwezkhan)
- Raft elections now correctly ignore votes from removed peers (#8353)
- Filestore encryption key files are now synced to disk more aggressively (#8366)
- Raft now handles the append entry iterator returning no more entries correctly (#8372)
- Fixed a bug in the filestore which could prevent some filestore block cache references from being weakened correctly, which could result in unexpected memory usage and GC pressure (#8380)
- Attempting to update the consumer storage type now correctly returns an error (#8382)
- Stream publish checks will now correctly reject messages that exceed the maximum store size before proposal (#8389)
- Creating a clustered consumer immediately after creating a clustered stream should no longer respond with a `stream not found` error (#8410)
- Replicated streams that were recreated while a node was down are no longer treated as an update by a returning node processing a snapshot, avoiding stale Raft groups from continuing to run and unexpected behaviour with consumers (#8413)
- Stream snapshot endpoints now more strictly check the reply subject for validity

MQTT

- Packet identifiers for QoS1 and QoS2 are now issued by a monotonic counter, avoiding accidental ID reuse (#8358, thanks to @nberlee)
- Pending QoS1 and QoS2 deliveries should no longer leak when the subscription is downgraded to QoS0 (#8359, thanks to @nberlee)
- QoS2 messages released on a resumed sessions should no longer lose their QoS or packet ID (#8414)

### Complete Changes
 
https://github.com/nats-io/nats-server/compare/v2.12.12...v2.12.14