Files
natsdotnet/gaps/gateways.md
Joseph Doherty c30e67a69d Fix E2E test gaps and add comprehensive E2E + parity test suites
- Fix pull consumer fetch: send original stream subject in HMSG (not inbox)
  so NATS client distinguishes data messages from control messages
- Fix MaxAge expiry: add background timer in StreamManager for periodic pruning
- Fix JetStream wire format: Go-compatible anonymous objects with string enums,
  proper offset-based pagination for stream/consumer list APIs
- Add 42 E2E black-box tests (core messaging, auth, TLS, accounts, JetStream)
- Add ~1000 parity tests across all subsystems (gaps closure)
- Update gap inventory docs to reflect implementation status
2026-03-12 14:09:23 -04:00

31 KiB
Raw Blame History

Gateways — Gap Analysis

This file tracks what has and hasn't been ported from Go to .NET for the Gateways module. See stillmissing.md for the full LOC comparison across all modules.

LLM Instructions: How to Analyze This Category

Step 1: Read the Go Reference Files

Read each Go source file listed below. For every file:

  1. Extract all exported types (structs, interfaces, type aliases)
  2. Extract all exported methods on those types (receiver functions)
  3. Extract all exported standalone functions
  4. Note key constants, enums, and protocol states
  5. Note important unexported helpers that implement core logic (functions >20 lines)
  6. Pay attention to concurrency patterns (goroutines, mutexes, channels) — these map to different .NET patterns

Step 2: Read the .NET Implementation Files

Read all .cs files in the .NET directories listed below. For each Go symbol found in Step 1:

  1. Search for a matching type, method, or function in .NET
  2. If found, compare the behavior: does it handle the same edge cases? Same error paths?
  3. If partially implemented, note what's missing
  4. If not found, note it as MISSING

Step 3: Cross-Reference Tests

Compare Go test functions against .NET test methods:

  1. For each Go Test* function, check if a corresponding .NET [Fact] or [Theory] exists
  2. Note which test scenarios are covered and which are missing
  3. Check the parity DB (docs/test_parity.db) for existing mappings:
    sqlite3 docs/test_parity.db "SELECT go_test, dotnet_test, confidence FROM test_mappings tm JOIN go_tests gt ON tm.go_test_id=gt.rowid JOIN dotnet_tests dt ON tm.dotnet_test_id=dt.rowid WHERE gt.go_file LIKE '%PATTERN%'"
    

Step 4: Classify Each Item

Use these status values:

Status Meaning
PORTED Equivalent exists in .NET with matching behavior
PARTIAL .NET implementation exists but is incomplete (missing edge cases, error handling, or features)
MISSING No .NET equivalent found — needs to be ported
NOT_APPLICABLE Go-specific pattern that doesn't apply to .NET (build tags, platform-specific goroutine tricks, etc.)
DEFERRED Intentionally skipped for now (document why)

Step 5: Fill In the Gap Inventory

Add rows to the Gap Inventory table below. Group by Go source file. Include the Go file and line number so a porting LLM can jump directly to the reference implementation.

Key Porting Notes for Gateways

  • Gateways connect separate NATS clusters. They start in "optimistic" mode (forward everything) then switch to "interest-only" mode.
  • Reply subject mapping: _GR_.<cluster>.<hash>.<reply> prevents routing loops.
  • Gateway connections use ClientKind = GATEWAY.

Go Reference Files (Source)

  • golang/nats-server/server/gateway.go — Inter-cluster bridges (~3,400 lines). Interest-only mode optimizes traffic. Reply subject mapping (_GR_. prefix) avoids cross-cluster conflicts.

Go Reference Files (Tests)

  • golang/nats-server/server/gateway_test.go
  • golang/nats-server/test/gateway_test.go (integration)

.NET Implementation Files (Source)

  • src/NATS.Server/Gateways/ (all files)

.NET Implementation Files (Tests)

  • tests/NATS.Server.Tests/Gateways/

Gap Inventory

golang/nats-server/server/gateway.go

Constants, Variables, and Enums

