diff --git a/dotnet/src/ZB.MOM.NatsNet.Server/Accounts/Account.cs b/dotnet/src/ZB.MOM.NatsNet.Server/Accounts/Account.cs
index 4a9c55a..ab7eb64 100644
--- a/dotnet/src/ZB.MOM.NatsNet.Server/Accounts/Account.cs
+++ b/dotnet/src/ZB.MOM.NatsNet.Server/Accounts/Account.cs
@@ -19,38 +19,6 @@ using ZB.MOM.NatsNet.Server.Internal.DataStructures;
namespace ZB.MOM.NatsNet.Server;
-// ============================================================================
-// AccountNumConns — remote server connection/leafnode count message
-// Mirrors Go `AccountNumConns` struct used in updateRemoteServer.
-// ============================================================================
-
-///
-/// Carries the number of client connections and leaf nodes that a remote server
-/// has for a given account, along with the remote server's identity.
-/// Mirrors Go AccountNumConns in server/accounts.go.
-///
-internal sealed class AccountNumConns
-{
- /// Remote server identity. Mirrors Go Server ServerInfo.
- public ServerIdentity Server { get; set; } = new();
-
- /// Number of client connections on the remote server. Mirrors Go Conns int.
- public int Conns { get; set; }
-
- /// Number of leaf nodes on the remote server. Mirrors Go LeafNodes int.
- public int LeafNodes { get; set; }
-}
-
-///
-/// Minimal remote server identity stub used by .
-/// Full implementation lives with the server cluster sessions.
-///
-internal sealed class ServerIdentity
-{
- /// Unique server ID. Mirrors Go ID string.
- public string ID { get; set; } = string.Empty;
-}
-
// ============================================================================
// Account — full implementation
// Mirrors Go `Account` struct in server/accounts.go lines 52-119.
@@ -666,8 +634,8 @@ public sealed class Account : INatsAccount
{
_strack ??= new Dictionary();
- _strack.TryGetValue(m.Server.ID, out var prev);
- _strack[m.Server.ID] = new SConns
+ _strack.TryGetValue(m.Server.Id, out var prev);
+ _strack[m.Server.Id] = new SConns
{
Conns = m.Conns,
Leafs = m.LeafNodes,
diff --git a/dotnet/src/ZB.MOM.NatsNet.Server/Config/ReloadOptions.cs b/dotnet/src/ZB.MOM.NatsNet.Server/Config/ReloadOptions.cs
new file mode 100644
index 0000000..32f16b1
--- /dev/null
+++ b/dotnet/src/ZB.MOM.NatsNet.Server/Config/ReloadOptions.cs
@@ -0,0 +1,996 @@
+// Copyright 2017-2026 The NATS Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Adapted from server/reload.go in the NATS server Go source.
+
+namespace ZB.MOM.NatsNet.Server;
+
+// =============================================================================
+// IReloadOption — mirrors Go `option` interface in reload.go
+// =============================================================================
+
+///
+/// Represents a hot-swappable configuration setting that can be applied to a
+/// running server. Mirrors Go option interface in server/reload.go.
+///
+public interface IReloadOption
+{
+ /// Apply this option to the running server.
+ void Apply(NatsServer server);
+
+ /// Returns true if this option requires reloading the logger.
+ bool IsLoggingChange();
+
+ ///
+ /// Returns true if this option requires reloading the cached trace level.
+ /// Clients store trace level separately.
+ ///
+ bool IsTraceLevelChange();
+
+ /// Returns true if this option requires reloading authorization.
+ bool IsAuthChange();
+
+ /// Returns true if this option requires reloading TLS.
+ bool IsTlsChange();
+
+ /// Returns true if this option requires reloading cluster permissions.
+ bool IsClusterPermsChange();
+
+ ///
+ /// Returns true if this option requires special handling for changes in
+ /// cluster pool size or accounts list.
+ ///
+ bool IsClusterPoolSizeOrAccountsChange();
+
+ ///
+ /// Returns true if this option indicates a change in the server's JetStream config.
+ /// Account changes are handled separately in reloadAuthorization.
+ ///
+ bool IsJetStreamChange();
+
+ /// Returns true if this change requires publishing the server's statz.
+ bool IsStatszChange();
+}
+
+// =============================================================================
+// NoopReloadOption — mirrors Go `noopOption` struct in reload.go
+// =============================================================================
+
+///
+/// Base class providing no-op implementations for all
+/// methods. Concrete option types override only the methods relevant to them.
+/// Mirrors Go noopOption struct in server/reload.go.
+///
+public abstract class NoopReloadOption : IReloadOption
+{
+ ///
+ public virtual void Apply(NatsServer server) { }
+
+ ///
+ public virtual bool IsLoggingChange() => false;
+
+ ///
+ public virtual bool IsTraceLevelChange() => false;
+
+ ///
+ public virtual bool IsAuthChange() => false;
+
+ ///
+ public virtual bool IsTlsChange() => false;
+
+ ///
+ public virtual bool IsClusterPermsChange() => false;
+
+ ///
+ public virtual bool IsClusterPoolSizeOrAccountsChange() => false;
+
+ ///
+ public virtual bool IsJetStreamChange() => false;
+
+ ///
+ public virtual bool IsStatszChange() => false;
+}
+
+// =============================================================================
+// Intermediate base classes (mirrors Go loggingOption / traceLevelOption)
+// =============================================================================
+
+///
+/// Base for all logging-related reload options.
+/// Mirrors Go loggingOption struct.
+///
+internal abstract class LoggingReloadOption : NoopReloadOption
+{
+ public override bool IsLoggingChange() => true;
+}
+
+///
+/// Base for all trace-level reload options.
+/// Mirrors Go traceLevelOption struct.
+///
+internal abstract class TraceLevelReloadOption : LoggingReloadOption
+{
+ public override bool IsTraceLevelChange() => true;
+}
+
+///
+/// Base for all authorization-related reload options.
+/// Mirrors Go authOption struct.
+///
+internal abstract class AuthReloadOption : NoopReloadOption
+{
+ public override bool IsAuthChange() => true;
+}
+
+///
+/// Base for TLS reload options.
+/// Mirrors Go tlsOption (as a base, not the concrete type).
+///
+internal abstract class TlsBaseReloadOption : NoopReloadOption
+{
+ public override bool IsTlsChange() => true;
+}
+
+// =============================================================================
+// Logging & Trace option types
+// =============================================================================
+
+///
+/// Reload option for the trace setting.
+/// Mirrors Go traceOption struct in reload.go.
+///
+internal sealed class TraceReloadOption : TraceLevelReloadOption
+{
+ private readonly bool _newValue;
+ public TraceReloadOption(bool newValue) => _newValue = newValue;
+
+ public override void Apply(NatsServer server)
+ => server.Noticef("Reloaded: trace = {0}", _newValue);
+}
+
+///
+/// Reload option for the trace_verbose setting.
+/// Mirrors Go traceVerboseOption struct in reload.go.
+///
+internal sealed class TraceVerboseReloadOption : TraceLevelReloadOption
+{
+ private readonly bool _newValue;
+ public TraceVerboseReloadOption(bool newValue) => _newValue = newValue;
+
+ public override void Apply(NatsServer server)
+ => server.Noticef("Reloaded: trace_verbose = {0}", _newValue);
+}
+
+///
+/// Reload option for the trace_headers setting.
+/// Mirrors Go traceHeadersOption struct in reload.go.
+///
+internal sealed class TraceHeadersReloadOption : TraceLevelReloadOption
+{
+ private readonly bool _newValue;
+ public TraceHeadersReloadOption(bool newValue) => _newValue = newValue;
+
+ public override void Apply(NatsServer server)
+ => server.Noticef("Reloaded: trace_headers = {0}", _newValue);
+}
+
+///
+/// Reload option for the debug setting.
+/// Mirrors Go debugOption struct in reload.go.
+///
+internal sealed class DebugReloadOption : LoggingReloadOption
+{
+ private readonly bool _newValue;
+ public DebugReloadOption(bool newValue) => _newValue = newValue;
+
+ public override void Apply(NatsServer server)
+ {
+ server.Noticef("Reloaded: debug = {0}", _newValue);
+ // TODO: session 13 — call server.ReloadDebugRaftNodes(_newValue)
+ }
+}
+
+///
+/// Reload option for the logtime setting.
+/// Mirrors Go logtimeOption struct in reload.go.
+///
+internal sealed class LogtimeReloadOption : LoggingReloadOption
+{
+ private readonly bool _newValue;
+ public LogtimeReloadOption(bool newValue) => _newValue = newValue;
+
+ public override void Apply(NatsServer server)
+ => server.Noticef("Reloaded: logtime = {0}", _newValue);
+}
+
+///
+/// Reload option for the logtime_utc setting.
+/// Mirrors Go logtimeUTCOption struct in reload.go.
+///
+internal sealed class LogtimeUtcReloadOption : LoggingReloadOption
+{
+ private readonly bool _newValue;
+ public LogtimeUtcReloadOption(bool newValue) => _newValue = newValue;
+
+ public override void Apply(NatsServer server)
+ => server.Noticef("Reloaded: logtime_utc = {0}", _newValue);
+}
+
+///
+/// Reload option for the log_file setting.
+/// Mirrors Go logfileOption struct in reload.go.
+///
+internal sealed class LogFileReloadOption : LoggingReloadOption
+{
+ private readonly string _newValue;
+ public LogFileReloadOption(string newValue) => _newValue = newValue;
+
+ public override void Apply(NatsServer server)
+ => server.Noticef("Reloaded: log_file = {0}", _newValue);
+}
+
+///
+/// Reload option for the syslog setting.
+/// Mirrors Go syslogOption struct in reload.go.
+///
+internal sealed class SyslogReloadOption : LoggingReloadOption
+{
+ private readonly bool _newValue;
+ public SyslogReloadOption(bool newValue) => _newValue = newValue;
+
+ public override void Apply(NatsServer server)
+ => server.Noticef("Reloaded: syslog = {0}", _newValue);
+}
+
+///
+/// Reload option for the remote_syslog setting.
+/// Mirrors Go remoteSyslogOption struct in reload.go.
+///
+internal sealed class RemoteSyslogReloadOption : LoggingReloadOption
+{
+ private readonly string _newValue;
+ public RemoteSyslogReloadOption(string newValue) => _newValue = newValue;
+
+ public override void Apply(NatsServer server)
+ => server.Noticef("Reloaded: remote_syslog = {0}", _newValue);
+}
+
+// =============================================================================
+// TLS option types
+// =============================================================================
+
+///
+/// Reload option for the tls setting.
+/// Mirrors Go tlsOption struct in reload.go.
+/// The TLS config is stored as object? because the full
+/// TlsConfig type is not yet ported.
+/// TODO: session 13 — replace object? with the ported TlsConfig type.
+///
+internal sealed class TlsReloadOption : NoopReloadOption
+{
+ // TODO: session 13 — replace object? with ported TlsConfig type
+ private readonly object? _newValue;
+ public TlsReloadOption(object? newValue) => _newValue = newValue;
+
+ public override bool IsTlsChange() => true;
+
+ public override void Apply(NatsServer server)
+ {
+ var message = _newValue is null ? "disabled" : "enabled";
+ server.Noticef("Reloaded: tls = {0}", message);
+ // TODO: session 13 — update server.Info.TLSRequired / TLSVerify
+ }
+}
+
+///
+/// Reload option for the TLS timeout setting.
+/// Mirrors Go tlsTimeoutOption struct in reload.go.
+///
+internal sealed class TlsTimeoutReloadOption : NoopReloadOption
+{
+ private readonly double _newValue;
+ public TlsTimeoutReloadOption(double newValue) => _newValue = newValue;
+
+ public override void Apply(NatsServer server)
+ => server.Noticef("Reloaded: tls timeout = {0}", _newValue);
+}
+
+///
+/// Reload option for the TLS pinned_certs setting.
+/// Mirrors Go tlsPinnedCertOption struct in reload.go.
+/// The pinned cert set is stored as object? pending the port
+/// of the PinnedCertSet type.
+/// TODO: session 13 — replace object? with ported PinnedCertSet type.
+///
+internal sealed class TlsPinnedCertReloadOption : NoopReloadOption
+{
+ // TODO: session 13 — replace object? with ported PinnedCertSet type
+ private readonly object? _newValue;
+ public TlsPinnedCertReloadOption(object? newValue) => _newValue = newValue;
+
+ public override void Apply(NatsServer server)
+ => server.Noticef("Reloaded: pinned_certs");
+}
+
+///
+/// Reload option for the TLS handshake_first setting.
+/// Mirrors Go tlsHandshakeFirst struct in reload.go.
+///
+internal sealed class TlsHandshakeFirstReloadOption : NoopReloadOption
+{
+ private readonly bool _newValue;
+ public TlsHandshakeFirstReloadOption(bool newValue) => _newValue = newValue;
+
+ public override void Apply(NatsServer server)
+ => server.Noticef("Reloaded: Client TLS handshake first: {0}", _newValue);
+}
+
+///
+/// Reload option for the TLS handshake_first_fallback delay setting.
+/// Mirrors Go tlsHandshakeFirstFallback struct in reload.go.
+///
+internal sealed class TlsHandshakeFirstFallbackReloadOption : NoopReloadOption
+{
+ private readonly TimeSpan _newValue;
+ public TlsHandshakeFirstFallbackReloadOption(TimeSpan newValue) => _newValue = newValue;
+
+ public override void Apply(NatsServer server)
+ => server.Noticef("Reloaded: Client TLS handshake first fallback delay: {0}", _newValue);
+}
+
+// =============================================================================
+// Authorization option types
+// =============================================================================
+
+///
+/// Reload option for the username authorization setting.
+/// Mirrors Go usernameOption struct in reload.go.
+///
+internal sealed class UsernameReloadOption : AuthReloadOption
+{
+ public override void Apply(NatsServer server)
+ => server.Noticef("Reloaded: authorization username");
+}
+
+///
+/// Reload option for the password authorization setting.
+/// Mirrors Go passwordOption struct in reload.go.
+///
+internal sealed class PasswordReloadOption : AuthReloadOption
+{
+ public override void Apply(NatsServer server)
+ => server.Noticef("Reloaded: authorization password");
+}
+
+///
+/// Reload option for the token authorization setting.
+/// Mirrors Go authorizationOption struct in reload.go.
+///
+internal sealed class AuthorizationReloadOption : AuthReloadOption
+{
+ public override void Apply(NatsServer server)
+ => server.Noticef("Reloaded: authorization token");
+}
+
+///
+/// Reload option for the authorization timeout setting.
+/// Note: this is a NoopReloadOption (not auth) because authorization
+/// will be reloaded with options separately.
+/// Mirrors Go authTimeoutOption struct in reload.go.
+///
+internal sealed class AuthTimeoutReloadOption : NoopReloadOption
+{
+ private readonly double _newValue;
+ public AuthTimeoutReloadOption(double newValue) => _newValue = newValue;
+
+ public override void Apply(NatsServer server)
+ => server.Noticef("Reloaded: authorization timeout = {0}", _newValue);
+}
+
+///
+/// Reload option for the tags setting.
+/// Mirrors Go tagsOption struct in reload.go.
+///
+internal sealed class TagsReloadOption : NoopReloadOption
+{
+ public override void Apply(NatsServer server)
+ => server.Noticef("Reloaded: tags");
+
+ public override bool IsStatszChange() => true;
+}
+
+///
+/// Reload option for the metadata setting.
+/// Mirrors Go metadataOption struct in reload.go.
+///
+internal sealed class MetadataReloadOption : NoopReloadOption
+{
+ public override void Apply(NatsServer server)
+ => server.Noticef("Reloaded: metadata");
+
+ public override bool IsStatszChange() => true;
+}
+
+///
+/// Reload option for the authorization users setting.
+/// Mirrors Go usersOption struct in reload.go.
+///
+internal sealed class UsersReloadOption : AuthReloadOption
+{
+ public override void Apply(NatsServer server)
+ => server.Noticef("Reloaded: authorization users");
+}
+
+///
+/// Reload option for the authorization nkeys setting.
+/// Mirrors Go nkeysOption struct in reload.go.
+///
+internal sealed class NkeysReloadOption : AuthReloadOption
+{
+ public override void Apply(NatsServer server)
+ => server.Noticef("Reloaded: authorization nkey users");
+}
+
+///
+/// Reload option for the accounts setting.
+/// Mirrors Go accountsOption struct in reload.go.
+///
+internal sealed class AccountsReloadOption : AuthReloadOption
+{
+ public override void Apply(NatsServer server)
+ => server.Noticef("Reloaded: accounts");
+}
+
+// =============================================================================
+// Cluster option types
+// =============================================================================
+
+///
+/// Reload option for the cluster setting.
+/// Stores cluster options as object? pending the port of ClusterOpts.
+/// Mirrors Go clusterOption struct in reload.go.
+/// TODO: session 13 — replace object? with ported ClusterOpts type.
+///
+internal sealed class ClusterReloadOption : AuthReloadOption
+{
+ // TODO: session 13 — replace object? with ported ClusterOpts type
+ private readonly object? _newValue;
+ private readonly bool _permsChanged;
+ private readonly bool _poolSizeChanged;
+ private readonly bool _compressChanged;
+ private readonly string[] _accsAdded;
+ private readonly string[] _accsRemoved;
+
+ public ClusterReloadOption(
+ object? newValue,
+ bool permsChanged,
+ bool poolSizeChanged,
+ bool compressChanged,
+ string[] accsAdded,
+ string[] accsRemoved)
+ {
+ _newValue = newValue;
+ _permsChanged = permsChanged;
+ _poolSizeChanged = poolSizeChanged;
+ _compressChanged = compressChanged;
+ _accsAdded = accsAdded;
+ _accsRemoved = accsRemoved;
+ }
+
+ public override bool IsClusterPermsChange()
+ => _permsChanged;
+
+ public override bool IsClusterPoolSizeOrAccountsChange()
+ => _poolSizeChanged || _accsAdded.Length > 0 || _accsRemoved.Length > 0;
+
+ public override void Apply(NatsServer server)
+ {
+ // TODO: session 13 — full cluster apply logic (TLS, route info, compression)
+ server.Noticef("Reloaded: cluster");
+ }
+}
+
+///
+/// Reload option for the cluster routes setting.
+/// Routes to add/remove are stored as object[] pending the port of URL handling.
+/// Mirrors Go routesOption struct in reload.go.
+/// TODO: session 13 — replace object[] with Uri[] when route types are ported.
+///
+internal sealed class RoutesReloadOption : NoopReloadOption
+{
+ // TODO: session 13 — replace object[] with Uri[] when route URL types are ported
+ private readonly object[] _add;
+ private readonly object[] _remove;
+
+ public RoutesReloadOption(object[] add, object[] remove)
+ {
+ _add = add;
+ _remove = remove;
+ }
+
+ public override void Apply(NatsServer server)
+ {
+ // TODO: session 13 — add/remove routes, update varzUpdateRouteURLs
+ server.Noticef("Reloaded: cluster routes");
+ }
+}
+
+// =============================================================================
+// Connection limit & network option types
+// =============================================================================
+
+///
+/// Reload option for the max_connections setting.
+/// Mirrors Go maxConnOption struct in reload.go.
+///
+internal sealed class MaxConnReloadOption : NoopReloadOption
+{
+ private readonly int _newValue;
+ public MaxConnReloadOption(int newValue) => _newValue = newValue;
+
+ public override void Apply(NatsServer server)
+ {
+ // TODO: session 13 — close random connections if over limit
+ server.Noticef("Reloaded: max_connections = {0}", _newValue);
+ }
+}
+
+///
+/// Reload option for the pid_file setting.
+/// Mirrors Go pidFileOption struct in reload.go.
+///
+internal sealed class PidFileReloadOption : NoopReloadOption
+{
+ private readonly string _newValue;
+ public PidFileReloadOption(string newValue) => _newValue = newValue;
+
+ public override void Apply(NatsServer server)
+ {
+ if (string.IsNullOrEmpty(_newValue))
+ return;
+ // TODO: session 13 — call server.LogPid()
+ server.Noticef("Reloaded: pid_file = {0}", _newValue);
+ }
+}
+
+///
+/// Reload option for the ports_file_dir setting.
+/// Mirrors Go portsFileDirOption struct in reload.go.
+///
+internal sealed class PortsFileDirReloadOption : NoopReloadOption
+{
+ private readonly string _oldValue;
+ private readonly string _newValue;
+
+ public PortsFileDirReloadOption(string oldValue, string newValue)
+ {
+ _oldValue = oldValue;
+ _newValue = newValue;
+ }
+
+ public override void Apply(NatsServer server)
+ {
+ // TODO: session 13 — call server.DeletePortsFile(_oldValue) and server.LogPorts()
+ server.Noticef("Reloaded: ports_file_dir = {0}", _newValue);
+ }
+}
+
+///
+/// Reload option for the max_control_line setting.
+/// Mirrors Go maxControlLineOption struct in reload.go.
+///
+internal sealed class MaxControlLineReloadOption : NoopReloadOption
+{
+ private readonly int _newValue;
+ public MaxControlLineReloadOption(int newValue) => _newValue = newValue;
+
+ public override void Apply(NatsServer server)
+ {
+ // TODO: session 13 — update mcl on each connected client
+ server.Noticef("Reloaded: max_control_line = {0}", _newValue);
+ }
+}
+
+///
+/// Reload option for the max_payload setting.
+/// Mirrors Go maxPayloadOption struct in reload.go.
+///
+internal sealed class MaxPayloadReloadOption : NoopReloadOption
+{
+ private readonly int _newValue;
+ public MaxPayloadReloadOption(int newValue) => _newValue = newValue;
+
+ public override void Apply(NatsServer server)
+ {
+ // TODO: session 13 — update server info and mpay on each client
+ server.Noticef("Reloaded: max_payload = {0}", _newValue);
+ }
+}
+
+///
+/// Reload option for the ping_interval setting.
+/// Mirrors Go pingIntervalOption struct in reload.go.
+///
+internal sealed class PingIntervalReloadOption : NoopReloadOption
+{
+ private readonly TimeSpan _newValue;
+ public PingIntervalReloadOption(TimeSpan newValue) => _newValue = newValue;
+
+ public override void Apply(NatsServer server)
+ => server.Noticef("Reloaded: ping_interval = {0}", _newValue);
+}
+
+///
+/// Reload option for the ping_max setting.
+/// Mirrors Go maxPingsOutOption struct in reload.go.
+///
+internal sealed class MaxPingsOutReloadOption : NoopReloadOption
+{
+ private readonly int _newValue;
+ public MaxPingsOutReloadOption(int newValue) => _newValue = newValue;
+
+ public override void Apply(NatsServer server)
+ => server.Noticef("Reloaded: ping_max = {0}", _newValue);
+}
+
+///
+/// Reload option for the write_deadline setting.
+/// Mirrors Go writeDeadlineOption struct in reload.go.
+///
+internal sealed class WriteDeadlineReloadOption : NoopReloadOption
+{
+ private readonly TimeSpan _newValue;
+ public WriteDeadlineReloadOption(TimeSpan newValue) => _newValue = newValue;
+
+ public override void Apply(NatsServer server)
+ => server.Noticef("Reloaded: write_deadline = {0}", _newValue);
+}
+
+///
+/// Reload option for the client_advertise setting.
+/// Mirrors Go clientAdvertiseOption struct in reload.go.
+///
+internal sealed class ClientAdvertiseReloadOption : NoopReloadOption
+{
+ private readonly string _newValue;
+ public ClientAdvertiseReloadOption(string newValue) => _newValue = newValue;
+
+ public override void Apply(NatsServer server)
+ {
+ // TODO: session 13 — call server.SetInfoHostPort()
+ server.Noticef("Reload: client_advertise = {0}", _newValue);
+ }
+}
+
+// =============================================================================
+// JetStream option type
+// =============================================================================
+
+///
+/// Reload option for the jetstream setting.
+/// Mirrors Go jetStreamOption struct in reload.go.
+///
+internal sealed class JetStreamReloadOption : NoopReloadOption
+{
+ private readonly bool _newValue;
+ public JetStreamReloadOption(bool newValue) => _newValue = newValue;
+
+ public override bool IsJetStreamChange() => true;
+ public override bool IsStatszChange() => true;
+
+ public override void Apply(NatsServer server)
+ => server.Noticef("Reloaded: JetStream");
+}
+
+// =============================================================================
+// Miscellaneous option types
+// =============================================================================
+
+///
+/// Reload option for the default_sentinel setting.
+/// Mirrors Go defaultSentinelOption struct in reload.go.
+///
+internal sealed class DefaultSentinelReloadOption : NoopReloadOption
+{
+ private readonly string _newValue;
+ public DefaultSentinelReloadOption(string newValue) => _newValue = newValue;
+
+ public override void Apply(NatsServer server)
+ => server.Noticef("Reloaded: default_sentinel = {0}", _newValue);
+}
+
+///
+/// Reload option for the OCSP setting.
+/// The new value is stored as object? pending the port of OCSPConfig.
+/// Mirrors Go ocspOption struct in reload.go.
+/// TODO: session 13 — replace object? with ported OcspConfig type.
+///
+internal sealed class OcspReloadOption : TlsBaseReloadOption
+{
+ // TODO: session 13 — replace object? with ported OcspConfig type
+ private readonly object? _newValue;
+ public OcspReloadOption(object? newValue) => _newValue = newValue;
+
+ public override void Apply(NatsServer server)
+ => server.Noticef("Reloaded: OCSP");
+}
+
+///
+/// Reload option for the OCSP response cache setting.
+/// The new value is stored as object? pending the port of
+/// OCSPResponseCacheConfig.
+/// Mirrors Go ocspResponseCacheOption struct in reload.go.
+/// TODO: session 13 — replace object? with ported OcspResponseCacheConfig type.
+///
+internal sealed class OcspResponseCacheReloadOption : TlsBaseReloadOption
+{
+ // TODO: session 13 — replace object? with ported OcspResponseCacheConfig type
+ private readonly object? _newValue;
+ public OcspResponseCacheReloadOption(object? newValue) => _newValue = newValue;
+
+ public override void Apply(NatsServer server)
+ => server.Noticef("Reloaded OCSP peer cache");
+}
+
+///
+/// Reload option for the connect_error_reports setting.
+/// Mirrors Go connectErrorReports struct in reload.go.
+///
+internal sealed class ConnectErrorReportsReloadOption : NoopReloadOption
+{
+ private readonly int _newValue;
+ public ConnectErrorReportsReloadOption(int newValue) => _newValue = newValue;
+
+ public override void Apply(NatsServer server)
+ => server.Noticef("Reloaded: connect_error_reports = {0}", _newValue);
+}
+
+///
+/// Reload option for the reconnect_error_reports setting.
+/// Mirrors Go reconnectErrorReports struct in reload.go.
+///
+internal sealed class ReconnectErrorReportsReloadOption : NoopReloadOption
+{
+ private readonly int _newValue;
+ public ReconnectErrorReportsReloadOption(int newValue) => _newValue = newValue;
+
+ public override void Apply(NatsServer server)
+ => server.Noticef("Reloaded: reconnect_error_reports = {0}", _newValue);
+}
+
+///
+/// Reload option for the max_traced_msg_len setting.
+/// Mirrors Go maxTracedMsgLenOption struct in reload.go.
+///
+internal sealed class MaxTracedMsgLenReloadOption : NoopReloadOption
+{
+ private readonly int _newValue;
+ public MaxTracedMsgLenReloadOption(int newValue) => _newValue = newValue;
+
+ public override void Apply(NatsServer server)
+ {
+ // TODO: session 13 — update server.Opts.MaxTracedMsgLen under lock
+ server.Noticef("Reloaded: max_traced_msg_len = {0}", _newValue);
+ }
+}
+
+// =============================================================================
+// MQTT option types
+// =============================================================================
+
+///
+/// Reload option for the MQTT ack_wait setting.
+/// Mirrors Go mqttAckWaitReload struct in reload.go.
+///
+internal sealed class MqttAckWaitReloadOption : NoopReloadOption
+{
+ private readonly TimeSpan _newValue;
+ public MqttAckWaitReloadOption(TimeSpan newValue) => _newValue = newValue;
+
+ public override void Apply(NatsServer server)
+ => server.Noticef("Reloaded: MQTT ack_wait = {0}", _newValue);
+}
+
+///
+/// Reload option for the MQTT max_ack_pending setting.
+/// Mirrors Go mqttMaxAckPendingReload struct in reload.go.
+///
+internal sealed class MqttMaxAckPendingReloadOption : NoopReloadOption
+{
+ private readonly ushort _newValue;
+ public MqttMaxAckPendingReloadOption(ushort newValue) => _newValue = newValue;
+
+ public override void Apply(NatsServer server)
+ {
+ // TODO: session 13 — call server.MqttUpdateMaxAckPending(_newValue)
+ server.Noticef("Reloaded: MQTT max_ack_pending = {0}", _newValue);
+ }
+}
+
+///
+/// Reload option for the MQTT stream_replicas setting.
+/// Mirrors Go mqttStreamReplicasReload struct in reload.go.
+///
+internal sealed class MqttStreamReplicasReloadOption : NoopReloadOption
+{
+ private readonly int _newValue;
+ public MqttStreamReplicasReloadOption(int newValue) => _newValue = newValue;
+
+ public override void Apply(NatsServer server)
+ => server.Noticef("Reloaded: MQTT stream_replicas = {0}", _newValue);
+}
+
+///
+/// Reload option for the MQTT consumer_replicas setting.
+/// Mirrors Go mqttConsumerReplicasReload struct in reload.go.
+///
+internal sealed class MqttConsumerReplicasReloadOption : NoopReloadOption
+{
+ private readonly int _newValue;
+ public MqttConsumerReplicasReloadOption(int newValue) => _newValue = newValue;
+
+ public override void Apply(NatsServer server)
+ => server.Noticef("Reloaded: MQTT consumer_replicas = {0}", _newValue);
+}
+
+///
+/// Reload option for the MQTT consumer_memory_storage setting.
+/// Mirrors Go mqttConsumerMemoryStorageReload struct in reload.go.
+///
+internal sealed class MqttConsumerMemoryStorageReloadOption : NoopReloadOption
+{
+ private readonly bool _newValue;
+ public MqttConsumerMemoryStorageReloadOption(bool newValue) => _newValue = newValue;
+
+ public override void Apply(NatsServer server)
+ => server.Noticef("Reloaded: MQTT consumer_memory_storage = {0}", _newValue);
+}
+
+///
+/// Reload option for the MQTT consumer_inactive_threshold setting.
+/// Mirrors Go mqttInactiveThresholdReload struct in reload.go.
+///
+internal sealed class MqttInactiveThresholdReloadOption : NoopReloadOption
+{
+ private readonly TimeSpan _newValue;
+ public MqttInactiveThresholdReloadOption(TimeSpan newValue) => _newValue = newValue;
+
+ public override void Apply(NatsServer server)
+ => server.Noticef("Reloaded: MQTT consumer_inactive_threshold = {0}", _newValue);
+}
+
+// =============================================================================
+// Profiling option type
+// =============================================================================
+
+///
+/// Reload option for the prof_block_rate setting.
+/// Mirrors Go profBlockRateReload struct in reload.go.
+///
+internal sealed class ProfBlockRateReloadOption : NoopReloadOption
+{
+ private readonly int _newValue;
+ public ProfBlockRateReloadOption(int newValue) => _newValue = newValue;
+
+ public override void Apply(NatsServer server)
+ {
+ // TODO: session 13 — call server.SetBlockProfileRate(_newValue)
+ server.Noticef("Reloaded: prof_block_rate = {0}", _newValue);
+ }
+}
+
+// =============================================================================
+// LeafNode option type
+// =============================================================================
+
+///
+/// Reload option for leaf-node settings (TLS handshake-first, compression, disabled).
+/// Mirrors Go leafNodeOption struct in reload.go.
+///
+internal sealed class LeafNodeReloadOption : NoopReloadOption
+{
+ private readonly bool _tlsFirstChanged;
+ private readonly bool _compressionChanged;
+ private readonly bool _disabledChanged;
+
+ public LeafNodeReloadOption(bool tlsFirstChanged, bool compressionChanged, bool disabledChanged)
+ {
+ _tlsFirstChanged = tlsFirstChanged;
+ _compressionChanged = compressionChanged;
+ _disabledChanged = disabledChanged;
+ }
+
+ public override void Apply(NatsServer server)
+ {
+ // TODO: session 13 — full leaf-node apply logic from Go leafNodeOption.Apply()
+ if (_tlsFirstChanged)
+ server.Noticef("Reloaded: LeafNode TLS HandshakeFirst settings");
+ if (_compressionChanged)
+ server.Noticef("Reloaded: LeafNode compression settings");
+ if (_disabledChanged)
+ server.Noticef("Reloaded: LeafNode disabled/enabled state");
+ }
+}
+
+// =============================================================================
+// NoFastProducerStall option type
+// =============================================================================
+
+///
+/// Reload option for the no_fast_producer_stall setting.
+/// Mirrors Go noFastProdStallReload struct in reload.go.
+///
+internal sealed class NoFastProducerStallReloadOption : NoopReloadOption
+{
+ private readonly bool _noStall;
+ public NoFastProducerStallReloadOption(bool noStall) => _noStall = noStall;
+
+ public override void Apply(NatsServer server)
+ {
+ var not = _noStall ? "not " : string.Empty;
+ server.Noticef("Reloaded: fast producers will {0}be stalled", not);
+ }
+}
+
+// =============================================================================
+// Proxies option type
+// =============================================================================
+
+///
+/// Reload option for the proxies trusted keys setting.
+/// Mirrors Go proxiesReload struct in reload.go.
+///
+internal sealed class ProxiesReloadOption : NoopReloadOption
+{
+ private readonly string[] _add;
+ private readonly string[] _del;
+
+ public ProxiesReloadOption(string[] add, string[] del)
+ {
+ _add = add;
+ _del = del;
+ }
+
+ public override void Apply(NatsServer server)
+ {
+ // TODO: session 13 — disconnect proxied clients for removed keys,
+ // call server.ProcessProxiesTrustedKeys()
+ if (_del.Length > 0)
+ server.Noticef("Reloaded: proxies trusted keys {0} were removed", string.Join(", ", _del));
+ if (_add.Length > 0)
+ server.Noticef("Reloaded: proxies trusted keys {0} were added", string.Join(", ", _add));
+ }
+}
+
+// =============================================================================
+// ConfigReloader — stub for server/reload.go Reload() / ReloadOptions()
+// =============================================================================
+
+///
+/// Stub for the configuration reloader.
+/// Full reload logic (diffOptions, applyOptions, recheckPinnedCerts) will be
+/// implemented in a future session.
+/// Mirrors Go Server.Reload() and Server.ReloadOptions() in
+/// server/reload.go.
+///
+internal sealed class ConfigReloader
+{
+ // TODO: session 13 — full reload logic
+ // Mirrors Go server.Reload() / server.ReloadOptions() in server/reload.go
+
+ ///
+ /// Stub: read and apply the server config file.
+ /// Returns null on success; a non-null Exception describes the failure.
+ ///
+ public Exception? Reload(NatsServer server) => null;
+}
diff --git a/dotnet/src/ZB.MOM.NatsNet.Server/Events/EventTypes.cs b/dotnet/src/ZB.MOM.NatsNet.Server/Events/EventTypes.cs
new file mode 100644
index 0000000..540ac72
--- /dev/null
+++ b/dotnet/src/ZB.MOM.NatsNet.Server/Events/EventTypes.cs
@@ -0,0 +1,778 @@
+// Copyright 2018-2026 The NATS Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Adapted from server/events.go in the NATS server Go source.
+
+using System.Text.Json.Serialization;
+using ZB.MOM.NatsNet.Server.Auth.CertificateIdentityProvider;
+using ZB.MOM.NatsNet.Server.Internal;
+
+namespace ZB.MOM.NatsNet.Server;
+
+// ============================================================================
+// System subject constants
+// Mirrors the const block at the top of server/events.go.
+// ============================================================================
+
+///
+/// System-account subject templates and constants used for internal NATS server
+/// event routing. All format-string fields use with
+/// the appropriate server/account ID substituted at call time.
+/// Mirrors the const block in server/events.go.
+///
+public static class SystemSubjects
+{
+ // Account lookup / claims
+ public const string AccLookupReqSubj = "$SYS.REQ.ACCOUNT.{0}.CLAIMS.LOOKUP";
+ public const string AccPackReqSubj = "$SYS.REQ.CLAIMS.PACK";
+ public const string AccListReqSubj = "$SYS.REQ.CLAIMS.LIST";
+ public const string AccClaimsReqSubj = "$SYS.REQ.CLAIMS.UPDATE";
+ public const string AccDeleteReqSubj = "$SYS.REQ.CLAIMS.DELETE";
+
+ // Connection events
+ public const string ConnectEventSubj = "$SYS.ACCOUNT.{0}.CONNECT";
+ public const string DisconnectEventSubj = "$SYS.ACCOUNT.{0}.DISCONNECT";
+
+ // Direct request routing
+ public const string AccDirectReqSubj = "$SYS.REQ.ACCOUNT.{0}.{1}";
+ public const string AccPingReqSubj = "$SYS.REQ.ACCOUNT.PING.{0}";
+
+ // Account update events (both old and new forms kept for backward compatibility)
+ public const string AccUpdateEventSubjOld = "$SYS.ACCOUNT.{0}.CLAIMS.UPDATE";
+ public const string AccUpdateEventSubjNew = "$SYS.REQ.ACCOUNT.{0}.CLAIMS.UPDATE";
+
+ public const string ConnsRespSubj = "$SYS._INBOX_.{0}";
+ public const string AccConnsEventSubjNew = "$SYS.ACCOUNT.{0}.SERVER.CONNS";
+ public const string AccConnsEventSubjOld = "$SYS.SERVER.ACCOUNT.{0}.CONNS"; // backward compat
+
+ // Server lifecycle events
+ public const string LameDuckEventSubj = "$SYS.SERVER.{0}.LAMEDUCK";
+ public const string ShutdownEventSubj = "$SYS.SERVER.{0}.SHUTDOWN";
+
+ // Client control
+ public const string ClientKickReqSubj = "$SYS.REQ.SERVER.{0}.KICK";
+ public const string ClientLdmReqSubj = "$SYS.REQ.SERVER.{0}.LDM";
+
+ // Auth error events
+ public const string AuthErrorEventSubj = "$SYS.SERVER.{0}.CLIENT.AUTH.ERR";
+ public const string AuthErrorAccountEventSubj = "$SYS.ACCOUNT.CLIENT.AUTH.ERR";
+
+ // Stats
+ public const string ServerStatsSubj = "$SYS.SERVER.{0}.STATSZ";
+ public const string ServerDirectReqSubj = "$SYS.REQ.SERVER.{0}.{1}";
+ public const string ServerPingReqSubj = "$SYS.REQ.SERVER.PING.{0}";
+ public const string ServerStatsPingReqSubj = "$SYS.REQ.SERVER.PING"; // deprecated; use STATSZ variant
+ public const string ServerReloadReqSubj = "$SYS.REQ.SERVER.{0}.RELOAD";
+
+ // Leaf node
+ public const string LeafNodeConnectEventSubj = "$SYS.ACCOUNT.{0}.LEAFNODE.CONNECT"; // internal only
+
+ // Latency
+ public const string RemoteLatencyEventSubj = "$SYS.LATENCY.M2.{0}";
+ public const string InboxRespSubj = "$SYS._INBOX.{0}.{1}";
+
+ // User info
+ public const string UserDirectInfoSubj = "$SYS.REQ.USER.INFO";
+ public const string UserDirectReqSubj = "$SYS.REQ.USER.{0}.INFO";
+
+ // Subscription count
+ public const string AccNumSubsReqSubj = "$SYS.REQ.ACCOUNT.NSUBS";
+
+ // Debug
+ public const string AccSubsSubj = "$SYS.DEBUG.SUBSCRIBERS";
+
+ // OCSP peer events
+ public const string OcspPeerRejectEventSubj = "$SYS.SERVER.{0}.OCSP.PEER.CONN.REJECT";
+ public const string OcspPeerChainlinkInvalidEventSubj = "$SYS.SERVER.{0}.OCSP.PEER.LINK.INVALID";
+
+ // Parsing constants (token indexes / counts)
+ public const int AccLookupReqTokens = 6;
+ public const int ShutdownEventTokens = 4;
+ public const int ServerSubjectIndex = 2;
+ public const int AccUpdateTokensNew = 6;
+ public const int AccUpdateTokensOld = 5;
+ public const int AccUpdateAccIdxOld = 2;
+ public const int AccReqTokens = 5;
+ public const int AccReqAccIndex = 3;
+}
+
+// ============================================================================
+// Advisory message type schema URI constants
+// Mirrors the const string variables near each struct in server/events.go.
+// ============================================================================
+
+public static class EventMsgTypes
+{
+ public const string ConnectEventMsgType = "io.nats.server.advisory.v1.client_connect";
+ public const string DisconnectEventMsgType = "io.nats.server.advisory.v1.client_disconnect";
+ public const string OcspPeerRejectEventMsgType = "io.nats.server.advisory.v1.ocsp_peer_reject";
+ public const string OcspPeerChainlinkInvalidEventMsgType = "io.nats.server.advisory.v1.ocsp_peer_link_invalid";
+ public const string AccountNumConnsMsgType = "io.nats.server.advisory.v1.account_connections";
+}
+
+// ============================================================================
+// Heartbeat / rate-limit intervals (mirrors package-level vars in events.go)
+// ============================================================================
+
+///
+/// Default timing constants for server event heartbeats and rate limiting.
+/// Mirrors Go package-level var declarations in events.go.
+///
+public static class EventIntervals
+{
+ /// Default HB interval for events. Mirrors Go eventsHBInterval = 30s.
+ public static readonly TimeSpan EventsHbInterval = TimeSpan.FromSeconds(30);
+
+ /// Default HB interval for stats. Mirrors Go statsHBInterval = 10s.
+ public static readonly TimeSpan StatsHbInterval = TimeSpan.FromSeconds(10);
+
+ /// Minimum interval between statsz publishes. Mirrors Go defaultStatszRateLimit = 1s.
+ public static readonly TimeSpan DefaultStatszRateLimit = TimeSpan.FromSeconds(1);
+}
+
+// ============================================================================
+// SysMsgHandler — delegate for internal system message dispatch
+// Mirrors Go sysMsgHandler func type in events.go.
+// ============================================================================
+
+///
+/// Callback invoked when an internal system message is dispatched.
+/// Mirrors Go sysMsgHandler in server/events.go.
+///
+public delegate void SysMsgHandler(
+ Subscription sub,
+ NatsClient client,
+ Account acc,
+ string subject,
+ string reply,
+ byte[] hdr,
+ byte[] msg);
+
+// ============================================================================
+// InSysMsg — queued internal system message
+// Mirrors Go inSysMsg struct in server/events.go.
+// ============================================================================
+
+///
+/// Holds a system message queued for internal delivery, avoiding the
+/// route/gateway path.
+/// Mirrors Go inSysMsg struct in server/events.go.
+///
+internal sealed class InSysMsg
+{
+ public Subscription? Sub { get; set; }
+ public NatsClient? Client { get; set; }
+ public Account? Acc { get; set; }
+ public string Subject { get; set; } = string.Empty;
+ public string Reply { get; set; } = string.Empty;
+ public byte[]? Hdr { get; set; }
+ public byte[]? Msg { get; set; }
+ public SysMsgHandler? Cb { get; set; }
+}
+
+// ============================================================================
+// InternalState — server internal/sys state
+// Mirrors Go internal struct in server/events.go.
+// Uses Monitor lock (lock(this)) in place of Go's embedded sync.Mutex.
+// ============================================================================
+
+///
+/// Holds all internal state used by the server's system-account event
+/// machinery: account reference, client, send/receive queues, timers,
+/// reply handlers, and heartbeat configuration.
+/// Mirrors Go internal struct in server/events.go.
+///
+internal sealed class InternalState
+{
+ // ---- identity / sequencing ----
+ public Account? Account { get; set; }
+ public NatsClient? Client { get; set; }
+ public ulong Seq { get; set; }
+ public int Sid { get; set; }
+
+ // ---- remote server tracking ----
+ /// Map of server ID → serverUpdate. Mirrors Go servers map[string]*serverUpdate.
+ public Dictionary Servers { get; set; } = new();
+
+ // ---- timers ----
+ /// Sweeper timer. Mirrors Go sweeper *time.Timer.
+ public System.Threading.Timer? Sweeper { get; set; }
+
+ /// Stats heartbeat timer. Mirrors Go stmr *time.Timer.
+ public System.Threading.Timer? StatsMsgTimer { get; set; }
+
+ // ---- reply handlers ----
+ ///
+ /// Pending reply subject → handler map.
+ /// Mirrors Go replies map[string]msgHandler.
+ ///
+ public Dictionary> Replies { get; set; } = new();
+
+ // ---- queues ----
+ /// Outbound message send queue. Mirrors Go sendq *ipQueue[*pubMsg].
+ public IpQueue? SendQueue { get; set; }
+
+ /// Inbound receive queue. Mirrors Go recvq *ipQueue[*inSysMsg].
+ public IpQueue? RecvQueue { get; set; }
+
+ /// Priority receive queue for STATSZ/Pings. Mirrors Go recvqp *ipQueue[*inSysMsg].
+ public IpQueue? RecvQueuePriority { get; set; }
+
+ /// Reset channel used to restart the send loop. Mirrors Go resetCh chan struct{}.
+ public System.Threading.Channels.Channel? ResetChannel { get; set; }
+
+ // ---- durations ----
+ /// Maximum time before an orphaned server entry is removed. Mirrors Go orphMax.
+ public TimeSpan OrphanMax { get; set; }
+
+ /// Interval at which orphan checks run. Mirrors Go chkOrph.
+ public TimeSpan CheckOrphan { get; set; }
+
+ /// Interval between statsz publishes. Mirrors Go statsz.
+ public TimeSpan StatszInterval { get; set; }
+
+ /// Client-facing statsz interval. Mirrors Go cstatsz.
+ public TimeSpan ClientStatszInterval { get; set; }
+
+ // ---- misc ----
+ /// Short hash used for shared-inbox routing. Mirrors Go shash string.
+ public string ShortHash { get; set; } = string.Empty;
+
+ /// Inbox prefix for this server's internal client. Mirrors Go inboxPre string.
+ public string InboxPrefix { get; set; } = string.Empty;
+
+ /// Subscription for remote stats. Mirrors Go remoteStatsSub *subscription.
+ public Subscription? RemoteStatsSub { get; set; }
+
+ /// Time of the last statsz publish. Mirrors Go lastStatsz time.Time.
+ public DateTime LastStatsz { get; set; }
+}
+
+// ============================================================================
+// ServerUpdate — remote server heartbeat tracking
+// Mirrors Go serverUpdate struct in server/events.go.
+// ============================================================================
+
+///
+/// Tracks the sequence number and last-seen timestamp of a remote server's
+/// system heartbeat. Used to detect orphaned servers.
+/// Mirrors Go serverUpdate struct in server/events.go.
+///
+internal sealed class ServerUpdate
+{
+ /// Last sequence number received from the remote server.
+ public ulong Seq { get; set; }
+
+ /// Wall-clock time of the last heartbeat.
+ public DateTime LTime { get; set; }
+}
+
+// ============================================================================
+// PubMsg — internally-queued outbound publish message
+// Mirrors Go pubMsg struct in server/events.go.
+// ============================================================================
+
+///
+/// Holds an outbound message that the server wants to publish via the internal
+/// send loop, avoiding direct route/gateway writes.
+/// Mirrors Go pubMsg struct in server/events.go.
+///
+internal sealed class PubMsg
+{
+ public NatsClient? Client { get; set; }
+ public string Subject { get; set; } = string.Empty;
+ public string Reply { get; set; } = string.Empty;
+ public ServerInfo? Si { get; set; }
+ public byte[]? Hdr { get; set; }
+ public object? Msg { get; set; }
+
+ /// Compression type. TODO: session 12 — wire up compressionType enum.
+ public int Oct { get; set; }
+
+ public bool Echo { get; set; }
+ public bool Last { get; set; }
+
+ // TODO: session 12 — add pool return helper (returnToPool).
+}
+
+// ============================================================================
+// DataStats — message/byte counter pair (sent or received)
+// Mirrors Go DataStats struct in server/events.go.
+// ============================================================================
+
+///
+/// Reports how many messages and bytes were sent or received.
+/// Optionally breaks out gateway, route, and leaf-node traffic.
+/// Mirrors Go DataStats struct in server/events.go.
+///
+public sealed class DataStats
+{
+ [JsonPropertyName("msgs")]
+ public long Msgs { get; set; }
+
+ [JsonPropertyName("bytes")]
+ public long Bytes { get; set; }
+
+ [JsonPropertyName("gateways")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public MsgBytes? Gateways { get; set; }
+
+ [JsonPropertyName("routes")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public MsgBytes? Routes { get; set; }
+
+ [JsonPropertyName("leafs")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public MsgBytes? Leafs { get; set; }
+}
+
+// ============================================================================
+// MsgBytes — simple message+byte pair used inside DataStats
+// Mirrors Go MsgBytes struct in server/events.go.
+// ============================================================================
+
+///
+/// A simple pair of message and byte counts, used as a nested breakdown
+/// inside .
+/// Mirrors Go MsgBytes struct in server/events.go.
+///
+public sealed class MsgBytes
+{
+ [JsonPropertyName("msgs")]
+ public long Msgs { get; set; }
+
+ [JsonPropertyName("bytes")]
+ public long Bytes { get; set; }
+}
+
+// ============================================================================
+// RouteStat / GatewayStat — per-route and per-gateway stat snapshots
+// Mirrors Go RouteStat and GatewayStat in server/events.go.
+// ============================================================================
+
+///
+/// Statistics snapshot for a single cluster route connection.
+/// Mirrors Go RouteStat in server/events.go.
+///
+public sealed class RouteStat
+{
+ [JsonPropertyName("rid")]
+ public ulong Id { get; set; }
+
+ [JsonPropertyName("name")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? Name { get; set; }
+
+ [JsonPropertyName("sent")]
+ public DataStats Sent { get; set; } = new();
+
+ [JsonPropertyName("received")]
+ public DataStats Received { get; set; } = new();
+
+ [JsonPropertyName("pending")]
+ public int Pending { get; set; }
+}
+
+///
+/// Statistics snapshot for a gateway connection.
+/// Mirrors Go GatewayStat in server/events.go.
+///
+public sealed class GatewayStat
+{
+ [JsonPropertyName("gwid")]
+ public ulong Id { get; set; }
+
+ [JsonPropertyName("name")]
+ public string Name { get; set; } = string.Empty;
+
+ [JsonPropertyName("sent")]
+ public DataStats Sent { get; set; } = new();
+
+ [JsonPropertyName("received")]
+ public DataStats Received { get; set; } = new();
+
+ [JsonPropertyName("inbound_connections")]
+ public int NumInbound { get; set; }
+}
+
+// ============================================================================
+// ServerStatsMsg — periodic stats advisory published on $SYS.SERVER.{id}.STATSZ
+// Mirrors Go ServerStatsMsg struct in server/events.go.
+// ============================================================================
+
+///
+/// Periodic advisory message containing the current server statistics.
+/// Mirrors Go ServerStatsMsg struct in server/events.go.
+///
+public sealed class ServerStatsMsg
+{
+ [JsonPropertyName("server")]
+ public ServerInfo Server { get; set; } = new();
+
+ [JsonPropertyName("statsz")]
+ public ServerStatsAdvisory Stats { get; set; } = new();
+}
+
+// ============================================================================
+// ServerStatsAdvisory — the statsz payload inside ServerStatsMsg
+// Mirrors Go ServerStats struct (advisory form) in server/events.go.
+// NOTE: distinct from the internal ServerStats in NatsServerTypes.cs.
+// ============================================================================
+
+///
+/// The JSON-serialisable statistics payload included inside .
+/// Mirrors Go ServerStats struct (advisory form) in server/events.go.
+///
+public sealed class ServerStatsAdvisory
+{
+ [JsonPropertyName("start")]
+ public DateTime Start { get; set; }
+
+ [JsonPropertyName("mem")]
+ public long Mem { get; set; }
+
+ [JsonPropertyName("cores")]
+ public int Cores { get; set; }
+
+ [JsonPropertyName("cpu")]
+ public double Cpu { get; set; }
+
+ [JsonPropertyName("connections")]
+ public int Connections { get; set; }
+
+ [JsonPropertyName("total_connections")]
+ public ulong TotalConnections { get; set; }
+
+ [JsonPropertyName("active_accounts")]
+ public int ActiveAccounts { get; set; }
+
+ [JsonPropertyName("subscriptions")]
+ public uint NumSubs { get; set; }
+
+ [JsonPropertyName("sent")]
+ public DataStats Sent { get; set; } = new();
+
+ [JsonPropertyName("received")]
+ public DataStats Received { get; set; } = new();
+
+ [JsonPropertyName("slow_consumers")]
+ public long SlowConsumers { get; set; }
+
+ [JsonPropertyName("slow_consumer_stats")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public SlowConsumersStats? SlowConsumersStats { get; set; }
+
+ [JsonPropertyName("stale_connections")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
+ public long StaleConnections { get; set; }
+
+ [JsonPropertyName("stale_connection_stats")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public StaleConnectionStats? StaleConnectionStats { get; set; }
+
+ [JsonPropertyName("stalled_clients")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
+ public long StalledClients { get; set; }
+
+ [JsonPropertyName("routes")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public List? Routes { get; set; }
+
+ [JsonPropertyName("gateways")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public List? Gateways { get; set; }
+
+ [JsonPropertyName("active_servers")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
+ public int ActiveServers { get; set; }
+
+ /// JetStream stats. TODO: session 19 — wire JetStreamVarz type.
+ [JsonPropertyName("jetstream")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public object? JetStream { get; set; }
+
+ [JsonPropertyName("gomemlimit")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
+ public long MemLimit { get; set; }
+
+ [JsonPropertyName("gomaxprocs")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
+ public int MaxProcs { get; set; }
+}
+
+// ============================================================================
+// SlowConsumersStats / StaleConnectionStats — advisory-layer per-kind counters
+// These are the JSON-serialisable variants used in ServerStatsAdvisory.
+// The internal atomic counters live in NatsServerTypes.cs (SlowConsumerStats /
+// StaleConnectionStats with different casing).
+// ============================================================================
+
+///
+/// Per-kind slow-consumer counters included in stats advisories.
+/// Mirrors Go SlowConsumersStats in server/monitor.go.
+///
+public sealed class SlowConsumersStats
+{
+ [JsonPropertyName("clients")]
+ public ulong Clients { get; set; }
+
+ [JsonPropertyName("routes")]
+ public ulong Routes { get; set; }
+
+ [JsonPropertyName("gateways")]
+ public ulong Gateways { get; set; }
+
+ [JsonPropertyName("leafs")]
+ public ulong Leafs { get; set; }
+}
+
+///
+/// Per-kind stale-connection counters included in stats advisories.
+/// Mirrors Go StaleConnectionStats in server/monitor.go.
+///
+public sealed class StaleConnectionStats
+{
+ [JsonPropertyName("clients")]
+ public ulong Clients { get; set; }
+
+ [JsonPropertyName("routes")]
+ public ulong Routes { get; set; }
+
+ [JsonPropertyName("gateways")]
+ public ulong Gateways { get; set; }
+
+ [JsonPropertyName("leafs")]
+ public ulong Leafs { get; set; }
+}
+
+// ============================================================================
+// ConnectEventMsg / DisconnectEventMsg — client lifecycle advisories
+// Mirrors Go structs in server/events.go.
+// ============================================================================
+
+///
+/// Advisory published on $SYS.ACCOUNT.{acc}.CONNECT when a new
+/// client connection is established within a tracked account.
+/// Mirrors Go ConnectEventMsg in server/events.go.
+///
+public sealed class ConnectEventMsg : TypedEvent
+{
+ [JsonPropertyName("server")]
+ public ServerInfo Server { get; set; } = new();
+
+ [JsonPropertyName("client")]
+ public ClientInfo Client { get; set; } = new();
+}
+
+///
+/// Advisory published on $SYS.ACCOUNT.{acc}.DISCONNECT when a
+/// previously-tracked client connection closes.
+/// Mirrors Go DisconnectEventMsg in server/events.go.
+///
+public sealed class DisconnectEventMsg : TypedEvent
+{
+ [JsonPropertyName("server")]
+ public ServerInfo Server { get; set; } = new();
+
+ [JsonPropertyName("client")]
+ public ClientInfo Client { get; set; } = new();
+
+ [JsonPropertyName("sent")]
+ public DataStats Sent { get; set; } = new();
+
+ [JsonPropertyName("received")]
+ public DataStats Received { get; set; } = new();
+
+ [JsonPropertyName("reason")]
+ public string Reason { get; set; } = string.Empty;
+}
+
+// ============================================================================
+// OCSPPeerRejectEventMsg / OCSPPeerChainlinkInvalidEventMsg
+// Mirrors Go structs in server/events.go.
+// ============================================================================
+
+///
+/// Advisory published when a peer TLS handshake is rejected due to OCSP
+/// invalidation of the peer's leaf certificate.
+/// Mirrors Go OCSPPeerRejectEventMsg in server/events.go.
+///
+public sealed class OcspPeerRejectEventMsg : TypedEvent
+{
+ [JsonPropertyName("kind")]
+ public string Kind { get; set; } = string.Empty;
+
+ [JsonPropertyName("peer")]
+ public CertInfo Peer { get; set; } = new();
+
+ [JsonPropertyName("server")]
+ public ServerInfo Server { get; set; } = new();
+
+ [JsonPropertyName("reason")]
+ public string Reason { get; set; } = string.Empty;
+}
+
+///
+/// Advisory published when a certificate in a valid TLS chain is found to be
+/// OCSP-invalid during a peer handshake. Both the invalid link and the
+/// peer's leaf cert are included.
+/// Mirrors Go OCSPPeerChainlinkInvalidEventMsg in server/events.go.
+///
+public sealed class OcspPeerChainlinkInvalidEventMsg : TypedEvent
+{
+ [JsonPropertyName("link")]
+ public CertInfo Link { get; set; } = new();
+
+ [JsonPropertyName("peer")]
+ public CertInfo Peer { get; set; } = new();
+
+ [JsonPropertyName("server")]
+ public ServerInfo Server { get; set; } = new();
+
+ [JsonPropertyName("reason")]
+ public string Reason { get; set; } = string.Empty;
+}
+
+// ============================================================================
+// AccountNumConns / AccountStat — account connection count advisories
+// Mirrors Go structs in server/events.go.
+// ============================================================================
+
+///
+/// Advisory heartbeat published when the connection count for a tracked
+/// account changes, or on a periodic schedule.
+/// Mirrors Go AccountNumConns struct in server/events.go.
+///
+public sealed class AccountNumConns : TypedEvent
+{
+ [JsonPropertyName("server")]
+ public ServerInfo Server { get; set; } = new();
+
+ // Embedded AccountStat fields are inlined via composition.
+ [JsonPropertyName("acc")]
+ public string Account { get; set; } = string.Empty;
+
+ [JsonPropertyName("name")]
+ public string Name { get; set; } = string.Empty;
+
+ [JsonPropertyName("conns")]
+ public int Conns { get; set; }
+
+ [JsonPropertyName("leafnodes")]
+ public int LeafNodes { get; set; }
+
+ [JsonPropertyName("total_conns")]
+ public int TotalConns { get; set; }
+
+ [JsonPropertyName("num_subscriptions")]
+ public uint NumSubs { get; set; }
+
+ [JsonPropertyName("sent")]
+ public DataStats Sent { get; set; } = new();
+
+ [JsonPropertyName("received")]
+ public DataStats Received { get; set; } = new();
+
+ [JsonPropertyName("slow_consumers")]
+ public long SlowConsumers { get; set; }
+}
+
+///
+/// Statistic data common to and account-level
+/// monitoring responses.
+/// Mirrors Go AccountStat struct in server/events.go.
+///
+public sealed class AccountStat
+{
+ [JsonPropertyName("acc")]
+ public string Account { get; set; } = string.Empty;
+
+ [JsonPropertyName("name")]
+ public string Name { get; set; } = string.Empty;
+
+ [JsonPropertyName("conns")]
+ public int Conns { get; set; }
+
+ [JsonPropertyName("leafnodes")]
+ public int LeafNodes { get; set; }
+
+ [JsonPropertyName("total_conns")]
+ public int TotalConns { get; set; }
+
+ [JsonPropertyName("num_subscriptions")]
+ public uint NumSubs { get; set; }
+
+ [JsonPropertyName("sent")]
+ public DataStats Sent { get; set; } = new();
+
+ [JsonPropertyName("received")]
+ public DataStats Received { get; set; } = new();
+
+ [JsonPropertyName("slow_consumers")]
+ public long SlowConsumers { get; set; }
+}
+
+///
+/// Internal request payload sent when this server first starts tracking an
+/// account, asking peer servers for their local connection counts.
+/// Mirrors Go accNumConnsReq struct in server/events.go.
+///
+internal sealed class AccNumConnsReq
+{
+ [JsonPropertyName("server")]
+ public ServerInfo Server { get; set; } = new();
+
+ [JsonPropertyName("acc")]
+ public string Account { get; set; } = string.Empty;
+}
+
+// ============================================================================
+// ServerCapability / ServerID — server identity and capability flags
+// Mirrors Go types in server/events.go.
+// ============================================================================
+
+///
+/// Bit-flag capability set for a remote server.
+/// Mirrors Go ServerCapability uint64 in server/events.go.
+///
+[Flags]
+public enum ServerCapability : ulong
+{
+ /// No capabilities.
+ None = 0,
+
+ /// Server has JetStream enabled. Mirrors Go JetStreamEnabled.
+ JetStreamEnabled = 1UL << 0,
+
+ /// New stream snapshot capability. Mirrors Go BinaryStreamSnapshot.
+ BinaryStreamSnapshot = 1UL << 1,
+
+ /// Move NRG traffic out of system account. Mirrors Go AccountNRG.
+ AccountNrg = 1UL << 2,
+}
+
+///
+/// Minimal static identity for a remote server (name, host, ID).
+/// Mirrors Go ServerID struct in server/events.go.
+///
+public sealed class ServerIdentity
+{
+ [JsonPropertyName("name")]
+ public string Name { get; set; } = string.Empty;
+
+ [JsonPropertyName("host")]
+ public string Host { get; set; } = string.Empty;
+
+ [JsonPropertyName("id")]
+ public string Id { get; set; } = string.Empty;
+}
diff --git a/dotnet/src/ZB.MOM.NatsNet.Server/MessageTrace/MsgTraceTypes.cs b/dotnet/src/ZB.MOM.NatsNet.Server/MessageTrace/MsgTraceTypes.cs
new file mode 100644
index 0000000..da75fa9
--- /dev/null
+++ b/dotnet/src/ZB.MOM.NatsNet.Server/MessageTrace/MsgTraceTypes.cs
@@ -0,0 +1,465 @@
+// Copyright 2024-2026 The NATS Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Adapted from server/msgtrace.go in the NATS server Go source.
+
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace ZB.MOM.NatsNet.Server;
+
+// ============================================================================
+// Message-trace header name constants
+// Mirrors Go const block at top of server/msgtrace.go.
+// ============================================================================
+
+///
+/// NATS message-trace header names and special sentinel values.
+/// Mirrors Go const block in server/msgtrace.go.
+///
+public static class MsgTraceHeaders
+{
+ /// Header that carries the trace destination subject. Mirrors Go MsgTraceDest.
+ public const string MsgTraceDest = "Nats-Trace-Dest";
+
+ ///
+ /// Sentinel value placed in the trace-dest header to disable tracing
+ /// (must be an invalid NATS subject). Mirrors Go MsgTraceDestDisabled.
+ ///
+ public const string MsgTraceDestDisabled = "trace disabled";
+
+ /// Header used for hop-count tracking across servers. Mirrors Go MsgTraceHop.
+ public const string MsgTraceHop = "Nats-Trace-Hop";
+
+ /// Header that carries the originating account name. Mirrors Go MsgTraceOriginAccount.
+ public const string MsgTraceOriginAccount = "Nats-Trace-Origin-Account";
+
+ ///
+ /// When set to a truthy value, the message is consumed only for tracing
+ /// and not delivered to subscribers. Mirrors Go MsgTraceOnly.
+ ///
+ public const string MsgTraceOnly = "Nats-Trace-Only";
+
+ ///
+ /// W3C trace-context parent header. NATS no longer lower-cases this but
+ /// accepts it in any case. Mirrors Go traceParentHdr (internal).
+ ///
+ public const string TraceParentHdr = "traceparent";
+}
+
+// ============================================================================
+// MsgTraceType — discriminator string for polymorphic trace event lists
+// Mirrors Go MsgTraceType string in server/msgtrace.go.
+// ============================================================================
+
+///
+/// Discriminator string identifying the concrete type of a trace event
+/// within a list.
+/// Mirrors Go MsgTraceType string and its constants in server/msgtrace.go.
+///
+public sealed class MsgTraceType
+{
+ private readonly string _value;
+ private MsgTraceType(string value) => _value = value;
+
+ ///
+ public override string ToString() => _value;
+
+ public static implicit operator MsgTraceType(string value) => new(value);
+ public static implicit operator string(MsgTraceType t) => t._value;
+
+ public override bool Equals(object? obj) =>
+ obj is MsgTraceType other && _value == other._value;
+
+ public override int GetHashCode() => _value.GetHashCode();
+
+ // ---- Well-known type constants (mirror Go const block) ----
+
+ /// Ingress event. Mirrors Go MsgTraceIngressType = "in".
+ public static readonly MsgTraceType Ingress = new("in");
+
+ /// Subject-mapping event. Mirrors Go MsgTraceSubjectMappingType = "sm".
+ public static readonly MsgTraceType SubjectMapping = new("sm");
+
+ /// Stream-export event. Mirrors Go MsgTraceStreamExportType = "se".
+ public static readonly MsgTraceType StreamExport = new("se");
+
+ /// Service-import event. Mirrors Go MsgTraceServiceImportType = "si".
+ public static readonly MsgTraceType ServiceImport = new("si");
+
+ /// JetStream storage event. Mirrors Go MsgTraceJetStreamType = "js".
+ public static readonly MsgTraceType JetStream = new("js");
+
+ /// Egress (delivery) event. Mirrors Go MsgTraceEgressType = "eg".
+ public static readonly MsgTraceType Egress = new("eg");
+}
+
+// ============================================================================
+// IMsgTrace — interface for polymorphic trace events
+// Mirrors Go MsgTrace interface in server/msgtrace.go.
+// ============================================================================
+
+///
+/// Marker interface implemented by all concrete message-trace event types.
+/// Enables polymorphic handling of the list.
+/// Mirrors Go MsgTrace interface in server/msgtrace.go.
+///
+public interface IMsgTrace
+{
+ /// Returns the discriminator type string for this trace event.
+ string Typ();
+}
+
+// ============================================================================
+// MsgTraceBase — shared fields present in every trace event
+// Mirrors Go MsgTraceBase struct in server/msgtrace.go.
+// ============================================================================
+
+///
+/// Common base fields shared by all concrete message-trace event types.
+/// Mirrors Go MsgTraceBase struct in server/msgtrace.go.
+///
+public class MsgTraceBase : IMsgTrace
+{
+ [JsonPropertyName("type")]
+ public string Type { get; set; } = string.Empty;
+
+ [JsonPropertyName("ts")]
+ public DateTime Timestamp { get; set; }
+
+ ///
+ public virtual string Typ() => Type;
+}
+
+// ============================================================================
+// MsgTraceIngress — client / route / gateway / leaf connection ingress event
+// Mirrors Go MsgTraceIngress struct in server/msgtrace.go.
+// ============================================================================
+
+///
+/// Records the point at which a message was received by the server from a
+/// client, route, gateway, or leaf connection.
+/// Mirrors Go MsgTraceIngress struct in server/msgtrace.go.
+///
+public sealed class MsgTraceIngress : MsgTraceBase
+{
+ [JsonPropertyName("kind")]
+ public int Kind { get; set; }
+
+ [JsonPropertyName("cid")]
+ public ulong Cid { get; set; }
+
+ [JsonPropertyName("name")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? Name { get; set; }
+
+ [JsonPropertyName("acc")]
+ public string Account { get; set; } = string.Empty;
+
+ [JsonPropertyName("subj")]
+ public string Subject { get; set; } = string.Empty;
+
+ [JsonPropertyName("error")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? Error { get; set; }
+}
+
+// ============================================================================
+// MsgTraceSubjectMapping — subject-mapping rewrite event
+// Mirrors Go MsgTraceSubjectMapping struct in server/msgtrace.go.
+// ============================================================================
+
+///
+/// Records a subject-mapping rewrite applied to an in-flight message.
+/// Mirrors Go MsgTraceSubjectMapping struct in server/msgtrace.go.
+///
+public sealed class MsgTraceSubjectMapping : MsgTraceBase
+{
+ [JsonPropertyName("to")]
+ public string MappedTo { get; set; } = string.Empty;
+}
+
+// ============================================================================
+// MsgTraceStreamExport — stream export / cross-account delivery event
+// Mirrors Go MsgTraceStreamExport struct in server/msgtrace.go.
+// ============================================================================
+
+///
+/// Records delivery of a message to a stream-export destination account.
+/// Mirrors Go MsgTraceStreamExport struct in server/msgtrace.go.
+///
+public sealed class MsgTraceStreamExport : MsgTraceBase
+{
+ [JsonPropertyName("acc")]
+ public string Account { get; set; } = string.Empty;
+
+ [JsonPropertyName("to")]
+ public string To { get; set; } = string.Empty;
+}
+
+// ============================================================================
+// MsgTraceServiceImport — service import routing event
+// Mirrors Go MsgTraceServiceImport struct in server/msgtrace.go.
+// ============================================================================
+
+///
+/// Records routing of a message via a service-import from one account to
+/// another.
+/// Mirrors Go MsgTraceServiceImport struct in server/msgtrace.go.
+///
+public sealed class MsgTraceServiceImport : MsgTraceBase
+{
+ [JsonPropertyName("acc")]
+ public string Account { get; set; } = string.Empty;
+
+ [JsonPropertyName("from")]
+ public string From { get; set; } = string.Empty;
+
+ [JsonPropertyName("to")]
+ public string To { get; set; } = string.Empty;
+}
+
+// ============================================================================
+// MsgTraceJetStream — JetStream storage event
+// Mirrors Go MsgTraceJetStream struct in server/msgtrace.go.
+// ============================================================================
+
+///
+/// Records the attempt (and outcome) of storing or delivering a message
+/// to a JetStream stream.
+/// Mirrors Go MsgTraceJetStream struct in server/msgtrace.go.
+///
+public sealed class MsgTraceJetStream : MsgTraceBase
+{
+ [JsonPropertyName("stream")]
+ public string Stream { get; set; } = string.Empty;
+
+ [JsonPropertyName("subject")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? Subject { get; set; }
+
+ [JsonPropertyName("nointerest")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
+ public bool NoInterest { get; set; }
+
+ [JsonPropertyName("error")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? Error { get; set; }
+}
+
+// ============================================================================
+// MsgTraceEgress — outbound delivery event
+// Mirrors Go MsgTraceEgress struct in server/msgtrace.go.
+// ============================================================================
+
+///
+/// Records the outbound delivery of a message to a subscriber, route,
+/// gateway, or leaf connection.
+/// Mirrors Go MsgTraceEgress struct in server/msgtrace.go.
+///
+public sealed class MsgTraceEgress : MsgTraceBase
+{
+ [JsonPropertyName("kind")]
+ public int Kind { get; set; }
+
+ [JsonPropertyName("cid")]
+ public ulong Cid { get; set; }
+
+ [JsonPropertyName("name")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? Name { get; set; }
+
+ [JsonPropertyName("hop")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? Hop { get; set; }
+
+ [JsonPropertyName("acc")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? Account { get; set; }
+
+ [JsonPropertyName("sub")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? Subscription { get; set; }
+
+ [JsonPropertyName("queue")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? Queue { get; set; }
+
+ [JsonPropertyName("error")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? Error { get; set; }
+
+ ///
+ /// Optional link to the produced by the remote
+ /// server that received this egress message (route/leaf/gateway hop).
+ /// Not serialised. Mirrors Go Link *MsgTraceEvent.
+ ///
+ [JsonIgnore]
+ public MsgTraceEvent? Link { get; set; }
+}
+
+// ============================================================================
+// MsgTraceEvents — polymorphic list with custom JSON deserialiser
+// Mirrors Go MsgTraceEvents []MsgTrace and its UnmarshalJSON in msgtrace.go.
+// ============================================================================
+
+///
+/// Custom JSON converter that deserialises a MsgTraceEvents JSON array
+/// into the correct concrete subtype, using the
+/// "type" discriminator field.
+/// Mirrors Go MsgTraceEvents.UnmarshalJSON in server/msgtrace.go.
+///
+public sealed class MsgTraceEventsConverter : JsonConverter>
+{
+ private static readonly Dictionary> Factories = new()
+ {
+ ["in"] = e => e.Deserialize()!,
+ ["sm"] = e => e.Deserialize()!,
+ ["se"] = e => e.Deserialize()!,
+ ["si"] = e => e.Deserialize()!,
+ ["js"] = e => e.Deserialize()!,
+ ["eg"] = e => e.Deserialize()!,
+ };
+
+ public override List Read(
+ ref Utf8JsonReader reader,
+ Type typeToConvert,
+ JsonSerializerOptions options)
+ {
+ var result = new List();
+ using var doc = JsonDocument.ParseValue(ref reader);
+
+ foreach (var element in doc.RootElement.EnumerateArray())
+ {
+ if (!element.TryGetProperty("type", out var typeProp))
+ throw new JsonException("MsgTrace element missing 'type' field.");
+
+ var typeStr = typeProp.GetString() ?? string.Empty;
+
+ if (!Factories.TryGetValue(typeStr, out var factory))
+ throw new JsonException($"Unknown MsgTrace type '{typeStr}'.");
+
+ result.Add(factory(element));
+ }
+
+ return result;
+ }
+
+ public override void Write(
+ Utf8JsonWriter writer,
+ List value,
+ JsonSerializerOptions options)
+ {
+ writer.WriteStartArray();
+ foreach (var item in value)
+ JsonSerializer.Serialize(writer, item, item.GetType(), options);
+ writer.WriteEndArray();
+ }
+}
+
+// ============================================================================
+// MsgTraceRequest — the original request metadata included in a trace event
+// Mirrors Go MsgTraceRequest struct in server/msgtrace.go.
+// ============================================================================
+
+///
+/// Captures the headers and size of the original message that triggered a
+/// trace event.
+/// Mirrors Go MsgTraceRequest struct in server/msgtrace.go.
+///
+public sealed class MsgTraceRequest
+{
+ ///
+ /// Original message headers, preserving header-name casing.
+ /// Mirrors Go Header map[string][]string (not http.Header, so casing is preserved).
+ ///
+ [JsonPropertyName("header")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public Dictionary>? Header { get; set; }
+
+ [JsonPropertyName("msgsize")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
+ public int MsgSize { get; set; }
+}
+
+// ============================================================================
+// MsgTraceEvent — top-level trace event published to the trace destination
+// Mirrors Go MsgTraceEvent struct in server/msgtrace.go.
+// ============================================================================
+
+///
+/// The top-level message-trace advisory published to the trace destination
+/// subject. Contains server identity, the original request metadata, the
+/// hop count, and the ordered list of trace events.
+/// Mirrors Go MsgTraceEvent struct in server/msgtrace.go.
+///
+public sealed class MsgTraceEvent
+{
+ [JsonPropertyName("server")]
+ public ServerInfo Server { get; set; } = new();
+
+ [JsonPropertyName("request")]
+ public MsgTraceRequest Request { get; set; } = new();
+
+ [JsonPropertyName("hops")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
+ public int Hops { get; set; }
+
+ [JsonPropertyName("events")]
+ [JsonConverter(typeof(MsgTraceEventsConverter))]
+ public List Events { get; set; } = [];
+
+ // ---- Convenience accessors (mirrors Go helper methods on MsgTraceEvent) ----
+
+ ///
+ /// Returns the first event if it is a , else null.
+ /// Mirrors Go MsgTraceEvent.Ingress().
+ ///
+ public MsgTraceIngress? Ingress() =>
+ Events.Count > 0 ? Events[0] as MsgTraceIngress : null;
+
+ ///
+ /// Returns the first in the event list, or null.
+ /// Mirrors Go MsgTraceEvent.SubjectMapping().
+ ///
+ public MsgTraceSubjectMapping? SubjectMapping() =>
+ Events.OfType().FirstOrDefault();
+
+ ///
+ /// Returns all events.
+ /// Mirrors Go MsgTraceEvent.StreamExports().
+ ///
+ public IReadOnlyList StreamExports() =>
+ Events.OfType().ToList();
+
+ ///
+ /// Returns all events.
+ /// Mirrors Go MsgTraceEvent.ServiceImports().
+ ///
+ public IReadOnlyList ServiceImports() =>
+ Events.OfType().ToList();
+
+ ///
+ /// Returns the first event, or null.
+ /// Mirrors Go MsgTraceEvent.JetStream().
+ ///
+ public MsgTraceJetStream? JetStream() =>
+ Events.OfType().FirstOrDefault();
+
+ ///
+ /// Returns all events.
+ /// Mirrors Go MsgTraceEvent.Egresses().
+ ///
+ public IReadOnlyList Egresses() =>
+ Events.OfType().ToList();
+}
diff --git a/dotnet/src/ZB.MOM.NatsNet.Server/Monitor/MonitorSortOptions.cs b/dotnet/src/ZB.MOM.NatsNet.Server/Monitor/MonitorSortOptions.cs
new file mode 100644
index 0000000..9cc1167
--- /dev/null
+++ b/dotnet/src/ZB.MOM.NatsNet.Server/Monitor/MonitorSortOptions.cs
@@ -0,0 +1,294 @@
+// Copyright 2013-2026 The NATS Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Adapted from server/monitor_sort_opts.go in the NATS server Go source.
+
+using System.Text.Json.Serialization;
+
+namespace ZB.MOM.NatsNet.Server;
+
+// ============================================================================
+// SortOpt — string wrapper type for connection-list sort options
+// Mirrors Go SortOpt string in server/monitor_sort_opts.go.
+// ============================================================================
+
+///
+/// A strongly-typed sort option for .
+/// Wraps a raw string value corresponding to the JSON sort key.
+/// Mirrors Go SortOpt string in server/monitor_sort_opts.go.
+///
+public sealed class SortOpt
+{
+ private readonly string _value;
+
+ private SortOpt(string value) => _value = value;
+
+ /// Returns the raw sort-option string value.
+ public override string ToString() => _value;
+
+ /// Allows implicit conversion from a string literal.
+ public static implicit operator SortOpt(string value) => new(value);
+
+ /// Allows implicit conversion back to a plain string.
+ public static implicit operator string(SortOpt opt) => opt._value;
+
+ public override bool Equals(object? obj) =>
+ obj is SortOpt other && _value == other._value;
+
+ public override int GetHashCode() => _value.GetHashCode();
+
+ // ---- Well-known sort-option constants ----
+ // Mirrors Go const block in monitor_sort_opts.go.
+
+ /// Sort by connection ID (ascending). Mirrors Go ByCid = "cid".
+ public static readonly SortOpt ByCid = new("cid");
+
+ /// Sort by connection start time (same as ByCid). Mirrors Go ByStart = "start".
+ public static readonly SortOpt ByStart = new("start");
+
+ /// Sort by number of subscriptions (descending). Mirrors Go BySubs = "subs".
+ public static readonly SortOpt BySubs = new("subs");
+
+ /// Sort by pending bytes waiting to be sent (descending). Mirrors Go ByPending = "pending".
+ public static readonly SortOpt ByPending = new("pending");
+
+ /// Sort by number of outbound messages (descending). Mirrors Go ByOutMsgs = "msgs_to".
+ public static readonly SortOpt ByOutMsgs = new("msgs_to");
+
+ /// Sort by number of inbound messages (descending). Mirrors Go ByInMsgs = "msgs_from".
+ public static readonly SortOpt ByInMsgs = new("msgs_from");
+
+ /// Sort by bytes sent (descending). Mirrors Go ByOutBytes = "bytes_to".
+ public static readonly SortOpt ByOutBytes = new("bytes_to");
+
+ /// Sort by bytes received (descending). Mirrors Go ByInBytes = "bytes_from".
+ public static readonly SortOpt ByInBytes = new("bytes_from");
+
+ /// Sort by last activity time (descending). Mirrors Go ByLast = "last".
+ public static readonly SortOpt ByLast = new("last");
+
+ /// Sort by idle duration (descending). Mirrors Go ByIdle = "idle".
+ public static readonly SortOpt ByIdle = new("idle");
+
+ /// Sort by uptime (descending). Mirrors Go ByUptime = "uptime".
+ public static readonly SortOpt ByUptime = new("uptime");
+
+ /// Sort by stop time — only valid on closed connections. Mirrors Go ByStop = "stop".
+ public static readonly SortOpt ByStop = new("stop");
+
+ /// Sort by close reason — only valid on closed connections. Mirrors Go ByReason = "reason".
+ public static readonly SortOpt ByReason = new("reason");
+
+ /// Sort by round-trip time (descending). Mirrors Go ByRTT = "rtt".
+ public static readonly SortOpt ByRtt = new("rtt");
+
+ private static readonly HashSet ValidValues =
+ [
+ "", "cid", "start", "subs", "pending",
+ "msgs_to", "msgs_from", "bytes_to", "bytes_from",
+ "last", "idle", "uptime", "stop", "reason", "rtt"
+ ];
+
+ ///
+ /// Returns true if this sort option is a recognised value.
+ /// Mirrors Go SortOpt.IsValid() in monitor_sort_opts.go.
+ ///
+ public bool IsValid() => ValidValues.Contains(_value);
+}
+
+// ============================================================================
+// ConnInfos — sortable list wrapper for ConnInfo pointers
+// Mirrors Go ConnInfos []*ConnInfo in monitor_sort_opts.go.
+// ============================================================================
+
+///
+/// A list of objects that can be sorted using one of
+/// the SortBy* comparers defined in this file.
+/// Mirrors Go ConnInfos []*ConnInfo in server/monitor_sort_opts.go.
+///
+public sealed class ConnInfos : List
+{
+ public ConnInfos() { }
+ public ConnInfos(IEnumerable items) : base(items) { }
+}
+
+// ============================================================================
+// IComparer implementations — one per sort option
+// Each class mirrors the corresponding Less() method in monitor_sort_opts.go.
+// ============================================================================
+
+/// Sort by connection ID (ascending). Mirrors Go SortByCid.
+public sealed class SortByCid : IComparer
+{
+ public static readonly SortByCid Instance = new();
+ public int Compare(ConnInfo? x, ConnInfo? y)
+ {
+ if (x is null || y is null) return 0;
+ return x.Cid.CompareTo(y.Cid);
+ }
+}
+
+/// Sort by number of subscriptions (ascending for underlying sort; caller reverses if needed).
+/// Mirrors Go SortBySubs.
+public sealed class SortBySubs : IComparer
+{
+ public static readonly SortBySubs Instance = new();
+ public int Compare(ConnInfo? x, ConnInfo? y)
+ {
+ if (x is null || y is null) return 0;
+ return x.NumSubs.CompareTo(y.NumSubs);
+ }
+}
+
+/// Sort by pending bytes. Mirrors Go SortByPending.
+public sealed class SortByPending : IComparer
+{
+ public static readonly SortByPending Instance = new();
+ public int Compare(ConnInfo? x, ConnInfo? y)
+ {
+ if (x is null || y is null) return 0;
+ return x.Pending.CompareTo(y.Pending);
+ }
+}
+
+/// Sort by outbound message count. Mirrors Go SortByOutMsgs.
+public sealed class SortByOutMsgs : IComparer
+{
+ public static readonly SortByOutMsgs Instance = new();
+ public int Compare(ConnInfo? x, ConnInfo? y)
+ {
+ if (x is null || y is null) return 0;
+ return x.OutMsgs.CompareTo(y.OutMsgs);
+ }
+}
+
+/// Sort by inbound message count. Mirrors Go SortByInMsgs.
+public sealed class SortByInMsgs : IComparer
+{
+ public static readonly SortByInMsgs Instance = new();
+ public int Compare(ConnInfo? x, ConnInfo? y)
+ {
+ if (x is null || y is null) return 0;
+ return x.InMsgs.CompareTo(y.InMsgs);
+ }
+}
+
+/// Sort by outbound bytes. Mirrors Go SortByOutBytes.
+public sealed class SortByOutBytes : IComparer
+{
+ public static readonly SortByOutBytes Instance = new();
+ public int Compare(ConnInfo? x, ConnInfo? y)
+ {
+ if (x is null || y is null) return 0;
+ return x.OutBytes.CompareTo(y.OutBytes);
+ }
+}
+
+/// Sort by inbound bytes. Mirrors Go SortByInBytes.
+public sealed class SortByInBytes : IComparer
+{
+ public static readonly SortByInBytes Instance = new();
+ public int Compare(ConnInfo? x, ConnInfo? y)
+ {
+ if (x is null || y is null) return 0;
+ return x.InBytes.CompareTo(y.InBytes);
+ }
+}
+
+/// Sort by last activity timestamp. Mirrors Go SortByLast.
+public sealed class SortByLast : IComparer
+{
+ public static readonly SortByLast Instance = new();
+ public int Compare(ConnInfo? x, ConnInfo? y)
+ {
+ if (x is null || y is null) return 0;
+ return x.LastActivity.CompareTo(y.LastActivity);
+ }
+}
+
+///
+/// Sort by idle duration (time since last activity), relative to a supplied
+/// reference time. Mirrors Go SortByIdle.
+///
+public sealed class SortByIdle : IComparer
+{
+ private readonly DateTime _now;
+
+ public SortByIdle(DateTime now) => _now = now;
+
+ public int Compare(ConnInfo? x, ConnInfo? y)
+ {
+ if (x is null || y is null) return 0;
+ var idleX = _now - x.LastActivity;
+ var idleY = _now - y.LastActivity;
+ return idleX.CompareTo(idleY);
+ }
+}
+
+///
+/// Sort by uptime (time the connection has been open), relative to a supplied
+/// reference time. Mirrors Go SortByUptime.
+///
+public sealed class SortByUptime : IComparer
+{
+ private readonly DateTime _now;
+
+ public SortByUptime(DateTime now) => _now = now;
+
+ public int Compare(ConnInfo? x, ConnInfo? y)
+ {
+ if (x is null || y is null) return 0;
+ var uptimeX = (x.Stop is null || x.Stop == default) ? _now - x.Start : x.Stop.Value - x.Start;
+ var uptimeY = (y.Stop is null || y.Stop == default) ? _now - y.Start : y.Stop.Value - y.Start;
+ return uptimeX.CompareTo(uptimeY);
+ }
+}
+
+/// Sort by stop time (closed connections only). Mirrors Go SortByStop.
+public sealed class SortByStop : IComparer
+{
+ public static readonly SortByStop Instance = new();
+ public int Compare(ConnInfo? x, ConnInfo? y)
+ {
+ if (x is null || y is null) return 0;
+ // If either stop is null treat as zero (shouldn't happen for closed-only queries)
+ var stopX = x.Stop ?? DateTime.MinValue;
+ var stopY = y.Stop ?? DateTime.MinValue;
+ return stopX.CompareTo(stopY);
+ }
+}
+
+/// Sort by close reason string. Mirrors Go SortByReason.
+public sealed class SortByReason : IComparer
+{
+ public static readonly SortByReason Instance = new();
+ public int Compare(ConnInfo? x, ConnInfo? y)
+ {
+ if (x is null || y is null) return 0;
+ return string.Compare(x.Reason, y.Reason, StringComparison.Ordinal);
+ }
+}
+
+///
+/// Sort by round-trip time (nanoseconds, internal field).
+/// Mirrors Go SortByRTT.
+///
+public sealed class SortByRtt : IComparer
+{
+ public static readonly SortByRtt Instance = new();
+ public int Compare(ConnInfo? x, ConnInfo? y)
+ {
+ if (x is null || y is null) return 0;
+ return x.RttNanos.CompareTo(y.RttNanos);
+ }
+}
diff --git a/dotnet/src/ZB.MOM.NatsNet.Server/Monitor/MonitorTypes.cs b/dotnet/src/ZB.MOM.NatsNet.Server/Monitor/MonitorTypes.cs
new file mode 100644
index 0000000..0135ada
--- /dev/null
+++ b/dotnet/src/ZB.MOM.NatsNet.Server/Monitor/MonitorTypes.cs
@@ -0,0 +1,387 @@
+// Copyright 2013-2026 The NATS Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Adapted from server/monitor.go in the NATS server Go source.
+
+using System.Text.Json.Serialization;
+
+namespace ZB.MOM.NatsNet.Server;
+
+// ============================================================================
+// Monitor list size defaults
+// Mirrors Go const block near top of monitor.go.
+// ============================================================================
+
+///
+/// Default sizes for monitoring API response lists.
+/// Mirrors Go constants in server/monitor.go.
+///
+public static class MonitorDefaults
+{
+ /// Default maximum number of connection entries returned. Mirrors Go DefaultConnListSize = 1024.
+ public const int DefaultConnListSize = 1024;
+
+ /// Default maximum number of subscription entries returned. Mirrors Go DefaultSubListSize = 1024.
+ public const int DefaultSubListSize = 1024;
+}
+
+// ============================================================================
+// ConnState — connection state filter for Connz queries
+// Mirrors Go ConnState and its iota constants in monitor.go.
+// ============================================================================
+
+///
+/// Filter applied to connection-list queries to select open, closed, or
+/// all connections.
+/// Mirrors Go ConnState in server/monitor.go.
+///
+public enum ConnState
+{
+ /// Only return open (active) connections. Mirrors Go ConnOpen = 0.
+ ConnOpen = 0,
+
+ /// Only return closed connections. Mirrors Go ConnClosed.
+ ConnClosed = 1,
+
+ /// Return all connections, open or closed. Mirrors Go ConnAll.
+ ConnAll = 2,
+}
+
+// ============================================================================
+// ConnzOptions — query options for the Connz endpoint
+// Mirrors Go ConnzOptions struct in server/monitor.go.
+// ============================================================================
+
+///
+/// Options that control the output of a Connz monitoring query.
+/// Mirrors Go ConnzOptions struct in server/monitor.go.
+///
+public sealed class ConnzOptions
+{
+ ///
+ /// How to sort results. Only ByCid is ascending; all others are
+ /// descending. Mirrors Go Sort SortOpt.
+ ///
+ [JsonPropertyName("sort")]
+ public SortOpt Sort { get; set; } = SortOpt.ByCid;
+
+ /// When true, usernames are included in results. Mirrors Go Username bool.
+ [JsonPropertyName("auth")]
+ public bool Username { get; set; }
+
+ /// When true, subscription subjects are listed. Mirrors Go Subscriptions bool.
+ [JsonPropertyName("subscriptions")]
+ public bool Subscriptions { get; set; }
+
+ /// When true, verbose subscription detail is included. Mirrors Go SubscriptionsDetail bool.
+ [JsonPropertyName("subscriptions_detail")]
+ public bool SubscriptionsDetail { get; set; }
+
+ /// Zero-based offset for pagination. Mirrors Go Offset int.
+ [JsonPropertyName("offset")]
+ public int Offset { get; set; }
+
+ /// Maximum number of connections to return. Mirrors Go Limit int.
+ [JsonPropertyName("limit")]
+ public int Limit { get; set; }
+
+ /// Filter for a specific client connection by CID. Mirrors Go CID uint64.
+ [JsonPropertyName("cid")]
+ public ulong Cid { get; set; }
+
+ /// Filter for a specific MQTT client ID. Mirrors Go MQTTClient string.
+ [JsonPropertyName("mqtt_client")]
+ public string MqttClient { get; set; } = string.Empty;
+
+ /// Connection state filter. Mirrors Go State ConnState.
+ [JsonPropertyName("state")]
+ public ConnState State { get; set; } = ConnState.ConnOpen;
+
+ /// Filter by username. Mirrors Go User string.
+ [JsonPropertyName("user")]
+ public string User { get; set; } = string.Empty;
+
+ /// Filter by account name. Mirrors Go Account string.
+ [JsonPropertyName("acc")]
+ public string Account { get; set; } = string.Empty;
+
+ /// Filter by subject interest (requires Account filter). Mirrors Go FilterSubject string.
+ [JsonPropertyName("filter_subject")]
+ public string FilterSubject { get; set; } = string.Empty;
+}
+
+// ============================================================================
+// Connz — top-level connection list monitoring response
+// Mirrors Go Connz struct in server/monitor.go.
+// ============================================================================
+
+///
+/// Top-level response type for the /connz monitoring endpoint.
+/// Contains the current connection list and pagination metadata.
+/// Mirrors Go Connz struct in server/monitor.go.
+///
+public sealed class Connz
+{
+ [JsonPropertyName("server_id")]
+ public string Id { get; set; } = string.Empty;
+
+ [JsonPropertyName("now")]
+ public DateTime Now { get; set; }
+
+ [JsonPropertyName("num_connections")]
+ public int NumConns { get; set; }
+
+ [JsonPropertyName("total")]
+ public int Total { get; set; }
+
+ [JsonPropertyName("offset")]
+ public int Offset { get; set; }
+
+ [JsonPropertyName("limit")]
+ public int Limit { get; set; }
+
+ [JsonPropertyName("connections")]
+ public List Conns { get; set; } = [];
+}
+
+// ============================================================================
+// ConnInfo — per-connection detail record
+// Mirrors Go ConnInfo struct in server/monitor.go.
+// ============================================================================
+
+///
+/// Detailed information about a single client connection, as returned by the
+/// /connz monitoring endpoint.
+/// Mirrors Go ConnInfo struct in server/monitor.go.
+///
+public sealed class ConnInfo
+{
+ [JsonPropertyName("cid")]
+ public ulong Cid { get; set; }
+
+ [JsonPropertyName("kind")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? Kind { get; set; }
+
+ [JsonPropertyName("type")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? Type { get; set; }
+
+ [JsonPropertyName("ip")]
+ public string Ip { get; set; } = string.Empty;
+
+ [JsonPropertyName("port")]
+ public int Port { get; set; }
+
+ [JsonPropertyName("start")]
+ public DateTime Start { get; set; }
+
+ [JsonPropertyName("last_activity")]
+ public DateTime LastActivity { get; set; }
+
+ [JsonPropertyName("stop")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public DateTime? Stop { get; set; }
+
+ [JsonPropertyName("reason")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? Reason { get; set; }
+
+ [JsonPropertyName("rtt")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? Rtt { get; set; }
+
+ [JsonPropertyName("uptime")]
+ public string Uptime { get; set; } = string.Empty;
+
+ [JsonPropertyName("idle")]
+ public string Idle { get; set; } = string.Empty;
+
+ [JsonPropertyName("pending_bytes")]
+ public int Pending { get; set; }
+
+ [JsonPropertyName("in_msgs")]
+ public long InMsgs { get; set; }
+
+ [JsonPropertyName("out_msgs")]
+ public long OutMsgs { get; set; }
+
+ [JsonPropertyName("in_bytes")]
+ public long InBytes { get; set; }
+
+ [JsonPropertyName("out_bytes")]
+ public long OutBytes { get; set; }
+
+ [JsonPropertyName("stalls")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
+ public long Stalls { get; set; }
+
+ [JsonPropertyName("subscriptions")]
+ public uint NumSubs { get; set; }
+
+ [JsonPropertyName("name")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? Name { get; set; }
+
+ [JsonPropertyName("lang")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? Lang { get; set; }
+
+ [JsonPropertyName("version")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? Version { get; set; }
+
+ [JsonPropertyName("tls_version")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? TlsVersion { get; set; }
+
+ [JsonPropertyName("tls_cipher_suite")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? TlsCipher { get; set; }
+
+ [JsonPropertyName("tls_peer_certs")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public List? TlsPeerCerts { get; set; }
+
+ [JsonPropertyName("tls_first")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
+ public bool TlsFirst { get; set; }
+
+ [JsonPropertyName("authorized_user")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? AuthorizedUser { get; set; }
+
+ [JsonPropertyName("account")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? Account { get; set; }
+
+ [JsonPropertyName("subscriptions_list")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public List? Subs { get; set; }
+
+ [JsonPropertyName("subscriptions_list_detail")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public List? SubsDetail { get; set; }
+
+ [JsonPropertyName("jwt")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? Jwt { get; set; }
+
+ [JsonPropertyName("issuer_key")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? IssuerKey { get; set; }
+
+ [JsonPropertyName("name_tag")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? NameTag { get; set; }
+
+ [JsonPropertyName("tags")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string[]? Tags { get; set; }
+
+ [JsonPropertyName("mqtt_client")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? MqttClient { get; set; }
+
+ [JsonPropertyName("proxy")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public ProxyInfo? Proxy { get; set; }
+
+ ///
+ /// Internal field used for fast RTT-based sorting.
+ /// Mirrors Go rtt int64 unexported field in ConnInfo.
+ /// Not serialised.
+ ///
+ [JsonIgnore]
+ internal long RttNanos { get; set; }
+}
+
+// ============================================================================
+// ProxyInfo — proxy connection metadata
+// Mirrors Go ProxyInfo struct in server/monitor.go.
+// ============================================================================
+
+///
+/// Information about a proxied connection (e.g. HAProxy PROXY protocol).
+/// Mirrors Go ProxyInfo struct in server/monitor.go.
+///
+public sealed class ProxyInfo
+{
+ [JsonPropertyName("key")]
+ public string Key { get; set; } = string.Empty;
+}
+
+// ============================================================================
+// TlsPeerCert — TLS peer certificate summary
+// Mirrors Go TLSPeerCert struct in server/monitor.go.
+// ============================================================================
+
+///
+/// Basic information about a TLS peer certificate.
+/// Mirrors Go TLSPeerCert struct in server/monitor.go.
+///
+public sealed class TlsPeerCert
+{
+ [JsonPropertyName("subject")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? Subject { get; set; }
+
+ [JsonPropertyName("spki_sha256")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? SubjectPkiSha256 { get; set; }
+
+ [JsonPropertyName("cert_sha256")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? CertSha256 { get; set; }
+}
+
+// ============================================================================
+// SubDetail — verbose subscription information
+// Mirrors Go SubDetail struct in server/monitor.go (line ~961).
+// ============================================================================
+
+///
+/// Verbose information about a single subscription, included in detailed
+/// connection or account monitoring responses.
+/// Mirrors Go SubDetail struct in server/monitor.go.
+///
+public sealed class SubDetail
+{
+ [JsonPropertyName("account")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? Account { get; set; }
+
+ [JsonPropertyName("account_tag")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? AccountTag { get; set; }
+
+ [JsonPropertyName("subject")]
+ public string Subject { get; set; } = string.Empty;
+
+ [JsonPropertyName("qgroup")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? Queue { get; set; }
+
+ [JsonPropertyName("sid")]
+ public string Sid { get; set; } = string.Empty;
+
+ [JsonPropertyName("msgs")]
+ public long Msgs { get; set; }
+
+ [JsonPropertyName("max")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
+ public long Max { get; set; }
+
+ [JsonPropertyName("cid")]
+ public ulong Cid { get; set; }
+}
diff --git a/dotnet/src/ZB.MOM.NatsNet.Server/NatsServer.cs b/dotnet/src/ZB.MOM.NatsNet.Server/NatsServer.cs
index bcef42a..73b5dfe 100644
--- a/dotnet/src/ZB.MOM.NatsNet.Server/NatsServer.cs
+++ b/dotnet/src/ZB.MOM.NatsNet.Server/NatsServer.cs
@@ -57,7 +57,7 @@ public sealed partial class NatsServer : INatsServer
private readonly ServerStats _stats = new();
private readonly SlowConsumerStats _scStats = new();
- private readonly StaleConnectionStats _staleStats = new();
+ private readonly InternalStaleStats _staleStats = new();
// =========================================================================
// Core identity
diff --git a/dotnet/src/ZB.MOM.NatsNet.Server/NatsServerTypes.cs b/dotnet/src/ZB.MOM.NatsNet.Server/NatsServerTypes.cs
index 5f73d89..29daa35 100644
--- a/dotnet/src/ZB.MOM.NatsNet.Server/NatsServerTypes.cs
+++ b/dotnet/src/ZB.MOM.NatsNet.Server/NatsServerTypes.cs
@@ -122,10 +122,12 @@ internal sealed class SlowConsumerStats
}
///
-/// Per-kind stale-connection counters (atomic).
+/// Per-kind stale-connection counters (atomic, internal use only).
/// Mirrors Go embedded staleStats in server.go.
+/// NOTE: The public JSON-serialisable monitoring equivalent is StaleConnectionStats
+/// in Events/EventTypes.cs.
///
-internal sealed class StaleConnectionStats
+internal sealed class InternalStaleStats
{
public long Clients;
public long Routes;
@@ -204,12 +206,7 @@ public static class CompressionMode
// These stubs will be replaced with full implementations in later sessions.
// They are declared here to allow the NatsServer class to compile.
-/// Stub for the system/internal messaging state (session 12).
-internal sealed class InternalState
-{
- public Account? Account { get; set; }
- // Full implementation in session 12 (events.go)
-}
+// InternalState is now fully defined in Events/EventTypes.cs (session 12).
/// Stub for JetStream state pointer (session 19).
internal sealed class JetStreamState { }
diff --git a/porting.db b/porting.db
index 685e0db..22ef20a 100644
Binary files a/porting.db and b/porting.db differ
diff --git a/reports/current.md b/reports/current.md
index b456a35..3fd230f 100644
--- a/reports/current.md
+++ b/reports/current.md
@@ -1,6 +1,6 @@
# NATS .NET Porting Status Report
-Generated: 2026-02-26 20:37:09 UTC
+Generated: 2026-02-26 20:46:14 UTC
## Modules (12 total)
@@ -13,9 +13,9 @@ Generated: 2026-02-26 20:37:09 UTC
| Status | Count |
|--------|-------|
-| complete | 1075 |
+| complete | 1382 |
| n_a | 82 |
-| not_started | 2423 |
+| not_started | 2116 |
| stub | 93 |
## Unit Tests (3257 total)
@@ -36,4 +36,4 @@ Generated: 2026-02-26 20:37:09 UTC
## Overall Progress
-**1668/6942 items complete (24.0%)**
+**1975/6942 items complete (28.5%)**
diff --git a/reports/report_12a14ec.md b/reports/report_12a14ec.md
new file mode 100644
index 0000000..3fd230f
--- /dev/null
+++ b/reports/report_12a14ec.md
@@ -0,0 +1,39 @@
+# NATS .NET Porting Status Report
+
+Generated: 2026-02-26 20:46:14 UTC
+
+## Modules (12 total)
+
+| Status | Count |
+|--------|-------|
+| complete | 11 |
+| not_started | 1 |
+
+## Features (3673 total)
+
+| Status | Count |
+|--------|-------|
+| complete | 1382 |
+| n_a | 82 |
+| not_started | 2116 |
+| stub | 93 |
+
+## Unit Tests (3257 total)
+
+| Status | Count |
+|--------|-------|
+| complete | 319 |
+| n_a | 181 |
+| not_started | 2533 |
+| stub | 224 |
+
+## Library Mappings (36 total)
+
+| Status | Count |
+|--------|-------|
+| mapped | 36 |
+
+
+## Overall Progress
+
+**1975/6942 items complete (28.5%)**