From 8a27f84ed370e3a0a8b87f500bc35f20e94e2bc3 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Thu, 9 Jul 2026 11:38:05 +0300 Subject: [PATCH 1/2] docs(cluster): document that TLS must be enabled via defaults TLS settings from a rediss:// URL or a root node only apply to the topology-discovery connection; connections to discovered cluster nodes are built from the defaults option. Without defaults.socket.tls, node connections are attempted in plaintext and hang against clusters that enforce in-transit encryption. Co-Authored-By: Claude Fable 5 --- docs/clustering.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs/clustering.md b/docs/clustering.md index c43d63407d0..71889d59e1d 100644 --- a/docs/clustering.md +++ b/docs/clustering.md @@ -75,6 +75,25 @@ createCluster({ }); ``` +## TLS + +Like the password, TLS options specified in the URL (`rediss://`) or in a root node only affect the connection that is used to discover the cluster topology — they are **not** inherited by the connections made to the discovered cluster nodes. Those connections are created from the `defaults` option. + +If your cluster requires TLS (e.g. AWS ElastiCache with in-transit encryption enabled), enable it via `defaults.socket`, otherwise the node connections will be attempted in plaintext and can hang without an error: + +```javascript +createCluster({ + rootNodes: [{ + url: 'rediss://external-host.io:30001' + }], + defaults: { + socket: { + tls: true + } + } +}); +``` + ## Node Address Map A mapping between the addresses in the cluster (see `CLUSTER SHARDS`) and the addresses the client should connect to. From 3284faa220a7be18fb1534553ea4b130cc8bcd17 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Thu, 9 Jul 2026 11:58:47 +0300 Subject: [PATCH 2/2] docs(cluster): document rootNodes vs defaults scope generally + JSDoc Generalize the clustering guide: rootNodes configuration is only used for topology discovery and is not inherited by node connections, which are built from defaults. Password and TLS become examples of that rule. Also state the rule in the JSDoc of RedisCluster.create, createCluster and the rootNodes/defaults options. Co-Authored-By: Claude Fable 5 --- docs/clustering.md | 16 ++++++++++------ packages/client/lib/cluster/index.ts | 25 +++++++++++++++++++++++++ packages/redis/index.ts | 7 +++++++ 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/docs/clustering.md b/docs/clustering.md index 71889d59e1d..e4a83e58057 100644 --- a/docs/clustering.md +++ b/docs/clustering.md @@ -57,9 +57,15 @@ await cluster.sendCommand("key", false, ["SET", "key", "value", "NX"]); // 'OK' await cluster.sendCommand("key", true, ["HGETALL", "key"]); // ['key1', 'field1', 'key2', 'field2'] ``` -## Auth with password and username +## Root nodes vs. defaults -Specifying the password in the URL or a root node will only affect the connection to that specific node. In case you want to set the password for all the connections being created from a cluster instance, use the `defaults` option. +The configuration in `rootNodes` is only used for the connections that discover the cluster topology. It is **not** inherited by the connections the cluster then makes to the discovered nodes — those are created from the `defaults` option (plus the discovered host and port). + +This means that any setting that should apply to every connection in the cluster — credentials, TLS, timeouts, etc. — must be specified via `defaults`, even if it is already present in a root node URL or configuration. + +### Auth with password and username + +Specifying the password in the URL or a root node will only affect the connection used for topology discovery. In case you want to set the password for all the connections being created from a cluster instance, use the `defaults` option. ```javascript createCluster({ @@ -75,11 +81,9 @@ createCluster({ }); ``` -## TLS - -Like the password, TLS options specified in the URL (`rediss://`) or in a root node only affect the connection that is used to discover the cluster topology — they are **not** inherited by the connections made to the discovered cluster nodes. Those connections are created from the `defaults` option. +### TLS -If your cluster requires TLS (e.g. AWS ElastiCache with in-transit encryption enabled), enable it via `defaults.socket`, otherwise the node connections will be attempted in plaintext and can hang without an error: +Likewise, TLS options specified in the URL (`rediss://`) or in a root node only affect the topology discovery connection. If your cluster requires TLS (e.g. AWS ElastiCache with in-transit encryption enabled), enable it via `defaults.socket`, otherwise the connections to the discovered nodes will be attempted in plaintext and can hang without an error: ```javascript createCluster({ diff --git a/packages/client/lib/cluster/index.ts b/packages/client/lib/cluster/index.ts index 0203f14f209..43e82153847 100644 --- a/packages/client/lib/cluster/index.ts +++ b/packages/client/lib/cluster/index.ts @@ -69,11 +69,18 @@ export interface RedisClusterOptions< /** * Should contain details for some of the cluster nodes that the client will use to discover * the "cluster topology". We recommend including details for at least 3 nodes here. + * + * Note: this configuration is only used for the connections that discover the topology — it is + * not inherited by the connections made to the discovered nodes. Settings that should apply to + * every connection (e.g. credentials, TLS) must be specified via `defaults`. */ rootNodes: Array; /** * Default values used for every client in the cluster. Use this to specify global values, * for example: ACL credentials, timeouts, TLS configuration etc. + * + * The connections to the discovered cluster nodes are created from these defaults (plus the + * discovered host and port) — they do not inherit any other settings from `rootNodes`. */ defaults?: Partial; /** @@ -275,6 +282,24 @@ export default class RedisCluster< }; } + /** + * Creates a new Redis Cluster client. + * + * Note: `rootNodes` is only used to discover the cluster topology; its configuration is not + * inherited by the connections made to the discovered nodes. Any setting that should apply to + * every connection in the cluster (e.g. credentials, TLS) must be specified via `defaults`: + * + * @example + * ```javascript + * createCluster({ + * rootNodes: [{ url: 'rediss://external-host.io:30001' }], + * defaults: { + * password: 'password', + * socket: { tls: true } + * } + * }); + * ``` + */ static create< M extends RedisModules = {}, F extends RedisFunctions = {}, diff --git a/packages/redis/index.ts b/packages/redis/index.ts index b8e78396070..e09af5a694f 100644 --- a/packages/redis/index.ts +++ b/packages/redis/index.ts @@ -97,6 +97,13 @@ export type RedisClusterType< TYPE_MAPPING extends TypeMapping = {} > = genericRedisClusterType; +/** + * Creates a new Redis Cluster client. + * + * Note: `rootNodes` is only used to discover the cluster topology; its configuration is not + * inherited by the connections made to the discovered nodes. Any setting that should apply to + * every connection in the cluster (e.g. credentials, TLS) must be specified via `defaults`. + */ export function createCluster< M extends RedisModules = {}, F extends RedisFunctions = {},