Kuzzle 2.60 #
Released: 2.60.0 on 2026-10-28 and 2.60.1 on 2026-10-29.
The GitHub release notes list every commit. This page tells you what changes for you. For what to check before you upgrade, read Upgrading from 2.59.
2.60.1 #
Released: 2026-10-29. GitHub release.
A security fix. server:getConfig and admin:dump returned the configuration's secrets in clear: Redis and Elasticsearch passwords, TLS keys, plugin secrets, and in a dump also the environment variables, the security section and the vault key. They now show "***" in their place.
- What you need to do: nothing, unless a tool of yours reads a secret back through
server:getConfigor a dump (it now reads"***"), or you set a numeric-looking password through an environment variable (see the upgrade notes). - Fixed: a secret set through an environment variable keeps its exact text. A password such as
0123used to become the number123, and the connection failed. - New in the docs: how to connect Kuzzle to a Redis protected by a password, an ACL user, TLS or Sentinel: Securing Redis.
2.60.0 #
In short #
2.60 makes a cluster start and stop reliably. Most fixes target what happens when several nodes start at the same time, or when a node stops or crashes while the cluster starts. It also moves Kuzzle to Node.js 22.12 or later.
What you need to do #
- Use Node.js 22.12 or later. Node.js 20 is no longer supported. Check the base image of your
Dockerfile. - Check your log pipeline if it reads error fields. Errors are now logged under an
errfield. - Check your token settings if you still use
security.jwt. Itsalgorithm,expiresInandmaxTTLwere silently ignored since 2.16.8; they now apply. Move them tosecurity.authToken.
Nothing else changes in the API or the TypeScript types.
What's new #
withLockeverywhere. Every cluster-wide lock Kuzzle takes now lasts as long as the work it protects. Some used to expire after 5 seconds, which let another node start the same work in parallel.MutexAcquireError.withLocknow rejects with this error when it cannot get the lock, so you can tell a busy lock apart.- Delete your own API key by key or fingerprint over HTTP, with the new
DELETE /api-keysroute. - New type exports.
StartOptions,InstallationConfig,ImportConfigandSupportConfigname the options of the startup and of the plugin imports.
What's fixed #
Starting a cluster #
- Nodes wait for each other's startup imports (mappings, fixtures, permissions) instead of going on while they still run. A changed import is imported once, not at every start.
- A node that cannot join the cluster (another node has its IP address, the cluster cannot be reached, the join takes too long) now fails its startup with a clear
cluster.join.*error. It used to keep starting in the background. - A node restarted at once on the same IP address no longer mistakes its own previous registration for another node, and no longer loops on restarts.
- A node that joins late now sees the indexes and collections that the other nodes created before it joined, including during its plugins'
init(). - When two nodes create the same collection at the same time, both sets of mappings are applied. One of them used to be dropped, which could make a plugin fail its startup on a strict collection.
- A node stopped while it starts stops starting: it no longer takes the installations lock and makes the other nodes wait 30 seconds.
- Several cases where a cluster could stay stuck in a restart loop after a node stopped mid-startup are fixed: an internal setup left half done, a collection whose shards were not ready yet, cluster messages received too early.
Stopping a node #
- A failed startup now always stops the node and exits with code
1, so a restart policy restarts it. In production, the node used to stay alive, half started. - A shutdown caused by a failure is bounded to 30 seconds. A requested stop (
SIGTERM,admin:shutdown) still waits for the requests in progress, and exits with code0. - A node deletes its cluster registration and releases its locks when it stops, so the other nodes no longer wait for them to expire.
- A shutdown that fails now logs why and exits with code
1. After a requested stop, the node used to stay alive with its network layer already closed. - A stop logs
Initiating shutdown...once, not twice.
Other fixes #
- An empty Kuzzle index no longer keeps a useless extra Elasticsearch index once it has a real collection.
- A collection configured with
number_of_replicasbut nonumber_of_shardsno longer fails the startup in development mode. - An HTTP request with an empty body and large headers is now answered, instead of hanging until a proxy returns a
502. - An Elasticsearch error without a body now has a useful message, such as
Elasticsearch responded 503 (no body). security.authToken.gracePeriodis no longer ignored, and Kuzzle warns at startup when the deprecatedsecurity.jwtsection is used.- Installed with npm, Kuzzle finds your project's secrets file again (
config/secrets.enc.json). admin:loadFixturesanswers{ acknowledge: true }again, and a secondadmin:shutdownis refused while the node already shuts down.- A few cluster and Elasticsearch 7 errors that were left unhandled are now logged with a clear message. In development mode, they no longer crash the node.
- Four HTTP routes that pointed to long-removed actions are gone.
For plugin and application developers #
- A plugin request with
triggerEventsthat failed was run a second time, without events. It now fails once. - A pipe whose handler throws now rejects with that error, instead of never answering.
http:<verb>events always use the lowercase verb (http:get), even for a route declared asGET.request.getDate()with a custom format andrequest.getTimestamp()with an integer string now work. A date string given togetTimestamp()is now rejected, instead of being cut to its first number.Mutex.wait()works without argument,Logger.child()returns a fullLogger, andLocation/Last-Modifiedresponse headers are replaced instead of concatenated.InternalLoggerand theKoncordeclass exported by Kuzzle are deprecated (see Towards Kuzzle 3).
Under the hood #
Several dependencies moved to a new major version (ioredis 6, aedes 1, protobufjs 8, cookie 2). Each one is configured to behave exactly as before. Production installs carry 23 fewer packages.