diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/MeshTransportOptions.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/MeshTransportOptions.cs
index 7fa62de4..33593bdc 100644
--- a/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/MeshTransportOptions.cs
+++ b/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/MeshTransportOptions.cs
@@ -6,9 +6,11 @@ namespace ZB.MOM.WW.OtOpcUa.Cluster;
///
///
///
-/// is a deliberate dark switch: the transports live in the tree
-/// side by side so the docker-dev rig can run the same deployment over each and compare, and
-/// so a bad rollout rolls back with a config change rather than a revert.
+/// selects between a pair-local transport and a cross-mesh
+/// one. is the default and cannot cross an application-Cluster
+/// boundary; is the only mode that can, and
+/// requires it on any node carrying a
+/// cluster-{ClusterId} role.
///
///
/// The DistributedPubSub branch is retained, not scheduled for deletion. An earlier
@@ -16,15 +18,15 @@ namespace ZB.MOM.WW.OtOpcUa.Cluster;
/// deliberately reversed — see docs/Configuration.md § "The compiled transport-mode
/// defaults were deliberately NOT flipped". DPS remains the correct and default transport for
/// a single-mesh topology, which every integration test and every profile that leaves this
-/// key unset still runs. What is being replaced is , by
-/// .
+/// key unset still runs.
///
///
/// Discovery is deliberately asymmetric, matching the sister project. Central
/// discovers nodes from the database — enabled, non-maintenance ClusterNode
/// rows, rebuilt every . Nodes discover central from
-/// this section: static, restart to change. The asymmetry follows the rate of change —
-/// operators add and retire nodes, but central's own address is part of the deployment.
+/// this section (): static, restart to change. The
+/// asymmetry follows the rate of change — operators add and retire nodes, but central's own
+/// address is part of the deployment.
///
///
public sealed class MeshTransportOptions
@@ -32,26 +34,26 @@ public sealed class MeshTransportOptions
/// Configuration section name.
public const string SectionName = "MeshTransport";
- /// Every command stays on DistributedPubSub — the pre-Phase-2 behaviour.
+ ///
+ /// Every command stays on DistributedPubSub — pair-local, and the default. Cannot cross an
+ /// application-Cluster boundary.
+ ///
public const string ModeDps = "Dps";
- /// Commands cross the ClusterClient receptionist boundary.
- public const string ModeClusterClient = "ClusterClient";
-
///
/// Commands cross the mesh boundary as unary gRPC calls on the mesh_transport.v1
/// contract — central dials each node's NodeCommandService, a node posts its
- /// deployment ack to central's CentralCommandService. The replacement for
- /// ; unlike it, the command plane is authenticated
- /// ().
+ /// deployment ack to central's CentralCommandService. The command plane is
+ /// authenticated per application Cluster ().
///
public const string ModeGrpc = "Grpc";
///
- /// (default), or
- /// . Any other value fails the host at startup rather than silently
- /// falling back — a transport that is none of these delivers nothing, and the symptom would
- /// be commands that vanish.
+ /// (default) or . Any other value fails the host
+ /// at startup rather than silently falling back — a transport that is neither delivers
+ /// nothing, and the symptom would be commands that vanish. That includes the retired
+ /// ClusterClient value: a node still carrying it is refused at boot rather than
+ /// quietly demoted to DPS, which on a split-topology node would deliver nothing at all.
///
public string Mode { get; set; } = ModeDps;
diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/MeshTransportOptionsValidator.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/MeshTransportOptionsValidator.cs
index 0bd78471..65223102 100644
--- a/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/MeshTransportOptionsValidator.cs
+++ b/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/MeshTransportOptionsValidator.cs
@@ -40,16 +40,18 @@ public sealed class MeshTransportOptionsValidator : IValidateOptions();
var isDps = string.Equals(options.Mode, MeshTransportOptions.ModeDps, StringComparison.OrdinalIgnoreCase);
- var isClusterClient = string.Equals(
- options.Mode, MeshTransportOptions.ModeClusterClient, StringComparison.OrdinalIgnoreCase);
var isGrpc = string.Equals(options.Mode, MeshTransportOptions.ModeGrpc, StringComparison.OrdinalIgnoreCase);
- if (!isDps && !isClusterClient && !isGrpc)
+ // An unrecognised mode is refused, never defaulted. The retired 'ClusterClient' value lands
+ // here: a node left carrying it fails to start with the two remaining values named, rather
+ // than silently falling back to Dps — which on a split-topology node cannot cross the mesh
+ // boundary and would deliver nothing at all.
+ if (!isDps && !isGrpc)
{
errors.Add(
$"{MeshTransportOptions.SectionName}:{nameof(MeshTransportOptions.Mode)} is "
- + $"'{options.Mode}'. Expected '{MeshTransportOptions.ModeDps}', "
- + $"'{MeshTransportOptions.ModeClusterClient}' or '{MeshTransportOptions.ModeGrpc}'.");
+ + $"'{options.Mode}'. Expected '{MeshTransportOptions.ModeDps}' or "
+ + $"'{MeshTransportOptions.ModeGrpc}'.");
}
if (options.ContactRefreshSeconds <= 0)
@@ -64,6 +66,7 @@ public sealed class MeshTransportOptionsValidator : IValidateOptions