namespace ZB.MOM.WW.OtOpcUa.Cluster;
public sealed class AkkaClusterOptions
{
public const string SectionName = "Cluster";
/// Gets or sets the Akka system name.
public string SystemName { get; set; } = "otopcua";
/// Gets or sets the hostname to bind to (default 0.0.0.0).
public string Hostname { get; set; } = "0.0.0.0";
/// Gets or sets the port to listen on (default 4053).
public int Port { get; set; } = 4053;
///
/// Hostname advertised in cluster gossip. Must be reachable by other nodes.
/// In docker-compose this is the container DNS name; in bare metal it's the
/// host's stable LAN address.
///
public string PublicHostname { get; set; } = "127.0.0.1";
///
/// Gets or sets the seed nodes for cluster bootstrapping.
///
///
///
/// ORDER IS LOAD-BEARING (decision 2026-07-22): a node that is one of its own seeds
/// must list ITSELF first. Akka runs FirstSeedNodeProcess — the only bootstrap
/// path that can form a NEW cluster when no peer answers InitJoin — exclusively
/// when seed-nodes[0] is this node's own address; any other node runs
/// JoinSeedNodeProcess, which retries InitJoin forever and can never form a
/// cluster. So listing both peers does NOT mean either can cold-start alone: a node that
/// lists its partner first never comes Up while that partner is down. Self-first ordering
/// closes that gap inside Akka's own handshake — unlike the retired
/// Cluster:SelfFormAfter watchdog, which sat outside it and could not tell "no
/// seed answered" from "a seed answered and the join is in flight".
///
///
/// The rule is conditional: it binds only when this node's own address appears in
/// this list. A node seeded exclusively by someone else — today's docker-dev site nodes,
/// which list only central-1 — is legitimately not a seed and is exempt. Enforced
/// at boot by ; identity is
/// + (what Akka puts in
/// SelfAddress), never the bind address.
///
///
public string[] SeedNodes { get; set; } = Array.Empty();
///
/// Cluster roles for this node. When empty the role list comes from
/// OTOPCUA_ROLES via . Allowed values:
/// admin, driver, dev.
///
public string[] Roles { get; set; } = Array.Empty();
///
/// How the cluster decides to down a node it can no longer reach. One of auto-down
/// (default) or keep-oldest; any other value fails the host at startup.
///
///
///
/// auto-down — availability. The leader among the reachable members
/// downs the unreachable peer after
/// . A hard crash of either
/// node — including the oldest — fails over to the survivor with no operator action.
/// The trade is that a genuine network partition (both nodes alive, link cut) leaves
/// both sides running active until an operator restarts one.
///
///
/// keep-oldest — partition-safety. The SBR resolver sacrifices the younger
/// side of a split, so a partition can never run dual-active. The cost is severe for a
/// two-node pair: it cannot survive a crash of the oldest node at all. Akka.NET's
/// KeepOldest.OldestDecision only lets down-if-alone rescue a side holding
/// >= 2 members, so the 1-vs-1 survivor downs itself and shuts down — the
/// redundancy pair turns a single-node crash into a total outage. Choose this only for
/// clusters of three or more nodes, or where dual-active is genuinely worse than an
/// outage.
///
///
public string SplitBrainResolverStrategy { get; set; } = "auto-down";
}