# Core Server — Gap Analysis > This file tracks what has and hasn't been ported from Go to .NET for the **Core Server** 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 Core Server - Go's `client.go` is the **largest single file** (~6,700 lines). It contains the readLoop/writeLoop goroutines, dynamic buffer sizing (512→65536), slow consumer detection, and per-client subscription tracking. The .NET equivalent should use `async/await` with `System.IO.Pipelines`. - Go's `server.go` manages listeners, accept loops, and shutdown coordination. Map goroutines to `Task`-based async patterns. - Go's `opts.go` handles both CLI flags and config file parsing (CLI overrides config). The .NET version separates this into `Configuration/`. - `reload.go` implements hot-reload by diffing old vs new config and applying deltas without restart. - Platform-specific files (`service_windows.go`, `signal_windows.go`, `signal_wasm.go`) may be NOT_APPLICABLE. --- ## Go Reference Files (Source) - `golang/nats-server/server/server.go` — Server struct, startup lifecycle, listener management - `golang/nats-server/server/client.go` — Connection handling, readLoop/writeLoop, per-client state (~6,700 lines) - `golang/nats-server/server/opts.go` — Server options struct, CLI flag parsing, config file loading - `golang/nats-server/server/reload.go` — Hot reload on signal, config diffing - `golang/nats-server/server/service.go` — OS service/daemon management - `golang/nats-server/server/signal.go` — Signal handling (SIGHUP, SIGTERM, etc.) - `golang/nats-server/main.go` — Entry point ## Go Reference Files (Tests) - `golang/nats-server/server/server_test.go` - `golang/nats-server/server/client_test.go` - `golang/nats-server/server/opts_test.go` - `golang/nats-server/server/reload_test.go` - `golang/nats-server/server/signal_test.go` - `golang/nats-server/server/test_test.go` (test helpers) ## .NET Implementation Files (Source) - `src/NATS.Server/NatsServer.cs` — Server lifecycle - `src/NATS.Server/NatsClient.cs` — Per-connection client - `src/NATS.Server/NatsOptions.cs` — Server options - `src/NATS.Server/ClientFlags.cs` - `src/NATS.Server/ClientKind.cs` - `src/NATS.Server/ClientClosedReason.cs` - `src/NATS.Server/ClientTraceInfo.cs` - `src/NATS.Server/ClosedState.cs` - `src/NATS.Server/INatsClient.cs` - `src/NATS.Server/InternalClient.cs` - `src/NATS.Server/ServerStats.cs` - `src/NATS.Server/SlowConsumerTracker.cs` - `src/NATS.Server/MqttOptions.cs` - `src/NATS.Server.Host/Program.cs` — Entry point ## .NET Implementation Files (Tests) - `tests/NATS.Server.Tests/` (root-level test files) --- ## Gap Inventory | Go Symbol | Go File:Line | Status | .NET Equivalent | Notes | |-----------|:-------------|--------|:----------------|-------| | **golang/nats-server/main.go** | | | | | | main() | main.go:97 | PORTED | src/NATS.Server.Host/Program.cs:1 | CLI arg parsing, config load, server create, start, wait | | usage() | main.go:92 | PORTED | src/NATS.Server.Host/Program.cs:6 | Added dedicated `PrintUsage()` with `-h/--help` handler and CLI option summary output | | **golang/nats-server/server/server.go — Types** | | | | | | Info struct | server.go:109 | PORTED | src/NATS.Server/Protocol/NatsProtocol.cs:39 (ServerInfo) | Core fields ported; route/gateway/leafnode-specific fields are partial | | Server struct | server.go:169 | PARTIAL | src/NATS.Server/NatsServer.cs:31 | Core fields ported (clients, accounts, opts, listener, shutdown). Missing: route pool tracking, gateway internals, OCSP monitors, proxied conns, rate limiting maps | | stats struct | server.go:402 | PORTED | src/NATS.Server/ServerStats.cs:6 | All counters present | | scStats struct | server.go:413 | PORTED | src/NATS.Server/ServerStats.cs:18-25 | Per-kind slow consumer counters present | | staleStats struct | server.go:421 | PORTED | src/NATS.Server/ServerStats.cs:26 | Per-kind stale connection counters present | | nodeInfo struct | server.go:387 | NOT_APPLICABLE | — | JetStream cluster-specific; tracked in JetStream module | | Ports struct | server.go:4236 | PORTED | src/NATS.Server/NatsOptions.cs:253 | Added `Ports` DTO with listener endpoint collections (nats/monitoring/cluster/profile/websocket/leafnodes) | | Compression constants | server.go:437-446 | PORTED | src/NATS.Server/NatsOptions.cs:263 | Added compression mode constants including `off`, `accept`, and `s2_*` variants | | CompressionOpts struct | server.go:97 (opts.go) | PORTED | src/NATS.Server/NatsOptions.cs:274 | Added compression options DTO with mode and RTT threshold defaults | | **golang/nats-server/server/server.go — Exported Server Methods** | | | | | | NewServer() | server.go:716 | PORTED | src/NATS.Server/NatsServer.cs constructor | Options validation, NKey identity, info setup | | New() (deprecated) | server.go:698 | NOT_APPLICABLE | — | Deprecated wrapper | | NewServerFromConfig() | server.go:703 | PORTED | src/NATS.Server.Host/Program.cs:20-22 | Config file processing then new server | | (s) Start() | server.go:2263 | PORTED | src/NATS.Server/NatsServer.cs:502 (StartAsync) | Listener, accept loop, subsystem startup. Async in .NET | | (s) Shutdown() | server.go:2583 | PORTED | src/NATS.Server/NatsServer.cs:163 (ShutdownAsync) | Client disconnect, listener close, subsystem teardown | | (s) WaitForShutdown() | server.go:2775 | PORTED | src/NATS.Server/NatsServer.cs:150 | Blocks until shutdown complete | | (s) AcceptLoop() | server.go:2780 | PORTED | src/NATS.Server/NatsServer.cs:502 (inline in StartAsync) | Accept loop is part of StartAsync | | (s) ReadyForConnections() | server.go:4023 | PORTED | src/NATS.Server/NatsServer.cs:148 (WaitForReadyAsync) | Async TaskCompletionSource-based | | (s) Running() | server.go:1695 | PORTED | src/NATS.Server/NatsServer.cs:108 (IsShuttingDown inverted) | Derived from shutdown flag | | (s) ID() | server.go:4036 | PORTED | src/NATS.Server/NatsServer.cs:101 (ServerId) | — | | (s) Name() | server.go:4046 | PORTED | src/NATS.Server/NatsServer.cs:102 (ServerName) | — | | (s) NodeName() | server.go:4041 | PORTED | src/NATS.Server/NatsServer.cs:102 | Same as ServerName | | (s) ClusterName() | server.go:1017 | PORTED | src/NATS.Server/NatsServer.cs:121 | Dedicated cluster name getter from `NatsOptions.Cluster.Name` | | (s) ClientURL() | server.go:1086 | PORTED | src/NATS.Server/NatsServer.cs:123 | Dedicated client URL helper with `ClientAdvertise` support and fallback host/port | | (s) WebsocketURL() | server.go:1100 | PORTED | src/NATS.Server/NatsServer.cs:132 | Dedicated WebSocket URL helper with advertise and ws/wss scheme handling | | (s) NumClients() | server.go:3810 | PORTED | src/NATS.Server/NatsServer.cs:103 (ClientCount) | — | | (s) NumRoutes() | server.go:3773 | PORTED | src/NATS.Server/NatsServer.cs:148 | Dedicated route counter accessor | | (s) NumRemotes() | server.go:3790 | PORTED | src/NATS.Server/NatsServer.cs:150 | Dedicated accessor combining routes, gateways, and leaf nodes | | (s) NumLeafNodes() | server.go:3803 | PORTED | src/NATS.Server/NatsServer.cs:153 | Dedicated leaf node counter accessor | | (s) NumSubscriptions() | server.go:3836 | PORTED | src/NATS.Server/NatsServer.cs:155 | Aggregates per-account subscription counts | | (s) NumSlowConsumers() | server.go:3855 | PORTED | src/NATS.Server/ServerStats.cs:12 | Direct field access | | (s) NumSlowConsumersClients() | server.go:3865 | PORTED | src/NATS.Server/ServerStats.cs:18 | — | | (s) NumSlowConsumersRoutes() | server.go:3870 | PORTED | src/NATS.Server/ServerStats.cs:19 | — | | (s) NumSlowConsumersGateways() | server.go:3875 | PORTED | src/NATS.Server/ServerStats.cs:21 | — | | (s) NumSlowConsumersLeafs() | server.go:3880 | PORTED | src/NATS.Server/ServerStats.cs:20 | — | | (s) NumStalledClients() | server.go:3860 | PORTED | src/NATS.Server/ServerStats.cs:17 (Stalls) | — | | (s) NumStaleConnections() | server.go:3885 | PORTED | src/NATS.Server/ServerStats.cs:13 | — | | (s) NumStaleConnectionsClients() | server.go:3890 | PORTED | src/NATS.Server/ServerStats.cs:22 | — | | (s) NumStaleConnectionsRoutes() | server.go:3895 | PORTED | src/NATS.Server/ServerStats.cs:23 | — | | (s) NumStaleConnectionsGateways() | server.go:3900 | PORTED | src/NATS.Server/ServerStats.cs:24 | — | | (s) NumStaleConnectionsLeafs() | server.go:3905 | PORTED | src/NATS.Server/ServerStats.cs:25 | — | | (s) GetClient() | server.go:3817 | PORTED | src/NATS.Server/NatsServer.cs:119 (GetClients enumerable) | Enumerable, not by-ID lookup | | (s) GetLeafNode() | server.go:3829 | MISSING | — | No leaf node by-CID lookup | | (s) ConfigTime() | server.go:3910 | PORTED | src/NATS.Server/NatsServer.cs:157 | Tracks and exposes config load/reload timestamp | | (s) Addr() | server.go:3917 | PORTED | src/NATS.Server/NatsServer.cs:159 | Dedicated host:port address accessor | | (s) MonitorAddr() | server.go:3927 | PORTED | src/NATS.Server/NatsServer.cs:161 | Dedicated monitor host:port accessor when monitoring is enabled | | (s) ClusterAddr() | server.go:3937 | PARTIAL | src/NATS.Server/NatsServer.cs:166 | Dedicated cluster listen accessor exists, but .NET returns string endpoint (not TCPAddr) | | (s) ProfilerAddr() | server.go:3947 | PORTED | src/NATS.Server/NatsServer.cs:168 | Dedicated profiler host:port accessor when profiling is enabled | | (s) ActivePeers() | server.go:1577 | PORTED | src/NATS.Server/NatsServer.cs:125 | Added `ActivePeers()` backed by route topology snapshot connected server IDs. | | (s) NumActiveAccounts() | server.go:1716 | PORTED | src/NATS.Server/NatsServer.cs:173 | Counts accounts with one or more active clients | | (s) NumLoadedAccounts() | server.go:1744 | PORTED | src/NATS.Server/NatsServer.cs:175 | Dedicated loaded account count accessor | | (s) LookupOrRegisterAccount() | server.go:1749 | PORTED | src/NATS.Server/NatsServer.cs:1260 (GetOrCreateAccount) | — | | (s) RegisterAccount() | server.go:1762 | PORTED | src/NATS.Server/NatsServer.cs:1260 | Via GetOrCreateAccount | | (s) SetSystemAccount() | server.go:1775 | PORTED | src/NATS.Server/NatsServer.cs constructor | Set during construction | | (s) SystemAccount() | server.go:1798 | PORTED | src/NATS.Server/NatsServer.cs:105 | — | | (s) GlobalAccount() | server.go:1804 | PORTED | src/NATS.Server/NatsServer.cs:50 (_globalAccount) | — | | (s) LookupAccount() | server.go:2106 | PORTED | src/NATS.Server/NatsServer.cs:1260 | Via GetOrCreateAccount | | (s) StartProfiler() | server.go:2941 | PORTED | src/NATS.Server/NatsServer.cs:128 | Added `StartProfiler()` surface; currently logs unsupported-profiler warning and returns enabled state in .NET runtime model. | | (s) StartMonitoring() | server.go:3014 | PORTED | src/NATS.Server/Monitoring/MonitorServer.cs | Separate monitoring server class | | (s) StartHTTPMonitoring() | server.go:3003 | PORTED | src/NATS.Server/Monitoring/MonitorServer.cs:140 | — | | (s) StartHTTPSMonitoring() | server.go:3009 | PORTED | src/NATS.Server/Monitoring/MonitorServer.cs | HTTPS variant via options | | (s) HTTPHandler() | server.go:3207 | PARTIAL | src/NATS.Server/Monitoring/MonitorServer.cs | ASP.NET Kestrel handles routing, not an http.Handler | | (s) InProcessConn() | server.go:2876 | MISSING | — | No in-process connection support | | (s) LameDuckShutdown() | server.go:4421 | PORTED | src/NATS.Server/NatsServer.cs:239 (LameDuckShutdownAsync) | Full LDM with grace period and duration | | (s) DisconnectClientByID() | server.go:4742 | PORTED | src/NATS.Server/NatsServer.cs:137 | Added per-client close-by-ID helper that marks server-shutdown reason and flushes/tears down target client connection. | | (s) LDMClientByID() | server.go:4757 | PORTED | src/NATS.Server/NatsServer.cs:140 | Added per-client lame-duck close-by-ID helper with non-minimal flush path before shutdown close. | | (s) PortsInfo() | server.go:4247 | PORTED | src/NATS.Server/NatsServer.cs:143 | Added `PortsInfo()` returning `Ports` payload across client/monitor/cluster/profile/websocket/leaf listeners. | | (s) String() | server.go:4050 | PORTED | src/NATS.Server/NatsServer.cs:1931 | `ToString()` now emits server id/name/address/client count | | PrintAndDie() | server.go:1664 | NOT_APPLICABLE | — | .NET uses exceptions/logging | | PrintServerAndExit() | server.go:1670 | NOT_APPLICABLE | — | .NET uses --version flag differently | | ProcessCommandLineArgs() | server.go:1678 | PORTED | src/NATS.Server.Host/Program.cs:25-137 | Inline switch-based CLI parsing | | **golang/nats-server/server/server.go — Key Unexported Helpers** | | | | | | (s) createClient() | server.go:3245 | PORTED | src/NATS.Server/NatsServer.cs:666 (AcceptClientAsync) | Creates NatsClient, runs it | | (s) createInternalClient() | server.go:1925 | PORTED | src/NATS.Server/InternalClient.cs:29 | InternalClient class | | (s) createInternalSystemClient() | server.go:1910 | PORTED | src/NATS.Server/NatsServer.cs:377 | System internal client creation | | (s) acceptConnections() | server.go:2889 | PORTED | src/NATS.Server/NatsServer.cs:502 (StartAsync accept loop) | Inline in StartAsync | | (s) removeClient() | server.go:3708 | PORTED | src/NATS.Server/NatsServer.cs RemoveClient() via IMessageRouter | — | | (s) saveClosedClient() | server.go:3561 | PORTED | src/NATS.Server/Monitoring/ClosedConnectionRingBuffer.cs | ClosedConnectionRingBuffer | | (s) configureAccounts() | server.go:1230 | PARTIAL | src/NATS.Server/NatsServer.cs constructor | Basic account setup; no multi-account import/export wiring | | (s) setInfoHostPort() | server.go:2921 | PORTED | src/NATS.Server/NatsServer.cs:496 (BuildCachedInfo) | — | | (s) lameDuckMode() | server.go:4428 | PORTED | src/NATS.Server/NatsServer.cs:239 (LameDuckShutdownAsync) | Async version | | (s) handleSignals() | server.go (signal.go:37) | PORTED | src/NATS.Server/NatsServer.cs:320 (HandleSignals) | Uses PosixSignalRegistration on .NET | | (s) logPorts() | server.go:4332 | PARTIAL | src/NATS.Server/NatsServer.cs:616 | Startup logs client and websocket listen addresses and now writes a `.ports` file when `PortsFileDir` is configured. Residual gap: Go logs richer multi-listener details via `Ports` payload. | | (s) startGoRoutine() | server.go:4070 | NOT_APPLICABLE | — | .NET uses Task.Run; no goroutine tracking needed | | (s) readyForConnections() | server.go:3956 | PORTED | src/NATS.Server/NatsServer.cs:148 (WaitForReadyAsync) | — | | (s) getOpts() | server.go:1206 | PORTED | src/NATS.Server/NatsServer.cs:33 (_options field) | Direct field access | | (s) isRunning() | server.go:1700 | PORTED | src/NATS.Server/NatsServer.cs:108 | Inverted IsShuttingDown | | (s) isShuttingDown() | server.go:2577 | PORTED | src/NATS.Server/NatsServer.cs:108 (IsShuttingDown) | — | | (s) updateServerINFOAndSendINFOToClients() | server.go:3622 | PORTED | src/NATS.Server/NatsServer.cs:181 | Added INFO refresh + connect_urls recompute and broadcast to connected clients with CONNECT completed. | | (s) getConnectURLs() | server.go:4120 | PORTED | src/NATS.Server/NatsServer.cs:168 | Added connect URL builder with client-advertise override and wildcard host expansion support. | | (s) getNonLocalIPsIfHostIsIPAny() | server.go:4159 | PORTED | src/NATS.Server/NatsServer.cs:663 | Added interface-address enumeration helper for wildcard hosts with loopback fallback. | | (s) portFile() | server.go:4307 | PORTED | src/NATS.Server/NatsServer.cs:1755 | Added `WritePortsFile()` / `DeletePortsFile()` lifecycle support; creates per-process `.ports` file under `PortsFileDir` at startup and removes it on shutdown. | | (s) logPid() | server.go:1704 | PORTED | src/NATS.Server/NatsServer.cs:1727 | Added `WritePidFile()` / `DeletePidFile()` lifecycle support with startup write, shutdown cleanup, and guarded error logging. | | validateAndNormalizeCompressionOption() | server.go:466 | MISSING | — | No compression option validation | | selectCompressionMode() | server.go:559 | MISSING | — | No compression mode negotiation | | selectS2AutoModeBasedOnRTT() | server.go:618 | MISSING | — | No RTT-based auto compression | | needsCompression() | server.go:549 | MISSING | — | — | | s2WriterOptions() | server.go:678 | MISSING | — | — | | **golang/nats-server/server/client.go — Types** | | | | | | client struct | client.go:259 | PORTED | src/NATS.Server/NatsClient.cs:35 | Core fields: cid, kind, subs, opts, flags, stats. Missing: route/gw/leaf/mqtt sub-structs, perms cache, nonce, pubKey | | ClosedState enum | client.go:188 | PORTED | src/NATS.Server/ClosedState.cs:14 | Full enum parity with all values | | ClientOpts struct | client.go:661 | PORTED | src/NATS.Server/Protocol/ClientOptions.cs | Echo, Verbose, Pedantic, TLS, JWT, NKey, Headers, NoResponders | | clientFlag type | client.go:132 | PORTED | src/NATS.Server/ClientFlags.cs:9 | Core flags present; missing some (infoReceived, noReconnect, expectConnect, compressionNegotiated, didTLSFirst) | | WriteTimeoutPolicy | client.go:239 | PORTED | src/NATS.Server/NatsClient.cs:975 | Close and TcpFlush policies | | subscription struct | client.go:631 | PORTED | src/NATS.Server/Subscriptions/Subscription.cs | Subject, queue, sid, client, max, messageCount | | outbound struct | client.go:349 | PARTIAL | src/NATS.Server/NatsClient.cs:47-48 | Channel-based outbound; missing: net.Buffers, sync.Cond, S2 writer, stall channel | | readCache struct | client.go:485 | MISSING | — | No per-client subject lookup cache | | perAccountCache struct | client.go:541 | MISSING | — | No per-account L1 cache for routes/gateways | | pinfo struct | client.go:343 | PORTED | src/NATS.Server/NatsClient.cs:99 (_pingsOut) | Simplified: no timer struct, uses PeriodicTimer | | permissions struct | client.go:430 | PORTED | src/NATS.Server/Auth/ClientPermissions.cs | Pub/sub allow/deny; missing: pcache (permission cache) | | routeTarget struct | client.go:458 | MISSING | — | Route message targeting structure | | msgDeny struct | client.go:451 | MISSING | — | Deny-based message filtering cache | | resp struct | client.go:442 | MISSING | — | Dynamic response permission tracking | | CLIENT/ROUTER/GATEWAY/SYSTEM/LEAF/JETSTREAM/ACCOUNT constants | client.go:44-60 | PORTED | src/NATS.Server/ClientKind.cs:8 | All client kinds present | | isInternalClient() | client.go:63 | PORTED | src/NATS.Server/ClientKind.cs:20 (IsInternal extension) | — | | NON_CLIENT/NATS/MQTT/WS constants | client.go:70-79 | PORTED | src/NATS.Server/ClientConnectionType.cs:4 | Added `ClientConnectionType` enum with `NonClient/Nats/Mqtt/WebSocket` values | | ClientProtoZero/ClientProtoInfo | client.go:82-88 | PORTED | src/NATS.Server/ClientConnectionType.cs:15 | Added `ClientProtocolVersion.ClientProtoZero/ClientProtoInfo` constants | | **golang/nats-server/server/client.go — Exported Methods** | | | | | | (c) String() | client.go:547 | PORTED | src/NATS.Server/NatsClient.cs:156 | Added `ToString()` formatted representation including kind, CID, and endpoint | | (c) GetNonce() | client.go:557 | PORTED | src/NATS.Server/NatsClient.cs:141 | Added `GetNonce()` accessor returning nonce bytes | | (c) GetName() | client.go:565 | PORTED | src/NATS.Server/NatsClient.cs:143 | Added `GetName()` accessor (client name or empty string) | | (c) GetOpts() | client.go:573 | PORTED | src/NATS.Server/NatsClient.cs:58 (ClientOpts) | — | | (c) GetTLSConnectionState() | client.go:579 | PORTED | src/NATS.Server/NatsClient.cs:110 (TlsState) | TlsConnectionState type | | (c) RemoteAddress() | client.go:822 | PORTED | src/NATS.Server/NatsClient.cs:85-86 (RemoteIp, RemotePort) | Separate IP and port properties | | (c) Kind() | client.go:844 | PORTED | src/NATS.Server/NatsClient.cs:57 (Kind property) | — | | (c) RegisterUser() | client.go:981 | MISSING | — | User registration on client not implemented | | (c) RegisterNkeyUser() | client.go:1018 | MISSING | — | NKey user registration on client not implemented | | (c) Account() | client.go:6204 | PORTED | src/NATS.Server/NatsClient.cs:61 (Account property) | — | | (c) Error/Errorf/Debugf/Noticef/Tracef/Warnf | client.go:6586-6610 | PORTED | src/NATS.Server/NatsClient.cs:52 (_logger) | Uses ILogger structured logging | | (c) RateLimitErrorf/Warnf/Debugf | client.go:6610-6650 | MISSING | — | No rate-limited logging per client | | **golang/nats-server/server/client.go — Key Unexported Methods** | | | | | | (c) initClient() | client.go:704 | PORTED | src/NATS.Server/NatsClient.cs:116 (constructor) | CID assignment, outbound setup, subs map, trace level | | (c) readLoop() | client.go:1358 | PORTED | src/NATS.Server/NatsClient.cs:296 (FillPipeAsync + ProcessCommandsAsync) | System.IO.Pipelines instead of goroutine + byte buffer | | (c) writeLoop() | client.go:1274 | PORTED | src/NATS.Server/NatsClient.cs:785 (RunWriteLoopAsync) | Channel-based async write loop | | (c) processConnect() | client.go:2167 | PORTED | src/NATS.Server/NatsClient.cs:450 (ProcessConnectAsync) | JSON deserialization, auth, account binding | | (c) processPub() | client.go:2822 | PORTED | src/NATS.Server/NatsClient.cs:627 (ProcessPub) | Subject validation, permission check, router dispatch | | (c) processHeaderPub() | client.go:2745 | PORTED | src/NATS.Server/NatsClient.cs:660 | HPUB handling with header size split | | (c) parseSub() / processSub() | client.go:2898/2927 | PORTED | src/NATS.Server/NatsClient.cs:560 (ProcessSub) | Permission check, limit check, sublist insert | | (c) processUnsub() | client.go:3366 | PORTED | src/NATS.Server/NatsClient.cs:605 (ProcessUnsub) | Max messages auto-unsub, sublist remove | | (c) processPing() | client.go:2628 | PORTED | src/NATS.Server/NatsClient.cs:414 (Pong case in DispatchCommandAsync) | — | | (c) processPong() | client.go:2680 | PORTED | src/NATS.Server/NatsClient.cs:419-428 | RTT calculation, pingsOut reset | | (c) queueOutbound() | client.go:2448 | PORTED | src/NATS.Server/NatsClient.cs:140 (QueueOutbound) | Channel-based with pending bytes tracking | | (c) flushOutbound() | client.go:1618 | PORTED | src/NATS.Server/NatsClient.cs:785 (RunWriteLoopAsync drain) | Inline in write loop | | (c) flushSignal() | client.go:1996 | PORTED | src/NATS.Server/NatsClient.cs:197 (SignalFlushPending) | Interlocked counter | | (c) sendPing() | client.go:2577 | PORTED | src/NATS.Server/NatsClient.cs:888 | RTT start tracking, write PING | | (c) sendPong() | client.go:2540 | PORTED | src/NATS.Server/NatsClient.cs:397 | Write PONG bytes | | (c) sendRTTPing() | client.go:2548 | PORTED | src/NATS.Server/NatsClient.cs:887 | RTT start tick recording | | (c) sendErr() | client.go:2608 | PORTED | src/NATS.Server/NatsClient.cs:779 (SendErr) | — | | (c) sendOK() | client.go:2619 | PORTED | src/NATS.Server/NatsClient.cs:344 (OkBytes write on Verbose) | — | | (c) processPingTimer() | client.go:5537 | PORTED | src/NATS.Server/NatsClient.cs:853 (RunPingTimerAsync) | PeriodicTimer-based, stale detection | | (c) closeConnection() | client.go:5868 | PORTED | src/NATS.Server/NatsClient.cs:834 (CloseWithReasonAsync) | Async close with reason | | (c) markConnAsClosed() | client.go:1906 | PORTED | src/NATS.Server/NatsClient.cs:902 (MarkClosed) | Skip-flush flag, reason tracking | | (c) deliverMsg() | client.go:3620 | PARTIAL | src/NATS.Server/NatsClient.cs:692 (SendMessage) | Basic MSG/HMSG delivery. Missing: echo check, MQTT interop, GW reply mapping, stall wait | | (c) processInboundMsg() | client.go:4139 | PARTIAL | src/NATS.Server/NatsServer.cs:938 (ProcessMessage) | Routes to local subs; missing: route/gateway/leaf-specific inbound handlers | | (c) processInboundClientMsg() | client.go:4166 | PARTIAL | src/NATS.Server/NatsServer.cs:938 | Core pub path ported; missing: subject mapping, GW reply mapping, NRG prefix check | | (c) processMsgResults() | client.go:4932 | PARTIAL | src/NATS.Server/NatsServer.cs:938-1088 | Queue group selection ported; missing: route forwarding, gateway forwarding, queue name collection | | (c) stalledWait() | client.go:3575 | PORTED | src/NATS.Server/NatsClient.cs:1017 (StallGate class) | SemaphoreSlim-based stall gate | | (c) flushClients() | client.go:1324 | MISSING | — | Post-readLoop batch flush of producer clients (pcd) not implemented | | (c) handleWriteTimeout() | client.go:1842 | PARTIAL | src/NATS.Server/NatsClient.cs:807-813 | Slow consumer detection on timeout; missing: retry policy for routes | | (c) msgHeader() | client.go:3534 | PORTED | src/NATS.Server/NatsClient.cs:692 (SendMessage) | Inline MSG line construction | | (c) msgHeaderForRouteOrLeaf() | client.go:3447 | MISSING | — | RMSG header construction for routes/leafnodes | | (c) setPermissions() | client.go:1055 | PORTED | src/NATS.Server/NatsClient.cs:477 | Via ClientPermissions.Build(authResult.Permissions) | | (c) pubAllowed() | client.go:4052 | PORTED | src/NATS.Server/Auth/ClientPermissions.cs | IsPublishAllowed() | | (c) canSubscribe() | client.go:3230 | PORTED | src/NATS.Server/Auth/ClientPermissions.cs | IsSubscribeAllowed() | | (c) setExpiration() | client.go:1243 | PORTED | src/NATS.Server/NatsClient.cs:534-557 | Auth expiry timer via Task.Delay | | (c) authTimeout() | client.go:2383 | PORTED | src/NATS.Server/NatsClient.cs:238-253 | Auth timeout Task | | (c) authViolation() | client.go:2398 | PORTED | src/NATS.Server/NatsClient.cs:471-473 | Auth violation error + close | | (c) maxPayloadViolation() | client.go:2440 | PORTED | src/NATS.Server/NatsClient.cs:633-639 | Max payload check and close | | (c) maxSubsExceeded() | client.go:2433 | PORTED | src/NATS.Server/NatsClient.cs:571-576 | — | | (c) maxConnExceeded() | client.go:2428 | PARTIAL | src/NATS.Server/NatsServer.cs:610 | Server-level max conn check; no per-client method | | (c) subsAtLimit() | client.go:900 | PORTED | src/NATS.Server/NatsClient.cs:571 | MaxSubs check in ProcessSub | | (c) applyAccountLimits() | client.go:923 | PARTIAL | src/NATS.Server/NatsClient.cs:488-494 | Account client count check; missing: maxPayload/maxSubs per-account override | | (c) registerWithAccount() | client.go:854 | PORTED | src/NATS.Server/NatsClient.cs:480-494 | Account binding during connect | | (c) setTraceLevel() | client.go:695 | PORTED | src/NATS.Server/NatsClient.cs:68 (SetTraceMode) | — | | (c) clientType() | client.go:599 | PORTED | src/NATS.Server/NatsClient.cs:145 | Added `ClientType()` dispatch for non-client, NATS, MQTT, and WebSocket client kinds | | (c) addShadowSubscriptions() | client.go:3057 | MISSING | — | Account import shadow subscription system | | (c) pruneDenyCache() / prunePubPermsCache() / pruneReplyPerms() | client.go:4007-4019 | MISSING | — | Permission cache pruning | | (c) trackRemoteReply() / pruneRemoteTracking() | client.go:3915/3956 | MISSING | — | Reply tracking for latency | | (c) loadMsgDenyFilter() | client.go:1265 | MISSING | — | Message-level deny filter loading | | (c) generateClientInfoJSON() | client.go:2589 | PARTIAL | src/NATS.Server/NatsClient.cs:674 (SendInfo) | Generates INFO JSON; missing: per-client IP inclusion, connect_urls | | (c) updateS2AutoCompressionLevel() | client.go:2723 | MISSING | — | RTT-based compression adjustment | | (c) addToPCD() | client.go:3905 | MISSING | — | Producer client data batch tracking | | (c) collapsePtoNB() | client.go:1608 | NOT_APPLICABLE | — | Go-specific net.Buffers collapse; .NET uses Channel | | nbPoolGet/nbPoolPut | client.go:393-423 | NOT_APPLICABLE | — | Go-specific sync.Pool; .NET uses ArrayPool/MemoryPool | | **golang/nats-server/server/opts.go — Types** | | | | | | Options struct | opts.go:326 | PORTED | src/NATS.Server/NatsOptions.cs:8 | Core fields present. Missing: DontListen, NoLog, NoSigs, ProxyRequired, CustomAuth, CheckConfig, JetStreamTpm, resolverPreloads | | ClusterOpts struct | opts.go:64 | PORTED | src/NATS.Server/Configuration/ClusterOptions.cs | Core fields; missing: compression, pool size, pinned accounts, write timeout policy | | GatewayOpts struct | opts.go:114 | PORTED | src/NATS.Server/Configuration/GatewayOptions.cs | Core fields; missing: compression, reject unknown, send qsubs buf | | RemoteGatewayOpts struct | opts.go:145 | PORTED | src/NATS.Server/Configuration/GatewayOptions.cs:27 | — | | LeafNodeOpts struct | opts.go:154 | PORTED | src/NATS.Server/Configuration/LeafNodeOptions.cs:22 | Core fields; missing: compression, isolate subjects, min version | | RemoteLeafOpts struct | opts.go:218 | PORTED | src/NATS.Server/Configuration/LeafNodeOptions.cs:7 | — | | WebsocketOpts struct | opts.go:518 | PORTED | src/NATS.Server/NatsOptions.cs:138 (WebSocketOptions) | — | | MQTTOpts struct | opts.go:613 | PORTED | src/NATS.Server/MqttOptions.cs:8 | — | | TLSConfigOpts struct | opts.go:790 | PARTIAL | src/NATS.Server/NatsOptions.cs:96-107 | Flat TLS fields on NatsOptions; no TLSConfigOpts class | | OCSPConfig struct | opts.go:823 | PARTIAL | src/NATS.Server/NatsOptions.cs:110 (OcspConfig) | Basic config; missing: full OCSP mode selection | | AuthCallout struct | opts.go:308 | PORTED | src/NATS.Server/NatsOptions.cs:234 | Added auth callout DTO (`Issuer`, `Account`, `AuthUsers`, `XKey`, `AllowedAccounts`) | | JSLimitOpts struct | opts.go:289 | PORTED | src/NATS.Server/NatsOptions.cs:222 | Added JetStream account limit DTO fields used by options parity | | JSTpmOpts struct | opts.go:300 | NOT_APPLICABLE | — | TPM (Trusted Platform Module) not applicable to .NET | | ProxiesConfig struct | opts.go:832 | PORTED | src/NATS.Server/NatsOptions.cs:243 | Added proxy configuration DTO (`ProxiesConfig.Trusted` + `ProxyConfig.Key`) | | PinnedCertSet type | opts.go:59 | PORTED | src/NATS.Server/NatsOptions.cs:106 (TlsPinnedCerts HashSet) | — | | **golang/nats-server/server/opts.go — Exported Functions** | | | | | | ProcessConfigFile() | opts.go:870 | PORTED | src/NATS.Server/Configuration/ConfigProcessor.cs:15 | Full config file parsing | | ConfigureOptions() | opts.go:6023 | PORTED | src/NATS.Server.Host/Program.cs:25-137 | CLI flag parsing inline | | MergeOptions() | opts.go:5714 | PORTED | src/NATS.Server/Configuration/ConfigReloader.cs MergeCliOverrides | — | | RoutesFromStr() | opts.go:5797 | PORTED | src/NATS.Server/NatsOptions.cs:151 | Added parser for comma-delimited route URL strings with trimming and URI validation | | GenTLSConfig() | opts.go:5633 | PARTIAL | src/NATS.Server/Tls/ | TLS setup exists but not as a standalone GenTLSConfig function | | PrintTLSHelpAndDie() | opts.go:4886 | NOT_APPLICABLE | — | Go-specific CLI help | | NoErrOnUnknownFields() | opts.go:50 | PORTED | src/NATS.Server/NatsOptions.cs:144 | Added global toggle used by config parser to suppress unknown top-level field failures | | **golang/nats-server/server/opts.go — Exported Options Methods** | | | | | | (o) Clone() | opts.go:715 | PORTED | src/NATS.Server/NatsOptions.cs:166 | Added deep-copy clone behavior for common collections and pinned cert set | | (o) ProcessConfigFile() | opts.go:974 | PORTED | src/NATS.Server/Configuration/ConfigProcessor.cs:17 | — | | (o) ProcessConfigString() | opts.go:990 | PORTED | src/NATS.Server/NatsOptions.cs:196 | Added in-memory config parse/apply path and digest computation | | (o) ConfigDigest() | opts.go:1000 | PORTED | src/NATS.Server/NatsOptions.cs:203 | Added SHA-256-based config digest accessor | | **golang/nats-server/server/reload.go** | | | | | | FlagSnapshot var | reload.go:36 | PORTED | src/NATS.Server/NatsServer.cs:44-46 (_cliSnapshot, _cliFlags) | — | | option interface | reload.go:43 | PORTED | src/NATS.Server/Configuration/IConfigChange.cs | IConfigChange with Apply, IsLoggingChange, etc. | | noopOption / loggingOption / traceLevelOption | reload.go:77-129 | PORTED | src/NATS.Server/Configuration/IConfigChange.cs:42 (ConfigChange) | Simplified: single ConfigChange class with flags | | traceOption / debugOption / logfileOption / etc. | reload.go:131-200+ | PARTIAL | src/NATS.Server/Configuration/ConfigReloader.cs | Diff-based; individual option types not replicated, uses property comparison | | (s) Reload() | reload.go:1090 | PORTED | src/NATS.Server/NatsServer.cs:1623 (ReloadConfig) | Reads config file, diffs, applies | | (s) ReloadOptions() | reload.go:1111 | PORTED | src/NATS.Server/NatsServer.cs:1633 (ReloadConfigCore) | Config diff and apply | | reloadContext struct | reload.go:38 | MISSING | — | Reload context for cluster perms | | applyBoolFlags() | reload.go:1185 | PARTIAL | src/NATS.Server/Configuration/ConfigReloader.cs MergeCliOverrides | CLI override merge exists; no bool flag reflection | | **golang/nats-server/server/signal.go** | | | | | | SetProcessName() | signal.go:32 | NOT_APPLICABLE | — | Unix-specific process name | | (s) handleSignals() | signal.go:37 | PORTED | src/NATS.Server/NatsServer.cs:320 (HandleSignals) | PosixSignalRegistration for SIGINT/SIGTERM/SIGHUP/SIGUSR2 | | ProcessSignal() | signal.go:89 | NOT_APPLICABLE | — | Unix-specific kill/pgrep signal dispatch | | CommandToSignal() | signal.go:145 | NOT_APPLICABLE | — | Unix syscall.Signal mapping | | resolvePids() | signal.go:165 | NOT_APPLICABLE | — | Unix-specific pgrep | | **golang/nats-server/server/service.go** | | | | | | Run() | service.go:20 | PORTED | src/NATS.Server.Host/Program.cs:235 | StartAsync + WaitForShutdown | | isWindowsService() | service.go:26 | NOT_APPLICABLE | — | Build-tag guarded; not relevant for .NET | ### Summary Counts | Status | Count | |--------|-------| | PORTED | 165 | | PARTIAL | 19 | | MISSING | 24 | | NOT_APPLICABLE | 14 | | DEFERRED | 0 | | **Total** | **222** | --- ## 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/ -maxdepth 1 -name '*.cs' -type f -exec cat {} + | wc -l # Re-count .NET test LOC for this module find tests/NATS.Server.Tests/ -maxdepth 1 -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 core-server batch 4 parity closures: added `ActivePeers`, `StartProfiler`, `DisconnectClientByID`, `LDMClientByID`, `PortsInfo`, `UpdateServerINFOAndSendINFOToClients`, `GetConnectURLs`, and `GetNonLocalIPsIfHostIsIPAny` with targeted tests in `CoreServerGapParityTests`. | codex | | 2026-02-26 | Reclassified core server PID/ports-file parity rows: validated existing startup/shutdown PID and `.ports` file lifecycle implementation and updated `logPid`/`portFile` status to PORTED; refreshed `logPorts` residual note. | codex | | 2026-02-25 | File created with LLM analysis instructions | auto | | 2026-02-25 | Gap inventory populated: 222 symbols analyzed (123 PORTED, 30 PARTIAL, 55 MISSING, 14 NOT_APPLICABLE) across server.go, client.go, opts.go, reload.go, signal.go, service.go, main.go | claude-opus | | 2026-02-25 | Executed core-server batch 1 parity closures: added dedicated server URL/address/account-count/config-time/string helpers with targeted unit tests (`CoreServerGapParityTests`), and reclassified 14 rows (9 MISSING + 5 PARTIAL) to PORTED | codex | | 2026-02-25 | Executed core-server batch 3 options parity closures: added `Ports`, `CompressionModes`, `CompressionOpts`, `RoutesFromStr`, `NoErrOnUnknownFields`, `Clone`, `ProcessConfigString`, `ConfigDigest`, and DTOs (`JSLimitOpts`, `AuthCallout`, `ProxiesConfig`) with targeted tests (`CoreServerOptionsParityBatch3Tests`) | codex |