Go Symbol Go File:Line Status .NET Equivalent Notes
SetGatewaysSolicitDelay gateway.go:76 NOT_APPLICABLE Test-only helper; sets atomic delay before soliciting gateways. Go-specific test hook.
ResetGatewaysSolicitDelay gateway.go:83 NOT_APPLICABLE Test-only helper; resets atomic delay. Go-specific test hook.
GatewayDoNotForceInterestOnlyMode gateway.go:130 NOT_APPLICABLE Test-only global flag; disables forced interest-only mode in tests. Go-specific test hook.
GatewayInterestMode (enum: Optimistic/Transitioning/InterestOnly) gateway.go:94111 PORTED src/NATS.Server/Gateways/GatewayInterestTracker.cs:16 GatewayInterestMode enum with identical three values.
GatewayInterestMode.String() gateway.go:113 NOT_APPLICABLE Go stringer pattern; C# uses ToString() automatically.
gwReplyPrefix / gwReplyPrefixLen / gwHashLen / offset constants gateway.go:4958 PORTED src/NATS.Server/Gateways/ReplyMapper.cs:1217 Reply-prefix/length/hash-length constants are explicitly defined (GatewayReplyPrefix, GatewayReplyPrefixLen, GatewayHashLen, plus legacy counterparts).
oldGWReplyPrefix / oldGWReplyPrefixLen / oldGWReplyStart gateway.go:4346 PORTED src/NATS.Server/Gateways/ReplyMapper.cs:13,15,31 Legacy $GR. prefix constants and old-prefix detection are implemented via IsGatewayRoutedSubject(..., out isOldPrefix).
gatewayTLSInsecureWarning gateway.go:71 PORTED src/NATS.Server/Gateways/GatewayManager.cs:68 Gateway TLS insecure warning constant is defined for parity/documentation and diagnostic use.

Structs / Types

Go Symbol Go File:Line Status .NET Equivalent Notes
srvGateway struct gateway.go:134 PARTIAL src/NATS.Server/Gateways/GatewayManager.cs:66 GatewayManager covers outbound/inbound maps, accept loop, discovery, registration, and stats. Missing: RTT-ordered outbound list (outo), totalQSubs atomic counter, pasi per-account subscription interest map, rsubs recent-subscription sync.Map, sIDHash/routesIDByHash for reply routing, sqbsz/recSubExp, and oldHash/oldReplyPfx for backward compat.
sitally struct gateway.go:189 MISSING Subject-interest tally (ref count + queue flag) used in pasi.m. No equivalent in .NET.
gatewayCfg struct gateway.go:194 PARTIAL src/NATS.Server/Configuration/GatewayOptions.cs:27 (RemoteGatewayOptions) Added parity state and helpers for hash/oldHash, implicit, connection attempts, TLS host capture, URL add/update/get flows, and varz URL-update flag. Remaining gap: per-remote TLS config wiring into active gateway dial/handshake path.
gateway struct (per-client) gateway.go:207 MISSING Per-connection gateway state (outbound flag, outsim sync.Map, insim map, connectURL, useOldPrefix, interestOnlyMode, remoteName). Absorbed into GatewayConnection but without full per-message interest tracking.
outsie struct gateway.go:229 PARTIAL src/NATS.Server/Gateways/GatewayInterestTracker.cs GatewayInterestTracker.AccountState covers mode and no-interest set. Missing: per-account sl *Sublist for queue-sub tracking in InterestOnly mode, qsubs counter.
insie struct gateway.go:256 MISSING Inbound per-account no-interest set with mode tracking. No distinct type in .NET; GatewayInterestTracker handles outbound-side equivalent but not the inbound RS-/RS+ tracking map.
gwReplyMap struct gateway.go:261 PARTIAL src/NATS.Server/Gateways/ReplyMapper.cs:289 (CacheEntry) ReplyMapCache.CacheEntry provides key/value/TTL. Missing: Go stores ms string (routed subject) and exp int64 (Unix nano expiry); .NET uses DateTime but does not store the destination server/cluster hash separately.
gwReplyMapping struct gateway.go:266 PARTIAL src/NATS.Server/Gateways/ReplyMapper.cs:181 (ReplyMapCache) ReplyMapCache provides LRU+TTL. Missing: atomic check int32 field for fast-path bypass; Go mapping is per-client or per-account with explicit locker, .NET is a single shared cache.
GatewayConnectionState enum gateway.go (n/a) PORTED src/NATS.Server/Gateways/GatewayManager.cs:14 .NET addition: lifecycle states (Connecting/Connected/Disconnected/Draining). No direct Go equivalent but covers the connection state machine.
GatewayRegistration gateway.go (n/a) PORTED src/NATS.Server/Gateways/GatewayManager.cs:26 .NET-specific registration record with stats counters.
GatewayReconnectPolicy gateway.go:689 PORTED src/NATS.Server/Gateways/GatewayManager.cs:43 Exponential backoff with jitter matches Go's reconnect delay logic.
GatewayInfo gateway.go (Info struct) PARTIAL src/NATS.Server/Gateways/GatewayInfo.cs:7 Covers name and URLs for gossip discovery. Go's Info struct has many more gateway-related fields (GatewayCmd, GatewayCmdPayload, GatewayNRP, GatewayIOM, GatewayURLs, etc.) that are not in this type.

