# Gateways — Gap Analysis

> This file tracks what has and hasn't been ported from Go to .NET for the **Gateways** module.
> See [stillmissing.md](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:
   ```bash
   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

<!-- After analysis, fill in this table. Group rows by Go source file. -->

### `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:94–111 | 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:49–58 | PORTED | `src/NATS.Server/Gateways/ReplyMapper.cs:12–17` | Reply-prefix/length/hash-length constants are explicitly defined (`GatewayReplyPrefix`, `GatewayReplyPrefixLen`, `GatewayHashLen`, plus legacy counterparts). |
| `oldGWReplyPrefix` / `oldGWReplyPrefixLen` / `oldGWReplyStart` | gateway.go:43–46 | 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`:
   ```bash
   # 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:
   ```bash
   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 |
