diff --git a/dotnet/src/ZB.MOM.NatsNet.Server/Gateway/GatewayTypes.cs b/dotnet/src/ZB.MOM.NatsNet.Server/Gateway/GatewayTypes.cs
new file mode 100644
index 0000000..c405948
--- /dev/null
+++ b/dotnet/src/ZB.MOM.NatsNet.Server/Gateway/GatewayTypes.cs
@@ -0,0 +1,384 @@
+// Copyright 2018-2025 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/gateway.go in the NATS server Go source.
+
+using System.Threading;
+using ZB.MOM.NatsNet.Server.Internal;
+using ZB.MOM.NatsNet.Server.Internal.DataStructures;
+
+namespace ZB.MOM.NatsNet.Server;
+
+// ============================================================================
+// Session 16: Gateways
+// ============================================================================
+
+///
+/// Represents the interest mode for a given account on a gateway connection.
+/// Mirrors Go GatewayInterestMode byte iota in gateway.go.
+/// Do not change values — they are part of the wire-level gossip protocol.
+///
+public enum GatewayInterestMode : byte
+{
+ ///
+ /// Default mode: the cluster sends to a gateway unless told there is no
+ /// interest (applies to plain subscribers only).
+ ///
+ Optimistic = 0,
+
+ ///
+ /// Transitioning: the gateway has been sending too many no-interest signals
+ /// and is switching to mode for this account.
+ ///
+ Transitioning = 1,
+
+ ///
+ /// Interest-only mode: the cluster has sent all its subscription interest;
+ /// the gateway only forwards messages when explicit interest is known.
+ ///
+ InterestOnly = 2,
+
+ ///
+ /// Internal sentinel used after a cache flush; not part of the public wire enum.
+ ///
+ CacheFlushed = 3,
+}
+
+///
+/// Server-level gateway state kept on the instance.
+/// Replaces the stub that was in NatsServerTypes.cs.
+/// Mirrors Go srvGateway struct in gateway.go.
+///
+internal sealed class SrvGateway
+{
+ ///
+ /// Total number of queue subs across all remote gateways.
+ /// Accessed via Interlocked — must be 64-bit aligned.
+ ///
+ public long TotalQSubs;
+
+ private readonly ReaderWriterLockSlim _lock = new(LockRecursionPolicy.SupportsRecursion);
+
+ ///
+ /// True if both a gateway name and port are configured (immutable after init).
+ ///
+ public bool Enabled { get; set; }
+
+ /// Name of this server's gateway cluster.
+ public string Name { get; set; } = string.Empty;
+
+ /// Outbound gateway connections keyed by remote gateway name.
+ public Dictionary Out { get; set; } = new();
+
+ ///
+ /// Outbound gateway connections in RTT order, used for message routing.
+ ///
+ public List Outo { get; set; } = [];
+
+ /// Inbound gateway connections keyed by connection ID.
+ public Dictionary In { get; set; } = new();
+
+ /// Per-remote-gateway configuration, keyed by gateway name.
+ public Dictionary Remotes { get; set; } = new();
+
+ /// Reference-counted set of all gateway URLs in the cluster.
+ public RefCountedUrlSet Urls { get; set; } = new();
+
+ /// This server's own gateway URL (after random-port resolution).
+ public string Url { get; set; } = string.Empty;
+
+ /// Gateway INFO protocol object.
+ public ServerInfo? Info { get; set; }
+
+ /// Pre-marshalled INFO JSON bytes.
+ public byte[]? InfoJson { get; set; }
+
+ /// When true, reject connections from gateways not in .
+ public bool RejectUnknown { get; set; }
+
+ ///
+ /// Reply prefix bytes: "$GNR.<reserved>.<clusterHash>.<serverHash>."
+ ///
+ public byte[] ReplyPfx { get; set; } = [];
+
+ // Backward-compatibility reply prefix and hash (old "$GR." scheme)
+ public byte[] OldReplyPfx { get; set; } = [];
+ public byte[] OldHash { get; set; } = [];
+
+ // -------------------------------------------------------------------------
+ // pasi — per-account subject interest tally (protected by its own mutex)
+ // -------------------------------------------------------------------------
+
+ ///
+ /// Per-account subject-interest tally.
+ /// Outer key = account name; inner key = subject (or "subject queue" pair);
+ /// value = tally struct.
+ /// Mirrors Go's anonymous pasi embedded struct in srvGateway.
+ ///
+ private readonly Lock _pasiLock = new();
+ public Dictionary> Pasi { get; set; } = new();
+
+ public Lock PasiLock => _pasiLock;
+
+ // -------------------------------------------------------------------------
+ // Recent subscription tracking (thread-safe map)
+ // -------------------------------------------------------------------------
+
+ ///
+ /// Recent subscriptions for a given account (subject → expiry ticks).
+ /// Mirrors Go's rsubs sync.Map.
+ ///
+ public System.Collections.Concurrent.ConcurrentDictionary RSubs { get; set; } = new();
+
+ // -------------------------------------------------------------------------
+ // Other server-level gateway fields
+ // -------------------------------------------------------------------------
+
+ /// DNS resolver used before dialling gateway connections.
+ public INetResolver? Resolver { get; set; }
+
+ /// Max buffer size for sending queue-sub protocol (used in tests).
+ public int SqbSz { get; set; }
+
+ /// How long to look for a subscription match for a reply message.
+ public TimeSpan RecSubExp { get; set; }
+
+ /// Server ID hash (6 bytes) for routing mapped replies.
+ public byte[] SIdHash { get; set; } = [];
+
+ ///
+ /// Map from a route server's hashed ID (6 bytes) to the route client.
+ /// Mirrors Go's routesIDByHash sync.Map.
+ ///
+ public System.Collections.Concurrent.ConcurrentDictionary RoutesIdByHash { get; set; } = new();
+
+ ///
+ /// Gateway URLs from this server's own entry in the Gateways config block,
+ /// used for monitoring reports.
+ ///
+ public List OwnCfgUrls { get; set; } = [];
+
+ // -------------------------------------------------------------------------
+ // Lock helpers
+ // -------------------------------------------------------------------------
+
+ public void AcquireReadLock() => _lock.EnterReadLock();
+ public void ReleaseReadLock() => _lock.ExitReadLock();
+ public void AcquireWriteLock() => _lock.EnterWriteLock();
+ public void ReleaseWriteLock() => _lock.ExitWriteLock();
+}
+
+///
+/// Subject-interest tally entry. Indicates whether the key in the map is a
+/// queue subscription and how many matching subscriptions exist.
+/// Mirrors Go sitally struct in gateway.go.
+///
+internal sealed class SitAlly
+{
+ /// Number of subscriptions directly matching the subject/queue key.
+ public int N { get; set; }
+
+ /// True if this entry represents a queue subscription.
+ public bool Q { get; set; }
+}
+
+///
+/// Runtime configuration for a single remote gateway.
+/// Wraps with connection-attempt state and a lock.
+/// Mirrors Go gatewayCfg struct in gateway.go.
+///
+internal sealed class GatewayCfg
+{
+ private readonly ReaderWriterLockSlim _lock = new(LockRecursionPolicy.SupportsRecursion);
+
+ /// The raw remote-gateway options this cfg was built from.
+ public RemoteGatewayOpts? RemoteOpts { get; set; }
+
+ /// 6-byte cluster hash used for reply routing.
+ public byte[] Hash { get; set; } = [];
+
+ /// 4-byte old-style hash for backward compatibility.
+ public byte[] OldHash { get; set; } = [];
+
+ /// Map of URL string → parsed URL for this remote gateway.
+ public Dictionary Urls { get; set; } = new();
+
+ /// Number of connection attempts made so far.
+ public int ConnAttempts { get; set; }
+
+ /// TLS server name override for SNI.
+ public string TlsName { get; set; } = string.Empty;
+
+ /// True if this remote was discovered via gossip (not configured).
+ public bool Implicit { get; set; }
+
+ /// When true, monitoring should refresh the URL list on next varz inspection.
+ public bool VarzUpdateUrls { get; set; }
+
+ // Forwarded properties from RemoteGatewayOpts
+ public string Name { get => RemoteOpts?.Name ?? string.Empty; }
+
+ // -------------------------------------------------------------------------
+ // Lock helpers
+ // -------------------------------------------------------------------------
+
+ public void AcquireReadLock() => _lock.EnterReadLock();
+ public void ReleaseReadLock() => _lock.ExitReadLock();
+ public void AcquireWriteLock() => _lock.EnterWriteLock();
+ public void ReleaseWriteLock() => _lock.ExitWriteLock();
+}
+
+///
+/// Per-connection gateway state embedded in
+/// when the connection kind is Gateway.
+/// Mirrors Go gateway struct in gateway.go.
+///
+internal sealed class Gateway
+{
+ /// Name of the remote gateway cluster.
+ public string Name { get; set; } = string.Empty;
+
+ /// Configuration block for the remote gateway.
+ public GatewayCfg? Cfg { get; set; }
+
+ /// URL used for CONNECT after receiving the remote INFO (outbound only).
+ public Uri? ConnectUrl { get; set; }
+
+ ///
+ /// Per-account subject interest (outbound connection).
+ /// Maps account name → for that account.
+ /// Uses a thread-safe map because it is read from multiple goroutines.
+ ///
+ public System.Collections.Concurrent.ConcurrentDictionary? OutSim { get; set; }
+
+ ///
+ /// Per-account no-interest subjects or interest-only mode (inbound connection).
+ ///
+ public Dictionary? InSim { get; set; }
+
+ /// True if this is an outbound gateway connection.
+ public bool Outbound { get; set; }
+
+ ///
+ /// Set in the read loop without locking to record that the inbound side
+ /// sent its CONNECT protocol.
+ ///
+ public bool Connected { get; set; }
+
+ ///
+ /// True if the remote server only understands the old $GR. prefix,
+ /// not the newer $GNR. scheme.
+ ///
+ public bool UseOldPrefix { get; set; }
+
+ ///
+ /// When true the inbound side switches accounts to interest-only mode
+ /// immediately, so the outbound side can disregard optimistic mode.
+ ///
+ public bool InterestOnlyMode { get; set; }
+
+ /// Name of the remote server on this gateway connection.
+ public string RemoteName { get; set; } = string.Empty;
+}
+
+///
+/// Outbound subject-interest entry for a single account on a gateway connection.
+/// Mirrors Go outsie struct in gateway.go.
+///
+internal sealed class OutSide
+{
+ private readonly ReaderWriterLockSlim _lock = new(LockRecursionPolicy.SupportsRecursion);
+
+ /// Current interest mode for this account on the outbound gateway.
+ public GatewayInterestMode Mode { get; set; }
+
+ ///
+ /// Set of subjects for which the remote has signalled no-interest.
+ /// Null when the remote has sent all its subscriptions (interest-only mode).
+ ///
+ public HashSet? Ni { get; set; }
+
+ ///
+ /// Subscription index: contains queue subs in optimistic mode,
+ /// or all subs when has been switched.
+ ///
+ public SubscriptionIndex? Sl { get; set; }
+
+ /// Number of queue subscriptions tracked in .
+ public int Qsubs { get; set; }
+
+ // -------------------------------------------------------------------------
+ // Lock helpers
+ // -------------------------------------------------------------------------
+
+ public void AcquireReadLock() => _lock.EnterReadLock();
+ public void ReleaseReadLock() => _lock.ExitReadLock();
+ public void AcquireWriteLock() => _lock.EnterWriteLock();
+ public void ReleaseWriteLock() => _lock.ExitWriteLock();
+}
+
+///
+/// Inbound subject-interest entry for a single account on a gateway connection.
+/// Tracks subjects for which an RS- was sent to the remote, and the current mode.
+/// Mirrors Go insie struct in gateway.go.
+///
+internal sealed class InSide
+{
+ ///
+ /// Subjects for which RS- was sent to the remote (null when in interest-only mode).
+ ///
+ public HashSet? Ni { get; set; }
+
+ /// Current interest mode for this account on the inbound gateway.
+ public GatewayInterestMode Mode { get; set; }
+}
+
+///
+/// A single gateway reply-mapping entry: the mapped subject and its expiry.
+/// Mirrors Go gwReplyMap struct in gateway.go.
+///
+internal sealed class GwReplyMap
+{
+ /// The mapped (routed) subject string.
+ public string Ms { get; set; } = string.Empty;
+
+ /// Expiry expressed as (UTC).
+ public long Exp { get; set; }
+}
+
+///
+/// Gateway reply routing table and a fast-path check flag.
+/// Mirrors Go gwReplyMapping struct in gateway.go.
+///
+internal sealed class GwReplyMapping
+{
+ ///
+ /// Non-zero when the mapping table should be consulted while processing
+ /// inbound messages. Accessed via Interlocked — must be 32-bit aligned.
+ ///
+ public int Check;
+
+ /// Active reply-subject → GwReplyMap entries.
+ public Dictionary Mapping { get; set; } = new();
+
+ ///
+ /// Returns the routed subject for if a mapping
+ /// exists, otherwise returns the original subject and false.
+ /// Caller must hold any required lock before invoking.
+ ///
+ public (byte[] Subject, bool Found) Get(byte[] subject)
+ {
+ // TODO: session 16 — implement mapping lookup
+ return (subject, false);
+ }
+}
diff --git a/dotnet/src/ZB.MOM.NatsNet.Server/LeafNode/LeafNodeTypes.cs b/dotnet/src/ZB.MOM.NatsNet.Server/LeafNode/LeafNodeTypes.cs
new file mode 100644
index 0000000..2a7801f
--- /dev/null
+++ b/dotnet/src/ZB.MOM.NatsNet.Server/LeafNode/LeafNodeTypes.cs
@@ -0,0 +1,202 @@
+// Copyright 2019-2025 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/leafnode.go in the NATS server Go source.
+
+using System.Text.Json.Serialization;
+using System.Threading;
+using ZB.MOM.NatsNet.Server.Auth;
+using ZB.MOM.NatsNet.Server.Internal;
+
+namespace ZB.MOM.NatsNet.Server;
+
+// ============================================================================
+// Session 15: Leaf Nodes
+// ============================================================================
+
+///
+/// Per-connection leaf-node state embedded in
+/// when the connection kind is Leaf.
+/// Mirrors Go leaf struct in leafnode.go.
+///
+internal sealed class Leaf
+{
+ ///
+ /// Config for solicited (outbound) leaf connections; null for accepted connections.
+ ///
+ public LeafNodeCfg? Remote { get; set; }
+
+ ///
+ /// True when we are the spoke side of a hub/spoke leaf pair.
+ ///
+ public bool IsSpoke { get; set; }
+
+ ///
+ /// Cluster name of the remote server when we are a hub and the spoke is
+ /// part of a cluster.
+ ///
+ public string RemoteCluster { get; set; } = string.Empty;
+
+ /// Remote server name or ID.
+ public string RemoteServer { get; set; } = string.Empty;
+
+ /// Domain name of the remote server.
+ public string RemoteDomain { get; set; } = string.Empty;
+
+ /// Account name of the remote server.
+ public string RemoteAccName { get; set; } = string.Empty;
+
+ ///
+ /// When true, suppresses propagation of east-west interest from other leaf nodes.
+ ///
+ public bool Isolated { get; set; }
+
+ ///
+ /// Subject-interest suppression map shared with the remote side.
+ /// Key = subject, Value = interest count (positive = subscribe, negative = unsubscribe delta).
+ ///
+ public Dictionary Smap { get; set; } = new();
+
+ ///
+ /// Short-lived set of subscriptions added during initLeafNodeSmapAndSendSubs
+ /// to detect and avoid double-counting races.
+ ///
+ public HashSet? Tsub { get; set; }
+
+ /// Timer that clears after the initialization window.
+ public Timer? Tsubt { get; set; }
+
+ ///
+ /// Selected compression mode, which may differ from the server-configured mode.
+ ///
+ public string Compression { get; set; } = string.Empty;
+
+ ///
+ /// Gateway-mapped reply subscription used for GW reply routing via leaf nodes.
+ ///
+ public Subscription? GwSub { get; set; }
+}
+
+///
+/// Runtime configuration for a remote (solicited) leaf-node connection.
+/// Wraps with connection-attempt state and a
+/// reader-writer lock for concurrent access.
+/// Mirrors Go leafNodeCfg struct in leafnode.go.
+/// Replaces the stub that was in NatsServerTypes.cs.
+///
+public sealed class LeafNodeCfg
+{
+ private readonly ReaderWriterLockSlim _lock = new(LockRecursionPolicy.SupportsRecursion);
+
+ // -------------------------------------------------------------------------
+ // Embedded RemoteLeafOpts fields
+ // -------------------------------------------------------------------------
+
+ /// The raw remote options this cfg was constructed from.
+ public RemoteLeafOpts? RemoteOpts { get; set; }
+
+ // -------------------------------------------------------------------------
+ // Runtime connection-attempt fields
+ // -------------------------------------------------------------------------
+
+ /// Resolved URLs to attempt connections to.
+ public List Urls { get; set; } = [];
+
+ /// Currently selected URL from .
+ public Uri? CurUrl { get; set; }
+
+ /// TLS server name override for SNI.
+ public string TlsName { get; set; } = string.Empty;
+
+ /// Username for authentication (resolved from credentials or options).
+ public string Username { get; set; } = string.Empty;
+
+ /// Password for authentication (resolved from credentials or options).
+ public string Password { get; set; } = string.Empty;
+
+ /// Publish/subscribe permission overrides for this connection.
+ public Permissions? Perms { get; set; }
+
+ ///
+ /// Delay before the next connection attempt (e.g. during loop-detection back-off).
+ ///
+ public TimeSpan ConnDelay { get; set; }
+
+ ///
+ /// Timer used to trigger JetStream account migration for this leaf.
+ ///
+ public Timer? JsMigrateTimer { get; set; }
+
+ // -------------------------------------------------------------------------
+ // Forwarded properties from RemoteLeafOpts
+ // -------------------------------------------------------------------------
+
+ public string LocalAccount { get => RemoteOpts?.LocalAccount ?? string.Empty; }
+ public bool NoRandomize { get => RemoteOpts?.NoRandomize ?? false; }
+ public string Credentials { get => RemoteOpts?.Credentials ?? string.Empty; }
+ public bool Disabled { get => RemoteOpts?.Disabled ?? false; }
+
+ // -------------------------------------------------------------------------
+ // Lock helpers
+ // -------------------------------------------------------------------------
+
+ public void AcquireReadLock() => _lock.EnterReadLock();
+ public void ReleaseReadLock() => _lock.ExitReadLock();
+ public void AcquireWriteLock() => _lock.EnterWriteLock();
+ public void ReleaseWriteLock() => _lock.ExitWriteLock();
+}
+
+///
+/// CONNECT protocol payload sent by a leaf-node connection.
+/// Fields map 1-to-1 with the JSON tags in Go's leafConnectInfo.
+/// Mirrors Go leafConnectInfo struct in leafnode.go.
+///
+internal sealed class LeafConnectInfo
+{
+ [JsonPropertyName("version")] public string Version { get; set; } = string.Empty;
+ [JsonPropertyName("nkey")] public string Nkey { get; set; } = string.Empty;
+ [JsonPropertyName("jwt")] public string Jwt { get; set; } = string.Empty;
+ [JsonPropertyName("sig")] public string Sig { get; set; } = string.Empty;
+ [JsonPropertyName("user")] public string User { get; set; } = string.Empty;
+ [JsonPropertyName("pass")] public string Pass { get; set; } = string.Empty;
+ [JsonPropertyName("auth_token")] public string Token { get; set; } = string.Empty;
+ [JsonPropertyName("server_id")] public string Id { get; set; } = string.Empty;
+ [JsonPropertyName("domain")] public string Domain { get; set; } = string.Empty;
+ [JsonPropertyName("name")] public string Name { get; set; } = string.Empty;
+ [JsonPropertyName("is_hub")] public bool Hub { get; set; }
+ [JsonPropertyName("cluster")] public string Cluster { get; set; } = string.Empty;
+ [JsonPropertyName("headers")] public bool Headers { get; set; }
+ [JsonPropertyName("jetstream")] public bool JetStream { get; set; }
+ [JsonPropertyName("deny_pub")] public string[] DenyPub { get; set; } = [];
+ [JsonPropertyName("isolate")] public bool Isolate { get; set; }
+
+ ///
+ /// Compression mode string. The legacy boolean field was never used; this
+ /// string field uses a different JSON tag to avoid conflicts.
+ ///
+ [JsonPropertyName("compress_mode")] public string Compression { get; set; } = string.Empty;
+
+ ///
+ /// Used only to detect wrong-port connections (client connecting to leaf port).
+ ///
+ [JsonPropertyName("gateway")] public string Gateway { get; set; } = string.Empty;
+
+ /// Account name the remote is binding to on the accept side.
+ [JsonPropertyName("remote_account")] public string RemoteAccount { get; set; } = string.Empty;
+
+ ///
+ /// Protocol version sent by the soliciting side so the accepting side knows
+ /// which features are supported (e.g. message tracing).
+ ///
+ [JsonPropertyName("protocol")] public int Proto { get; set; }
+}
diff --git a/dotnet/src/ZB.MOM.NatsNet.Server/NatsServerTypes.cs b/dotnet/src/ZB.MOM.NatsNet.Server/NatsServerTypes.cs
index 29daa35..68506b3 100644
--- a/dotnet/src/ZB.MOM.NatsNet.Server/NatsServerTypes.cs
+++ b/dotnet/src/ZB.MOM.NatsNet.Server/NatsServerTypes.cs
@@ -225,11 +225,7 @@ public sealed class JetStreamConfig
public string UniqueTag { get; set; } = string.Empty;
}
-/// Stub for server gateway state (session 16).
-internal sealed class SrvGateway
-{
- public bool Enabled { get; set; }
-}
+// SrvGateway — full class is in Gateway/GatewayTypes.cs (session 16).
/// Stub for server websocket state (session 23).
internal sealed class SrvWebsocket
@@ -249,8 +245,7 @@ internal interface IOcspResponseCache { }
/// Stub for IP queue (session 02 — already ported as IpQueue).
// IpQueue is already in session 02 internals — used here via object.
-/// Stub for leaf node config (session 15).
-internal sealed class LeafNodeCfg { }
+// LeafNodeCfg — full class is in LeafNode/LeafNodeTypes.cs (session 15).
///
/// Network resolver used by .
diff --git a/dotnet/src/ZB.MOM.NatsNet.Server/Routes/RouteTypes.cs b/dotnet/src/ZB.MOM.NatsNet.Server/Routes/RouteTypes.cs
new file mode 100644
index 0000000..1449957
--- /dev/null
+++ b/dotnet/src/ZB.MOM.NatsNet.Server/Routes/RouteTypes.cs
@@ -0,0 +1,185 @@
+// Copyright 2013-2025 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/route.go in the NATS server Go source.
+
+using System.Text.Json.Serialization;
+using ZB.MOM.NatsNet.Server.Internal;
+
+namespace ZB.MOM.NatsNet.Server;
+
+// ============================================================================
+// Session 14: Routes
+// ============================================================================
+
+///
+/// Designates whether a route was explicitly configured or discovered via gossip.
+/// Mirrors Go RouteType iota in route.go.
+/// Note: Go defines Implicit=0, Explicit=1 — we keep TombStone=2 for future use.
+///
+public enum RouteType : int
+{
+ /// This route was learned from speaking to other routes.
+ Implicit = 0,
+ /// This route was explicitly configured.
+ Explicit = 1,
+ /// Reserved tombstone marker for removed routes.
+ TombStone = 2,
+}
+
+///
+/// Gossip mode constants exchanged between route servers.
+/// Mirrors the const block immediately after routeInfo in route.go.
+/// Do not change values — they are part of the wire protocol.
+///
+internal static class GossipMode
+{
+ public const byte Default = 0;
+ public const byte Disabled = 1;
+ public const byte Override = 2;
+}
+
+///
+/// Per-connection route state embedded in when the
+/// connection kind is Router.
+/// Mirrors Go route struct in route.go.
+///
+internal sealed class Route
+{
+ /// Remote server ID string.
+ public string RemoteId { get; set; } = string.Empty;
+
+ /// Remote server name.
+ public string RemoteName { get; set; } = string.Empty;
+
+ /// True if this server solicited the outbound connection.
+ public bool DidSolicit { get; set; }
+
+ /// True if the connection should be retried on failure.
+ public bool Retry { get; set; }
+
+ /// Leaf-node origin cluster flag (lnoc).
+ public bool Lnoc { get; set; }
+
+ /// Leaf-node origin cluster with unsub support (lnocu).
+ public bool Lnocu { get; set; }
+
+ /// Whether this is an explicit or implicit route.
+ public RouteType RouteType { get; set; }
+
+ /// Remote URL used to establish the connection.
+ public Uri? Url { get; set; }
+
+ /// True if the remote requires authentication.
+ public bool AuthRequired { get; set; }
+
+ /// True if the remote requires TLS.
+ public bool TlsRequired { get; set; }
+
+ /// True if JetStream is enabled on the remote.
+ public bool JetStream { get; set; }
+
+ /// List of client connect URLs advertised by the remote.
+ public List ConnectUrls { get; set; } = [];
+
+ /// List of WebSocket connect URLs advertised by the remote.
+ public List WsConnUrls { get; set; } = [];
+
+ /// Gateway URL advertised by the remote.
+ public string GatewayUrl { get; set; } = string.Empty;
+
+ /// Leaf-node URL advertised by the remote.
+ public string LeafnodeUrl { get; set; } = string.Empty;
+
+ /// Cluster hash used for routing.
+ public string Hash { get; set; } = string.Empty;
+
+ /// Server ID hash (6 bytes encoded).
+ public string IdHash { get; set; } = string.Empty;
+
+ ///
+ /// Index of this route in the s.routes[remoteID] slice.
+ /// Initialized to -1 to indicate the route has not yet been registered.
+ ///
+ public int PoolIdx { get; set; } = -1;
+
+ ///
+ /// When set, this route is pinned to a specific account and the account
+ /// name will not be included in routed protocols.
+ ///
+ public byte[]? AccName { get; set; }
+
+ /// True if this is a connection to an old server or one with pooling disabled.
+ public bool NoPool { get; set; }
+
+ ///
+ /// Selected compression mode, which may differ from the server-configured mode.
+ ///
+ public string Compression { get; set; } = string.Empty;
+
+ ///
+ /// Transient gossip mode byte sent when initiating an implicit route.
+ ///
+ public byte GossipMode { get; set; }
+
+ ///
+ /// When set in a pooling scenario, signals that the route should trigger
+ /// creation of the next pooled connection after receiving the first PONG.
+ ///
+ public RouteInfo? StartNewRoute { get; set; }
+}
+
+///
+/// Minimal descriptor used to create a new route connection, including
+/// the target URL, its type, and gossip mode.
+/// Mirrors Go routeInfo struct (the small inner type) in route.go.
+///
+internal sealed class RouteInfo
+{
+ public Uri? Url { get; set; }
+ public RouteType RouteType { get; set; }
+ public byte GossipMode { get; set; }
+}
+
+///
+/// CONNECT protocol payload exchanged between cluster servers.
+/// Fields map 1-to-1 with the JSON tags in Go's connectInfo.
+/// Mirrors Go connectInfo struct in route.go.
+///
+internal sealed class ConnectInfo
+{
+ [JsonPropertyName("echo")] public bool Echo { get; set; }
+ [JsonPropertyName("verbose")] public bool Verbose { get; set; }
+ [JsonPropertyName("pedantic")] public bool Pedantic { get; set; }
+ [JsonPropertyName("user")] public string User { get; set; } = string.Empty;
+ [JsonPropertyName("pass")] public string Pass { get; set; } = string.Empty;
+ [JsonPropertyName("tls_required")] public bool Tls { get; set; }
+ [JsonPropertyName("headers")] public bool Headers { get; set; }
+ [JsonPropertyName("name")] public string Name { get; set; } = string.Empty;
+ [JsonPropertyName("cluster")] public string Cluster { get; set; } = string.Empty;
+ [JsonPropertyName("cluster_dynamic")] public bool Dynamic { get; set; }
+ [JsonPropertyName("lnoc")] public bool Lnoc { get; set; }
+ [JsonPropertyName("lnocu")] public bool Lnocu { get; set; }
+ [JsonPropertyName("gateway")] public string Gateway { get; set; } = string.Empty;
+}
+
+///
+/// Holds a set of subscriptions for a single account, used when fanning out
+/// route subscription interest.
+/// Mirrors Go asubs struct in route.go.
+///
+internal sealed class ASubs
+{
+ public Account? Account { get; set; }
+ public List Subs { get; set; } = [];
+}
diff --git a/porting.db b/porting.db
index 22ef20a..0bb05ba 100644
Binary files a/porting.db and b/porting.db differ
diff --git a/reports/current.md b/reports/current.md
index 3fd230f..ca16166 100644
--- a/reports/current.md
+++ b/reports/current.md
@@ -1,6 +1,6 @@
# NATS .NET Porting Status Report
-Generated: 2026-02-26 20:46:14 UTC
+Generated: 2026-02-26 20:50:51 UTC
## Modules (12 total)
@@ -13,9 +13,9 @@ Generated: 2026-02-26 20:46:14 UTC
| Status | Count |
|--------|-------|
-| complete | 1382 |
+| complete | 1601 |
| n_a | 82 |
-| not_started | 2116 |
+| not_started | 1897 |
| stub | 93 |
## Unit Tests (3257 total)
@@ -36,4 +36,4 @@ Generated: 2026-02-26 20:46:14 UTC
## Overall Progress
-**1975/6942 items complete (28.5%)**
+**2194/6942 items complete (31.6%)**
diff --git a/reports/report_ce45dff.md b/reports/report_ce45dff.md
new file mode 100644
index 0000000..ca16166
--- /dev/null
+++ b/reports/report_ce45dff.md
@@ -0,0 +1,39 @@
+# NATS .NET Porting Status Report
+
+Generated: 2026-02-26 20:50:51 UTC
+
+## Modules (12 total)
+
+| Status | Count |
+|--------|-------|
+| complete | 11 |
+| not_started | 1 |
+
+## Features (3673 total)
+
+| Status | Count |
+|--------|-------|
+| complete | 1601 |
+| n_a | 82 |
+| not_started | 1897 |
+| 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
+
+**2194/6942 items complete (31.6%)**