Functions and Methods — srvGateway receiver

Go Symbol Go File:Line Status .NET Equivalent Notes
srvGateway.updateRemotesTLSConfig gateway.go:426 MISSING Config-reload TLS update for remote gateway connections. TLS not implemented in .NET.
srvGateway.rejectUnknown gateway.go:476 PARTIAL src/NATS.Server/Configuration/GatewayOptions.cs:11 GatewayOptions.RejectUnknown property exists but is not enforced in the accept-loop or handshake logic.
srvGateway.generateInfoJSON gateway.go:649 MISSING Generates the gateway INFO protocol JSON. No INFO protocol generation in .NET.
srvGateway.getClusterHash gateway.go:2892 MISSING Returns 6-byte cluster hash for reply routing. No cluster hash computed at runtime in .NET.
srvGateway.shouldMapReplyForGatewaySend gateway.go:2507 MISSING Checks recent subscriptions (rsubs) to decide whether to map a reply subject. rsubs not ported.
srvGateway.orderOutboundConnectionsLocked gateway.go:1764 MISSING Sorts outbound connections by lowest RTT. RTT-based ordering not implemented.
srvGateway.orderOutboundConnections gateway.go:1771 MISSING Locked wrapper for orderOutboundConnectionsLocked.

Functions and Methods — Server receiver

Go Symbol Go File:Line Status .NET Equivalent Notes
validateGatewayOptions gateway.go:306 PORTED src/NATS.Server/Gateways/GatewayManager.cs:113 (ValidateGatewayOptions) Basic gateway config validation implemented for required name, valid port range, and non-empty remotes.
getGWHash (standalone) gateway.go:335 PORTED src/NATS.Server/Gateways/ReplyMapper.cs:75 (ComputeGatewayHash) Deterministic short gateway hash helper implemented (6-char hex).
getOldHash (standalone) gateway.go:339 PORTED src/NATS.Server/Gateways/ReplyMapper.cs:85 (ComputeOldGatewayHash) Deterministic legacy short hash helper implemented (4-char hex).
Server.newGateway gateway.go:350 PARTIAL src/NATS.Server/Gateways/GatewayManager.cs:90 GatewayManager constructor covers basic setup. Missing: hash computation, reply prefix assembly, oldReplyPfx, pasi init, resolver config.
Server.startGateways gateway.go:487 PARTIAL src/NATS.Server/Gateways/GatewayManager.cs:161 (StartAsync) StartAsync starts accept loop and connects to remotes. Missing: solicit delay, cluster-formation wait.
Server.startGatewayAcceptLoop gateway.go:511 PARTIAL src/NATS.Server/Gateways/GatewayManager.cs:326 (AcceptLoopAsync) Accept loop exists. Missing: TLS config, authRequired, reject-unknown check, advertising, GatewayIOM flag, INFO protocol send.
Server.setGatewayInfoHostPort gateway.go:595 MISSING Configures gateway listener host/port with advertise override and non-local IP resolution.
Server.solicitGateways gateway.go:668 PARTIAL src/NATS.Server/Gateways/GatewayManager.cs:173 (StartAsync remote loop) Connects to configured remotes. Missing: implicit vs explicit distinction, goroutine-per-remote with proper lifecycle.
Server.reconnectGateway gateway.go:689 PORTED src/NATS.Server/Gateways/GatewayManager.cs:149 (ReconnectGatewayAsync) Exponential backoff reconnect delay with jitter.
Server.solicitGateway gateway.go:706 PARTIAL src/NATS.Server/Gateways/GatewayManager.cs:358 (ConnectWithRetryAsync) Retry loop with multiple URL support. Missing: random URL selection, shouldReportConnectErr throttling, implicit gateway retry limits, DNS resolution via resolver, ConnectBackoff flag.
srvGateway.hasInbound gateway.go:790 PORTED src/NATS.Server/Gateways/GatewayManager.cs:345 (HasInbound) Inbound-connection presence check is implemented by remote server id.
Server.createGateway gateway.go:805 PARTIAL src/NATS.Server/Gateways/GatewayManager.cs:344 (HandleInboundAsync) and ConnectWithRetryAsync Creates client connection for inbound/outbound. Missing: full client lifecycle, TLS handshake, CONNECT/INFO protocol, expectConnect flag, ping timer setup, temp-client registration.
client.sendGatewayConnect gateway.go:958 PARTIAL src/NATS.Server/Gateways/GatewayConnection.cs:113 (PerformOutboundHandshakeAsync) Handshake sends server ID. Go sends full CONNECT JSON with auth, TLS flag, gateway name. .NET sends a simplified GATEWAY {serverId} line.
client.processGatewayConnect gateway.go:993 MISSING Parses CONNECT from inbound gateway; validates gateway field, rejects wrong port/unknown gateways. No protocol parsing in .NET.
client.processGatewayInfo gateway.go:1045 MISSING Handles INFO protocol for both outbound (first connect + gossip) and inbound (register, switch to interest-only, send queue subs). Core of gateway handshake. Not ported.
Server.gossipGatewaysToInboundGateway gateway.go:1253 MISSING Sends INFO gossip for all known gateways to a new inbound connection for full-mesh formation.
Server.forwardNewGatewayToLocalCluster gateway.go:1279 MISSING Floods cluster routes with new gateway INFO to ensure all nodes connect.
Server.sendQueueSubsToGateway gateway.go:1311 PARTIAL src/NATS.Server/Gateways/GatewayConnection.cs:72 (AddQueueSubscription) Registers queue subs locally. Missing: actual RS+ wire protocol emission to the remote peer.
Server.sendAccountSubsToGateway gateway.go:1319 PARTIAL src/NATS.Server/Gateways/GatewayManager.cs:210 (SendAccountSubscriptions) Sends subject list to named gateway. Missing: wire protocol emission (RS+ with account prefix) and mode switch to InterestOnly.
gwBuildSubProto (standalone) gateway.go:1323 MISSING Builds RS+/RS- binary protocol buffer for a given account/subject map.
Server.sendSubsToGateway gateway.go:1342 MISSING Full subscription send loop (queue subs on connect, or all subs for an account on mode switch).
Server.processGatewayInfoFromRoute gateway.go:1394 MISSING Handles gateway gossip INFO received via a cluster route connection.
Server.sendGatewayConfigsToRoute gateway.go:1406 MISSING Sends known outbound gateway configs to a new route connection.
Server.processImplicitGateway gateway.go:1453 PARTIAL src/NATS.Server/Gateways/GatewayManager.cs:119 (ProcessImplicitGateway) Records discovered gateway name. Missing: URL augmentation of existing config, creation of gatewayCfg, launching solicitGateway goroutine for the new implicit remote.
Server.NumOutboundGateways gateway.go:1501 PORTED src/NATS.Server/NatsServer.cs:155 (NumOutboundGateways) Public server-facing outbound gateway count now exposed.
Server.numOutboundGateways gateway.go:1506 PORTED src/NATS.Server/Gateways/GatewayManager.cs:331 (NumOutboundGateways) Manager now computes outbound count from live connection direction (IsOutbound).
Server.numInboundGateways gateway.go:1514 PORTED src/NATS.Server/Gateways/GatewayManager.cs:338 (NumInboundGateways), src/NATS.Server/NatsServer.cs:156 Inbound gateway count is now tracked and exposed.
Server.getRemoteGateway gateway.go:1522 PARTIAL src/NATS.Server/Gateways/GatewayManager.cs:264 (GetRegistration) Returns registration by name. Missing: returns gatewayCfg in Go (with TLS, URLs, hash); .NET returns GatewayRegistration (only state/stats).
gatewayCfg.bumpConnAttempts gateway.go:1530 PORTED src/NATS.Server/Configuration/GatewayOptions.cs:57 (RemoteGatewayOptions.BumpConnAttempts) Added connection-attempt increment helper.
gatewayCfg.getConnAttempts gateway.go:1537 PORTED src/NATS.Server/Configuration/GatewayOptions.cs:59 (RemoteGatewayOptions.GetConnAttempts) Added connection-attempt read helper.
gatewayCfg.resetConnAttempts gateway.go:1545 PORTED src/NATS.Server/Configuration/GatewayOptions.cs:61 (RemoteGatewayOptions.ResetConnAttempts) Added connection-attempt reset helper.
gatewayCfg.isImplicit gateway.go:1552 PORTED src/NATS.Server/Configuration/GatewayOptions.cs:63 (RemoteGatewayOptions.IsImplicit) Added implicit-vs-explicit gateway config query.
gatewayCfg.getURLs gateway.go:1561 PORTED src/NATS.Server/Configuration/GatewayOptions.cs:65 (RemoteGatewayOptions.GetUrls) Added normalized + shuffled URL list helper for connection attempts.
gatewayCfg.getURLsAsStrings gateway.go:1576 PORTED src/NATS.Server/Configuration/GatewayOptions.cs:84 (RemoteGatewayOptions.GetUrlsAsStrings) Added URL string projection helper for gossip/config sync paths.
gatewayCfg.updateURLs gateway.go:1588 PORTED src/NATS.Server/Configuration/GatewayOptions.cs:92 (RemoteGatewayOptions.UpdateUrls) Added merged configured+discovered URL rebuild helper with normalization/deduplication.
gatewayCfg.saveTLSHostname gateway.go:1612 PORTED src/NATS.Server/Configuration/GatewayOptions.cs:100 (RemoteGatewayOptions.SaveTlsHostname) Added TLS hostname extraction/storage from URL host.
gatewayCfg.addURLs gateway.go:1621 PORTED src/NATS.Server/Configuration/GatewayOptions.cs:106 (RemoteGatewayOptions.AddUrls) Added incremental discovered-URL add helper with normalization/deduplication.
Server.addGatewayURL gateway.go:1648 MISSING Adds a URL to the server's gateway URL set and regenerates INFO JSON.
Server.removeGatewayURL gateway.go:1661 MISSING Removes a URL from the gateway URL set and regenerates INFO JSON.
Server.sendAsyncGatewayInfo gateway.go:1676 MISSING Sends updated INFO to all inbound gateway connections (e.g., after URL change).
Server.getGatewayURL gateway.go:1688 PORTED src/NATS.Server/NatsServer.cs:259 Added gateway listen URL accessor that returns configured listen endpoint when gateway manager is present.
Server.getGatewayName gateway.go:1697 PORTED src/NATS.Server/NatsServer.cs:260 Added gateway name accessor returning configured gateway cluster name.
Server.getAllGatewayConnections gateway.go:1703 MISSING Collects all inbound + outbound gateway clients into a map.
Server.registerInboundGatewayConnection gateway.go:1720 PARTIAL src/NATS.Server/Gateways/GatewayManager.cs:392 (Register) Register adds connection to dictionary. Missing: separate inbound map keyed by CID.
Server.registerOutboundGatewayConnection gateway.go:1728 PARTIAL src/NATS.Server/Gateways/GatewayManager.cs:392 (Register) Register handles registration. Missing: duplicate-prevention logic (return false if name already exists), RTT-ordered outo list.
Server.getOutboundGatewayConnection gateway.go:1743 PORTED src/NATS.Server/Gateways/GatewayManager.cs:352 (GetOutboundGatewayConnection) Outbound connection lookup by remote server id is implemented.
Server.getOutboundGatewayConnections gateway.go:1752 PORTED src/NATS.Server/Gateways/GatewayManager.cs:359 (GetOutboundGatewayConnections) Outbound connection snapshot enumeration is implemented.
Server.getInboundGatewayConnections gateway.go:1778 PORTED src/NATS.Server/Gateways/GatewayManager.cs:366 (GetInboundGatewayConnections) Inbound connection snapshot enumeration is implemented.
Server.removeRemoteGatewayConnection gateway.go:1788 PARTIAL src/NATS.Server/Gateways/GatewayManager.cs:416 (WatchConnectionAsync) Removes connection and decrements stats. Missing: outbound-specific cleanup (delete from outo/out, remove qsub tracking from totalQSubs), inbound-specific cleanup (remove _R_ subscriptions).
Server.GatewayAddr gateway.go:1862 PORTED src/NATS.Server/NatsServer.cs:258 Added gateway address accessor. .NET returns host:port string endpoint rather than Go *net.TCPAddr.
client.processGatewayAccountUnsub gateway.go:1875 PARTIAL src/NATS.Server/Gateways/GatewayInterestTracker.cs:86 (TrackNoInterest) Tracks no-interest at account level. Missing: handling of queue subs (reset ni map but keep entry if qsubs > 0), Go's nil-vs-entry distinction in outsim.
client.processGatewayAccountSub gateway.go:1904 PARTIAL src/NATS.Server/Gateways/GatewayInterestTracker.cs:61 (TrackInterest) Clears no-interest in optimistic mode. Missing: queue-sub check (don't delete entry if qsubs > 0).
client.processGatewayRUnsub gateway.go:1934 MISSING Parses RS- protocol; for optimistic mode stores in ni map, for InterestOnly/queue removes from sublist. Full RS- processing not ported.
client.processGatewayRSub gateway.go:2029 MISSING Parses RS+ protocol; registers interest in sublist for queue subs and InterestOnly mode. Full RS+ processing not ported.
client.gatewayInterest gateway.go:2165 PARTIAL src/NATS.Server/Gateways/GatewayInterestTracker.cs:113 (ShouldForward) ShouldForward handles Optimistic/Transitioning/InterestOnly modes. Missing: separate queue-sub result (*SublistResult) return, interestOnlyMode flag override, emptyResult guard.
Server.switchAccountToInterestMode gateway.go:2211 PORTED src/NATS.Server/Gateways/GatewayInterestTracker.cs:144 (SwitchToInterestOnly) Switches account to InterestOnly. Go iterates all inbound connections; .NET operates per-tracker instance.
Server.maybeSendSubOrUnsubToGateways gateway.go:2237 PARTIAL src/NATS.Server/Gateways/GatewayManager.cs:193 (PropagateLocalSubscription/Unsubscription) Propagates sub/unsub to inbound gateways. Missing: per-mode decision (optimistic ni-map check vs InterestOnly always-send), wildcard cleanup of ni-map, A+ send when clearing A-, actual wire protocol.
Server.sendQueueSubOrUnsubToGateways gateway.go:2335 PARTIAL src/NATS.Server/Gateways/GatewayManager.cs:199 (PropagateLocalUnsubscription) Propagates queue sub changes. Missing: wire RS+/RS- protocol, A- clearing logic.
Server.gatewayUpdateSubInterest gateway.go:2391 MISSING Ref-counted pasi map update + recent-sub tracking + triggers send to gateways. Core subscription-interest accounting not ported.
isGWRoutedReply (standalone) gateway.go:2484 PORTED src/NATS.Server/Gateways/ReplyMapper.cs:17 (HasGatewayReplyPrefix) Detects _GR_. prefix with length guard.
isGWRoutedSubjectAndIsOldPrefix (standalone) gateway.go:2490 PORTED src/NATS.Server/Gateways/ReplyMapper.cs:31 (IsGatewayRoutedSubject) Implemented explicit routed-subject detection with old-prefix flag output (isOldPrefix) for _GR_. and $GR..
hasGWRoutedReplyPrefix (standalone) gateway.go:2502 PORTED src/NATS.Server/Gateways/ReplyMapper.cs:17 (HasGatewayReplyPrefix) Equivalent prefix check.
srvGateway.shouldMapReplyForGatewaySend gateway.go:2507 MISSING Checks rsubs sync.Map to decide if a reply subject needs gateway mapping. No rsubs equivalent.
client.sendMsgToGateways gateway.go:2540 PARTIAL src/NATS.Server/Gateways/GatewayManager.cs:181 (ForwardMessageAsync) Iterates connections and sends. Missing: direct-send path for _GR_ subjects (hash routing), queue group filtering, reply subject mapping, header stripping for non-header peers, message tracing, per-account stats, RTT-ordered iteration.
Server.gatewayHandleAccountNoInterest gateway.go:2787 PARTIAL Partially in GatewayInterestTracker.TrackNoInterest Sends A- under pasi lock. Missing: pasi lock coordination, actual A- wire protocol emission from inbound side.
client.sendAccountUnsubToGateway gateway.go:2802 PARTIAL src/NATS.Server/Gateways/GatewayConnection.cs:142 (SendAMinusAsync) Sends A- wire protocol. Missing: idempotency guard (don't send if already sent, tracked via nil entry in insim).
Server.gatewayHandleSubjectNoInterest gateway.go:2830 MISSING On missing subject interest: sends RS- under pasi lock, counts RS-, triggers mode switch at threshold. Core interest-only negotiation not ported.
Server.storeRouteByHash gateway.go:2901 MISSING Stores route client keyed by server-ID hash for gateway reply routing.
Server.removeRouteByHash gateway.go:2909 MISSING Removes route entry by hash.
Server.getRouteByHash gateway.go:2918 MISSING Looks up route by hash for per-account or pool routing. Used in handleGatewayReply.
getSubjectFromGWRoutedReply (standalone) gateway.go:2948 PORTED src/NATS.Server/Gateways/ReplyMapper.cs:74 (TryRestoreGatewayReply) Extracts original subject from routed reply; handles both old and new prefix.
client.handleGatewayReply gateway.go:2963 MISSING On inbound message: detects _GR_ prefix, decodes cluster/server hash, routes to origin route or delivers locally. Core gateway reply routing not ported.
client.processInboundGatewayMsg gateway.go:3107 MISSING Main inbound message processor: handles GW reply, account lookup, no-interest signaling, message delivery. Not ported.
client.gatewayAllSubsReceiveStart gateway.go:3183 PARTIAL src/NATS.Server/Gateways/GatewayInterestTracker.cs:86 (mode=Transitioning path) Go sets mode to Transitioning; .NET sets Transitioning mode when threshold crossed. Missing: command-protocol parsing, explicit start triggered by INFO command.
client.gatewayAllSubsReceiveComplete gateway.go:3216 PARTIAL src/NATS.Server/Gateways/GatewayInterestTracker.cs:144 (SwitchToInterestOnly) Final switch to InterestOnly. Go clears ni map and sets mode; .NET does equivalent. Missing: triggered by INFO command with gatewayCmdAllSubsComplete.
getAccountFromGatewayCommand (standalone) gateway.go:3240 MISSING Extracts account from GatewayCmdPayload in INFO protocol. Not ported.
client.gatewaySwitchAccountToSendAllSubs gateway.go:3260 PARTIAL src/NATS.Server/Gateways/GatewayInterestTracker.cs:144 (SwitchToInterestOnly) Switches mode and triggers async all-subs send. Missing: INFO command emission (gatewayCmdAllSubsStart/gatewayCmdAllSubsComplete), async sendAccountSubsToGateway goroutine.
Server.trackGWReply gateway.go:3324 PARTIAL src/NATS.Server/Gateways/ReplyMapper.cs:231 (ReplyMapCache.Set) Caches reply mapping with TTL. Missing: per-client vs per-account duality, gwrm.m sync.Map for background cleanup, check int32 atomic flag, gwrm.ch channel to trigger expiry timer.
Server.startGWReplyMapExpiration gateway.go:3371 PARTIAL src/NATS.Server/Gateways/ReplyMapper.cs:267 (ReplyMapCache.PurgeExpired) Purge is manual on-demand. Go runs a dedicated goroutine with timer reset on new entries via channel. No background expiry goroutine in .NET.
gwReplyMapping.get gateway.go:280 PORTED src/NATS.Server/Gateways/ReplyMapper.cs:202 (ReplyMapCache.TryGet) LRU get with TTL check.
RemoteGatewayOpts.clone gateway.go:290 PORTED src/NATS.Server/Configuration/GatewayOptions.cs:36 (RemoteGatewayOptions.Clone) Deep-copy helper implemented for remote gateway option name + URL list.

Additional .NET-Only Types (No Go Equivalent)

.NET Symbol .NET File:Line Notes
GatewayCommandType enum src/NATS.Server/Gateways/GatewayCommands.cs:9 .NET-specific wire command enum; Go uses string constants and byte dispatch.
GatewayCommands static class src/NATS.Server/Gateways/GatewayCommands.cs:31 Wire-protocol constants and formatters. Partially maps to Go's rSubBytes/rUnsubBytes/aSubBytes/aUnsubBytes/InfoProto but uses a simplified format.
GatewayMessage record src/NATS.Server/Gateways/GatewayConnection.cs:346 DTO for received messages; no direct Go equivalent.
ReplyMapCache src/NATS.Server/Gateways/ReplyMapper.cs:181 LRU+TTL cache for reply mappings.

Keeping This File Updated

After porting work is completed:

  1. Update status: Change MISSING → PORTED or PARTIAL → PORTED for each item completed
  2. Add .NET path: Fill in the ".NET Equivalent" column with the actual file:line
  3. Re-count LOC: Update the LOC numbers in stillmissing.md:
    # Re-count .NET source LOC for this module
    find src/NATS.Server/Gateways/ -name '*.cs' -type f -exec cat {} + | wc -l
    # Re-count .NET test LOC for this module
    find tests/NATS.Server.Tests/Gateways/ -name '*.cs' -type f -exec cat {} + | wc -l
    
  4. Add a changelog entry below with date and summary of what was ported
  5. Update the parity DB if new test mappings were created:
    sqlite3 docs/test_parity.db "INSERT INTO test_mappings (go_test_id, dotnet_test_id, confidence, notes) VALUES (?, ?, 'manual', 'ported in YYYY-MM-DD session')"
    

Change Log

Date Change By
2026-02-26 Executed gateways batch 4 accessor parity slice: added server gateway accessors (GatewayAddr, GetGatewayURL, GetGatewayName) and targeted tests (GatewayServerAccessorParityBatch4Tests). Reclassified 3 rows to PORTED. codex
2026-02-25 File created with LLM analysis instructions auto
2026-02-25 Full gap inventory completed: analyzed all ~3,427 lines of gateway.go; classified 80+ Go symbols against 6 .NET source files. Final counts: 9 PORTED, 35 PARTIAL, 37 MISSING, 5 NOT_APPLICABLE. claude-sonnet-4-6
2026-02-25 Ported gateway parity helper batch: reply-prefix/hash constants and old-prefix detection, gateway hash helpers (getGWHash/getOldHash analogs), gateway option validator, TLS warning constant, and RemoteGatewayOptions.Clone; added focused tests and updated status rows. codex
2026-02-25 Ported gateway connection-direction parity batch: added inbound/outbound classification (IsOutbound), count/lookups (NumOutboundGateways, NumInboundGateways, HasInbound, outbound/inbound connection snapshots), and server wrappers with focused tests. codex
2026-02-25 Ported gateway remote-config parity batch: added RemoteGatewayOptions attempt counters and URL lifecycle helpers (Bump/Get/ResetConnAttempts, IsImplicit, GetUrls, GetUrlsAsStrings, UpdateUrls, SaveTlsHostname, AddUrls) with focused tests (GatewayRemoteConfigParityBatch3Tests). codex