# Auth & Accounts — Gap Analysis > This file tracks what has and hasn't been ported from Go to .NET for the **Auth & Accounts** 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 Auth & Accounts - `accounts.go` is the **second-largest functional file** (~4,100 lines). It manages multi-tenant isolation with per-account Sublists, service latency tracking, and subject import/export mappings. - The Go implementation supports 6+ auth mechanisms. Check which are implemented in .NET's `Auth/` directory. - JWT handling in Go includes account claims, user claims, activation tokens, and revocation lists. - Service imports/exports allow cross-account message delivery — check `src/NATS.Server/Imports/`. --- ## Go Reference Files (Source) - `golang/nats-server/server/auth.go` — Auth mechanisms: username/password, token, NKeys (Ed25519), JWT, external auth callout, LDAP - `golang/nats-server/server/auth_callout.go` — External auth callout service - `golang/nats-server/server/nkey.go` — NKey (Ed25519) validation - `golang/nats-server/server/jwt.go` — JWT claims parsing, account/user claims - `golang/nats-server/server/accounts.go` — Multi-tenant account isolation (~4,100 lines). Each account has its own Sublist, client set, subject namespace. Supports exports/imports. ## Go Reference Files (Tests) - `golang/nats-server/server/auth_test.go` - `golang/nats-server/server/auth_callout_test.go` - `golang/nats-server/server/nkey_test.go` - `golang/nats-server/server/jwt_test.go` - `golang/nats-server/server/accounts_test.go` - `golang/nats-server/server/trust_test.go` ## .NET Implementation Files (Source) - `src/NATS.Server/Auth/` (all files including `Jwt/` subdirectory) - `src/NATS.Server/Imports/` (account import/export) ## .NET Implementation Files (Tests) - `tests/NATS.Server.Tests/Auth/` - `tests/NATS.Server.Tests/Accounts/` --- ## Gap Inventory ### auth.go (1,697 lines) | Go Symbol | Go File:Line | Status | .NET Equivalent | Notes | |-----------|:-------------|--------|:----------------|-------| | Authentication interface | golang/nats-server/server/auth.go:40 | PORTED | src/NATS.Server/Auth/IAuthenticator.cs:7 | Renamed to IAuthenticator; Check(ClientAuthentication) -> Authenticate(ClientAuthContext) | | ClientAuthentication interface | golang/nats-server/server/auth.go:46 | PORTED | src/NATS.Server/Auth/IAuthenticator.cs:12 | Mapped to ClientAuthContext record | | NkeyUser struct | golang/nats-server/server/auth.go:62 | PORTED | src/NATS.Server/Auth/NKeyUser.cs:3 | Added parity fields `Issued`, `AllowedConnectionTypes`, and `ProxyRequired` | | User struct | golang/nats-server/server/auth.go:73 | PORTED | src/NATS.Server/Auth/User.cs:3 | Added parity fields `AllowedConnectionTypes` and `ProxyRequired` | | User.clone() | golang/nats-server/server/auth.go:85 | NOT_APPLICABLE | — | .NET uses immutable init-only records; deep clone not needed | | NkeyUser.clone() | golang/nats-server/server/auth.go:106 | NOT_APPLICABLE | — | .NET uses immutable init-only records; deep clone not needed | | SubjectPermission struct | golang/nats-server/server/auth.go:127 | PORTED | src/NATS.Server/Auth/Permissions.cs:10 | | | ResponsePermission struct | golang/nats-server/server/auth.go:134 | PORTED | src/NATS.Server/Auth/Permissions.cs:16 | | | Permissions struct | golang/nats-server/server/auth.go:141 | PORTED | src/NATS.Server/Auth/Permissions.cs:3 | | | RoutePermissions struct | golang/nats-server/server/auth.go:150 | MISSING | — | Route-level permissions for cluster; needed for clustering | | SubjectPermission.clone() | golang/nats-server/server/auth.go:156 | NOT_APPLICABLE | — | .NET uses immutable records | | Permissions.clone() | golang/nats-server/server/auth.go:174 | NOT_APPLICABLE | — | .NET uses immutable records | | checkAuthforWarnings() | golang/nats-server/server/auth.go:196 | MISSING | — | Warns about plaintext passwords at startup | | assignGlobalAccountToOrphanUsers() | golang/nats-server/server/auth.go:226 | PORTED | src/NATS.Server/Auth/AuthService.cs:176 | `AuthService.Build` now normalizes orphan users/nkeys onto `$G` via `NormalizeUsers`/`NormalizeNKeys`. | | validateResponsePermissions() | golang/nats-server/server/auth.go:243 | PORTED | src/NATS.Server/Auth/AuthService.cs:222 | Added response-permission normalization: ensures publish allow-list exists, sets `MaxMsgs` default to `DefaultAllowResponseMaxMsgs`, and `Expires` default to `DefaultAllowResponseExpiration` when zero. | | configureAuthorization() | golang/nats-server/server/auth.go:266 | PARTIAL | src/NATS.Server/Auth/AuthService.cs:30 | AuthService.Build covers the priority chain; missing websocket/mqtt auth config, auth callout account validation | | buildNkeysAndUsersFromOptions() | golang/nats-server/server/auth.go:325 | PARTIAL | src/NATS.Server/Auth/AuthService.cs:31 | User/NKey map building, clone normalization, orphan account assignment, and response-permission defaulting now occur in `AuthService.Build`; remaining gaps are server-level warnings and broader router/gateway/leaf auth wiring. | | checkAuthentication() | golang/nats-server/server/auth.go:365 | PARTIAL | src/NATS.Server/Auth/AuthService.cs:97 | Only CLIENT kind is implemented; ROUTER, GATEWAY, LEAF auth missing | | isClientAuthorized() | golang/nats-server/server/auth.go:382 | PORTED | src/NATS.Server/Auth/AuthService.cs:97 | Core flow matches; missing accountConnectEvent | | matchesPinnedCert() | golang/nats-server/server/auth.go:405 | PORTED | src/NATS.Server/Tls/TlsHelper.cs:132 | `TlsHelper.MatchesPinnedCert(cert, pinned)` implements hash-based pinned-certificate validation; covered by targeted tests in `TlsHelperTests`. | | processUserPermissionsTemplate() | golang/nats-server/server/auth.go:427 | PORTED | src/NATS.Server/Auth/Jwt/PermissionTemplates.cs:36 | Full template expansion with cartesian product | | processClientOrLeafAuthentication() | golang/nats-server/server/auth.go:588 | PARTIAL | src/NATS.Server/Auth/AuthService.cs:97 | Core client auth flow ported; missing leaf node auth, proxy check integration, auth callout defer, JWT src/time validation | | proxyCheck() | golang/nats-server/server/auth.go:1153 | PARTIAL | src/NATS.Server/Auth/ProxyAuthenticator.cs:3 | Basic proxy prefix auth exists; full NKey signature-based proxy verification missing | | getTLSAuthDCs() | golang/nats-server/server/auth.go:1198 | PORTED | src/NATS.Server/Auth/TlsMapAuthenticator.cs:68 | Added DC extraction helper for TLS auth subject matching (`GetTlsAuthDcs`). | | tlsMapAuthFn type | golang/nats-server/server/auth.go:1218 | NOT_APPLICABLE | — | Go function type; .NET uses delegate/lambda | | checkClientTLSCertSubject() | golang/nats-server/server/auth.go:1220 | PARTIAL | src/NATS.Server/Auth/TlsMapAuthenticator.cs:25 | Added DN/CN plus SAN email/DNS/URI matching and DC-augmented RDN matching; LDAP raw-subject DN parsing and full Go callback behavior remain unported. | | dnsAltNameLabels() | golang/nats-server/server/auth.go:1316 | PORTED | src/NATS.Server/Auth/TlsMapAuthenticator.cs:85 | Added DNS alt-name label splitter with lowercase normalization. | | dnsAltNameMatches() | golang/nats-server/server/auth.go:1321 | PORTED | src/NATS.Server/Auth/TlsMapAuthenticator.cs:93 | Added RFC6125-style DNS alt-name matching helper (left-most `*` wildcard only). | | isRouterAuthorized() | golang/nats-server/server/auth.go:1349 | MISSING | — | Cluster route authentication | | isGatewayAuthorized() | golang/nats-server/server/auth.go:1390 | MISSING | — | Gateway authentication | | registerLeafWithAccount() | golang/nats-server/server/auth.go:1425 | MISSING | — | Leaf node account registration | | isLeafNodeAuthorized() | golang/nats-server/server/auth.go:1443 | MISSING | — | Leaf node authentication | | isBcrypt() | golang/nats-server/server/auth.go:1569 | PORTED | src/NATS.Server/Auth/UserPasswordAuthenticator.cs:65 | Simplified to StartsWith("$2") check | | comparePasswords() | golang/nats-server/server/auth.go:1577 | PORTED | src/NATS.Server/Auth/UserPasswordAuthenticator.cs:46 | bcrypt + constant-time comparison | | validateAuth() | golang/nats-server/server/auth.go:1595 | MISSING | — | Top-level auth config validation | | validateAllowedConnectionTypes() | golang/nats-server/server/auth.go:1612 | PARTIAL | src/NATS.Server/Auth/Jwt/JwtConnectionTypes.cs:18 | Validation logic exists in JWT context; not used for config-level validation | | validateNoAuthUser() | golang/nats-server/server/auth.go:1631 | MISSING | — | Validates no_auth_user exists in Users/Nkeys | | validateProxies() | golang/nats-server/server/auth.go:1657 | MISSING | — | Validates proxy trusted keys are valid NKeys | | processProxiesTrustedKeys() | golang/nats-server/server/auth.go:1672 | MISSING | — | Builds NKey keypairs from proxy trusted keys | | getAuthErrClosedState() | golang/nats-server/server/auth.go:1688 | MISSING | — | Maps auth errors to connection close states | ### auth_callout.go (497 lines) | Go Symbol | Go File:Line | Status | .NET Equivalent | Notes | |-----------|:-------------|--------|:----------------|-------| | AuthCalloutSubject const | golang/nats-server/server/auth_callout.go:30 | PORTED | src/NATS.Server/Auth/ExternalAuthCalloutAuthenticator.cs:5 | Added `$SYS.REQ.USER.AUTH` constant | | AuthRequestSubject const | golang/nats-server/server/auth_callout.go:31 | PORTED | src/NATS.Server/Auth/ExternalAuthCalloutAuthenticator.cs:6 | Added `nats-authorization-request` constant | | AuthRequestXKeyHeader const | golang/nats-server/server/auth_callout.go:32 | PORTED | src/NATS.Server/Auth/ExternalAuthCalloutAuthenticator.cs:7 | Added `Nats-Server-Xkey` header constant | | processClientOrLeafCallout() | golang/nats-server/server/auth_callout.go:36 | PARTIAL | src/NATS.Server/Auth/ExternalAuthCalloutAuthenticator.cs:3 | .NET has a simplified external auth callout via IExternalAuthClient interface; Go implementation uses internal NATS messaging ($SYS subjects), JWT encoding/decoding, XKey encryption, replay prevention — all missing from .NET | | fillClientInfo() | golang/nats-server/server/auth_callout.go:456 | MISSING | — | Fills jwt.ClientInformation for auth callout requests | | fillConnectOpts() | golang/nats-server/server/auth_callout.go:477 | MISSING | — | Fills jwt.ConnectOptions for auth callout requests | ### nkey.go (47 lines) | Go Symbol | Go File:Line | Status | .NET Equivalent | Notes | |-----------|:-------------|--------|:----------------|-------| | nonceRawLen const | golang/nats-server/server/nkey.go:23 | PORTED | src/NATS.Server/Auth/AuthService.cs:147 | Uses 11-byte raw nonce | | nonceLen const | golang/nats-server/server/nkey.go:24 | NOT_APPLICABLE | — | Derived constant (base64 length) | | NonceRequired() | golang/nats-server/server/nkey.go:28 | PORTED | src/NATS.Server/Auth/AuthService.cs:18 | Property on AuthService | | nonceRequired() | golang/nats-server/server/nkey.go:36 | PORTED | src/NATS.Server/Auth/AuthService.cs:34 | Checked during Build based on NKeys/TrustedKeys | | generateNonce() | golang/nats-server/server/nkey.go:42 | PORTED | src/NATS.Server/Auth/AuthService.cs:146 | GenerateNonce + EncodeNonce use RandomNumberGenerator.Fill | ### jwt.go (245 lines) | Go Symbol | Go File:Line | Status | .NET Equivalent | Notes | |-----------|:-------------|--------|:----------------|-------| | jwtPrefix const | golang/nats-server/server/jwt.go:29 | PORTED | src/NATS.Server/Auth/Jwt/NatsJwt.cs:17 | "eyJ" constant | | ReadOperatorJWT() | golang/nats-server/server/jwt.go:32 | MISSING | — | Reads operator JWT from file/inline string | | readOperatorJWT() | golang/nats-server/server/jwt.go:37 | MISSING | — | Internal helper for operator JWT parsing | | wipeSlice() | golang/nats-server/server/jwt.go:61 | NOT_APPLICABLE | — | Go-specific memory wiping; .NET has CryptographicOperations.ZeroMemory | | validateTrustedOperators() | golang/nats-server/server/jwt.go:70 | MISSING | — | Validates operator JWT config (keys, resolver, system account, version) | | validateSrc() | golang/nats-server/server/jwt.go:182 | MISSING | — | Validates user JWT source CIDR restrictions | | validateTimes() | golang/nats-server/server/jwt.go:204 | MISSING | — | Validates user JWT time-of-day restrictions | ### accounts.go (4,774 lines) | Go Symbol | Go File:Line | Status | .NET Equivalent | Notes | |-----------|:-------------|--------|:----------------|-------| | globalAccountName const | golang/nats-server/server/accounts.go:44 | PORTED | src/NATS.Server/Auth/Account.cs:9 | Account.GlobalAccountName = "$G" | | Account struct | golang/nats-server/server/accounts.go:52 | PARTIAL | src/NATS.Server/Auth/Account.cs:7 | Core fields ported (Name, SubList, limits, clients, revocations, exports, imports). Missing: stats (gw/rt/ln), gwReplyMapping, claimJWT, mappings, hasMapped, lleafs, leafClusters, js/jsLimits, nrgAccount, extAuth, defaultPerms, nameTag, tags, traceDest | | limits struct | golang/nats-server/server/accounts.go:127 | PARTIAL | src/NATS.Server/Auth/Account.cs:15-19 | MaxConnections, MaxSubscriptions ported; missing MaxPayload, MaxLeafNodeConnections, MaxRemoteConnections | | sconns struct | golang/nats-server/server/accounts.go:136 | MISSING | — | Remote server connection count tracking | | streamImport struct | golang/nats-server/server/accounts.go:142 | PORTED | src/NATS.Server/Imports/StreamImport.cs:7 | | | ClientInfoHdr const | golang/nats-server/server/accounts.go:157 | PORTED | src/NATS.Server/Auth/Account.cs:11 | Added `Account.ClientInfoHdr = "Nats-Request-Info"` constant. | | serviceImport struct | golang/nats-server/server/accounts.go:160 | PORTED | src/NATS.Server/Imports/ServiceImport.cs:6 | | | serviceRespEntry struct | golang/nats-server/server/accounts.go:187 | MISSING | — | TTL-tracked response entries | | ServiceRespType enum | golang/nats-server/server/accounts.go:193 | PORTED | src/NATS.Server/Imports/ServiceResponseType.cs:3 | Singleton, Streamed, Chunked | | ServiceRespType.String() | golang/nats-server/server/accounts.go:203 | NOT_APPLICABLE | — | Go Stringer interface; .NET enum has ToString() | | exportAuth struct | golang/nats-server/server/accounts.go:217 | PORTED | src/NATS.Server/Imports/ExportAuth.cs:5 | TokenRequired, AccountPosition, ApprovedAccounts, RevokedAccounts | | streamExport struct | golang/nats-server/server/accounts.go:225 | PORTED | src/NATS.Server/Imports/StreamExport.cs:3 | | | serviceExport struct | golang/nats-server/server/accounts.go:230 | PORTED | src/NATS.Server/Imports/ServiceExport.cs:5 | | | serviceLatency struct | golang/nats-server/server/accounts.go:245 | PORTED | src/NATS.Server/Imports/ServiceLatency.cs:3 | | | exportMap struct | golang/nats-server/server/accounts.go:251 | PORTED | src/NATS.Server/Imports/ExportMap.cs:3 | | | importMap struct | golang/nats-server/server/accounts.go:259 | PORTED | src/NATS.Server/Imports/ImportMap.cs:5 | | | NewAccount() | golang/nats-server/server/accounts.go:266 | PORTED | src/NATS.Server/Auth/Account.cs:91 | Account constructor | | Account.String() | golang/nats-server/server/accounts.go:275 | NOT_APPLICABLE | — | Trivial Go Stringer | | Account.setTraceDest() | golang/nats-server/server/accounts.go:279 | MISSING | — | Message tracing destination | | Account.getTraceDestAndSampling() | golang/nats-server/server/accounts.go:285 | MISSING | — | Message tracing config | | Account.shallowCopy() | golang/nats-server/server/accounts.go:298 | MISSING | — | Shallow copy for account reload | | Account.nextEventID() | golang/nats-server/server/accounts.go:354 | MISSING | — | NUID-based event ID generation | | Account.getClientsLocked() | golang/nats-server/server/accounts.go:363 | MISSING | — | Returns client slice under lock | | Account.getClients() | golang/nats-server/server/accounts.go:375 | MISSING | — | Returns client slice with lock acquisition | | Account.getExternalClientsLocked() | golang/nats-server/server/accounts.go:384 | MISSING | — | Returns only external clients | | Account.updateRemoteServer() | golang/nats-server/server/accounts.go:399 | MISSING | — | Cluster remote server tracking | | Account.removeRemoteServer() | golang/nats-server/server/accounts.go:446 | MISSING | — | Cluster remote server removal | | Account.expectedRemoteResponses() | golang/nats-server/server/accounts.go:460 | MISSING | — | Expected responses from remote servers | | Account.clearEventing() | golang/nats-server/server/accounts.go:472 | MISSING | — | Clears event timers | | Account.GetName() | golang/nats-server/server/accounts.go:484 | PORTED | src/NATS.Server/Auth/Account.cs:12 | Name property | | Account.NumConnections() | golang/nats-server/server/accounts.go:519 | PORTED | src/NATS.Server/Auth/Account.cs:96 | ClientCount property | | Account.NumRemoteConnections() | golang/nats-server/server/accounts.go:528 | MISSING | — | Remote connection count (clustering) | | Account.NumLocalConnections() | golang/nats-server/server/accounts.go:537 | PORTED | src/NATS.Server/Auth/Account.cs:96 | ClientCount is local only | | Account.MaxTotalConnectionsReached() | golang/nats-server/server/accounts.go:563 | PORTED | src/NATS.Server/Auth/Account.cs:103 | AddClient checks MaxConnections | | Account.MaxActiveConnections() | golang/nats-server/server/accounts.go:575 | PORTED | src/NATS.Server/Auth/Account.cs:15 | MaxConnections property | | Account.MaxTotalLeafNodesReached() | golang/nats-server/server/accounts.go:583 | MISSING | — | Leaf node connection limits | | Account.NumLeafNodes() | golang/nats-server/server/accounts.go:599 | MISSING | — | Leaf node count | | Account.NumRemoteLeafNodes() | golang/nats-server/server/accounts.go:608 | MISSING | — | Remote leaf count | | Account.MaxActiveLeafNodes() | golang/nats-server/server/accounts.go:618 | MISSING | — | Leaf node limit | | Account.RoutedSubs() | golang/nats-server/server/accounts.go:627 | MISSING | — | Routed subscription count | | Account.TotalSubs() | golang/nats-server/server/accounts.go:634 | PORTED | src/NATS.Server/Auth/Account.cs:97 | SubscriptionCount | | Account.shouldLogMaxSubErr() | golang/nats-server/server/accounts.go:643 | MISSING | — | Rate-limited logging for max sub errors | | MapDest struct | golang/nats-server/server/accounts.go:660 | MISSING | — | Subject mapping destination with weight | | NewMapDest() | golang/nats-server/server/accounts.go:666 | MISSING | — | MapDest constructor | | destination struct | golang/nats-server/server/accounts.go:671 | MISSING | — | Internal mapping destination | | mapping struct | golang/nats-server/server/accounts.go:677 | MISSING | — | Subject mapping with weighted destinations | | Account.AddMapping() | golang/nats-server/server/accounts.go:686 | MISSING | — | Subject remapping (1:1) | | Account.AddWeightedMappings() | golang/nats-server/server/accounts.go:691 | MISSING | — | Weighted subject mappings | | Account.RemoveMapping() | golang/nats-server/server/accounts.go:810 | MISSING | — | Remove a subject mapping | | Account.hasMappings() | golang/nats-server/server/accounts.go:839 | MISSING | — | Check for subject mappings | | Account.selectMappedSubject() | golang/nats-server/server/accounts.go:848 | MISSING | — | Select destination via weighted mapping | | Account.SubscriptionInterest() | golang/nats-server/server/accounts.go:929 | PORTED | src/NATS.Server/Auth/Account.cs:797 | Added direct parity API: returns `Interest(subject) > 0`. | | Account.Interest() | golang/nats-server/server/accounts.go:934 | PORTED | src/NATS.Server/Auth/Account.cs:803 | Added matcher-count API backed by `SubList.NumInterest(subject)` (plain + queue). | | Account.addClient() | golang/nats-server/server/accounts.go:947 | PORTED | src/NATS.Server/Auth/Account.cs:103 | AddClient checks MaxConnections | | Account.registerLeafNodeCluster() | golang/nats-server/server/accounts.go:986 | MISSING | — | Leaf node cluster registration | | Account.hasLeafNodeCluster() | golang/nats-server/server/accounts.go:996 | MISSING | — | Check for leaf node cluster | | Account.isLeafNodeClusterIsolated() | golang/nats-server/server/accounts.go:1004 | MISSING | — | Leaf cluster isolation check | | Account.removeLeafNode() | golang/nats-server/server/accounts.go:1018 | MISSING | — | Remove leaf node from account | | Account.removeClient() | golang/nats-server/server/accounts.go:1038 | PORTED | src/NATS.Server/Auth/Account.cs:111 | RemoveClient | | setExportAuth() | golang/nats-server/server/accounts.go:1075 | PARTIAL | src/NATS.Server/Imports/ExportAuth.cs:5 | ExportAuth struct exists; setExportAuth helper logic partially in Account.AddServiceExport | | Account.AddServiceExport() | golang/nats-server/server/accounts.go:1101 | PORTED | src/NATS.Server/Auth/Account.cs:309 | AddServiceExport | | Account.AddServiceExportWithResponse() | golang/nats-server/server/accounts.go:1111 | PORTED | src/NATS.Server/Auth/Account.cs:309 | Included in AddServiceExport with responseType param | | Account.TrackServiceExport() | golang/nats-server/server/accounts.go:1163 | PARTIAL | src/NATS.Server/Auth/ServiceLatencyTracker.cs:7 | Tracker exists but wiring to export subjects via NATS internal pub missing | | Account.TrackServiceExportWithSampling() | golang/nats-server/server/accounts.go:1169 | PARTIAL | src/NATS.Server/Imports/ServiceLatency.cs:5 | SamplingPercentage field exists; full integration missing | | Account.UnTrackServiceExport() | golang/nats-server/server/accounts.go:1238 | MISSING | — | Remove latency tracking from export | | Account.IsExportService() | golang/nats-server/server/accounts.go:1281 | PORTED | src/NATS.Server/Auth/Account.cs:298 | HasServiceExport | | Account.IsExportServiceTracking() | golang/nats-server/server/accounts.go:1298 | MISSING | — | Check if latency tracking is enabled for export | | ServiceLatency struct | golang/nats-server/server/accounts.go:1326 | PORTED | src/NATS.Server/Imports/LatencyTracker.cs:5 | ServiceLatencyMsg | | ServiceLatencyType const | golang/nats-server/server/accounts.go:1340 | PORTED | src/NATS.Server/Imports/LatencyTracker.cs:8 | Type field default | | ServiceLatency.NATSTotalTime() | golang/nats-server/server/accounts.go:1343 | MISSING | — | Total time computation | | ServiceLatency.merge() | golang/nats-server/server/accounts.go:1354 | MISSING | — | Merge latency metrics | | sanitizeLatencyMetric() | golang/nats-server/server/accounts.go:1370 | MISSING | — | Sanitize negative latencies | | remoteLatency struct | golang/nats-server/server/accounts.go:1380 | MISSING | — | Remote latency for cluster | | Account.sendLatencyResult() | golang/nats-server/server/accounts.go:1388 | MISSING | — | Publish latency result to tracking subject | | Account.sendBadRequestTrackingLatency() | golang/nats-server/server/accounts.go:1401 | MISSING | — | Track bad request latency | | Account.sendReplyInterestLostTrackLatency() | golang/nats-server/server/accounts.go:1414 | MISSING | — | Track lost reply interest | | Account.sendBackendErrorTrackingLatency() | golang/nats-server/server/accounts.go:1430 | MISSING | — | Track backend error latency | | Account.sendTrackingLatency() | golang/nats-server/server/accounts.go:1458 | MISSING | — | Core latency tracking send | | updateAllClientsServiceExportResponseTime() | golang/nats-server/server/accounts.go:1527 | MISSING | — | Update client response times | | Account.lowestServiceExportResponseTime() | golang/nats-server/server/accounts.go:1542 | MISSING | — | Find lowest response time | | Account.AddServiceImportWithClaim() | golang/nats-server/server/accounts.go:1554 | PARTIAL | src/NATS.Server/Auth/Account.cs:338 | AddServiceImport exists; JWT claim-based import missing | | addServiceImportWithClaim() | golang/nats-server/server/accounts.go:1560 | PARTIAL | src/NATS.Server/Auth/Account.cs:338 | Core logic ported; missing claim validation, activation token handling | | MaxAccountCycleSearchDepth const | golang/nats-server/server/accounts.go:1593 | MISSING | — | Depth limit for cycle detection | | Account.serviceImportFormsCycle() | golang/nats-server/server/accounts.go:1595 | PORTED | src/NATS.Server/Auth/AccountImportExport.cs:18 | DetectCycle | | Account.checkServiceImportsForCycles() | golang/nats-server/server/accounts.go:1599 | PORTED | src/NATS.Server/Auth/AccountImportExport.cs:18 | DFS cycle detection | | Account.streamImportFormsCycle() | golang/nats-server/server/accounts.go:1627 | PORTED | src/NATS.Server/Auth/Account.cs:405 | StreamImportFormsCycle | | Account.hasServiceExportMatching() | golang/nats-server/server/accounts.go:1632 | PORTED | src/NATS.Server/Auth/Account.cs:298 | HasServiceExport | | Account.hasStreamExportMatching() | golang/nats-server/server/accounts.go:1642 | MISSING | — | Check if stream export matches subject | | Account.checkStreamImportsForCycles() | golang/nats-server/server/accounts.go:1651 | PORTED | src/NATS.Server/Auth/Account.cs:412 | DetectStreamImportCycle (private) | | Account.SetServiceImportSharing() | golang/nats-server/server/accounts.go:1686 | MISSING | — | Enable/disable service import sharing | | Account.AddServiceImport() | golang/nats-server/server/accounts.go:1715 | PORTED | src/NATS.Server/Auth/Account.cs:338 | | | Account.NumPendingReverseResponses() | golang/nats-server/server/accounts.go:1721 | PORTED | src/NATS.Server/Auth/Account.cs:772 | ReverseResponseMapCount | | Account.NumPendingAllResponses() | golang/nats-server/server/accounts.go:1728 | PORTED | src/NATS.Server/Auth/Account.cs:813 | Added parity API delegating to `NumPendingResponses("")`. | | Account.NumPendingResponses() | golang/nats-server/server/accounts.go:1736 | PORTED | src/NATS.Server/Auth/Account.cs:820 | Added filtered/aggregate pending-response counter over `Exports.Responses`, keyed by matched service export. | | Account.NumServiceImports() | golang/nats-server/server/accounts.go:1756 | PORTED | src/NATS.Server/Auth/Account.cs:843 | Added count of configured service import subject keys (`Imports.Services.Count`). | | rsiReason enum | golang/nats-server/server/accounts.go:1763 | PORTED | src/NATS.Server/Auth/Account.cs:1047 | Added `ResponseServiceImportRemovalReason` enum (`Ok`, `NoDelivery`, `Timeout`). | | Account.removeRespServiceImport() | golang/nats-server/server/accounts.go:1772 | PARTIAL | src/NATS.Server/Auth/Account.cs:849 | Added `RemoveRespServiceImport(..., reason)` and reason enum wiring; still missing Go's reverse-entry cleanup and reason-driven latency/metrics side effects. | | Account.getServiceImportForAccountLocked() | golang/nats-server/server/accounts.go:1795 | MISSING | — | Find service import by dest account + subject | | Account.removeServiceImport() | golang/nats-server/server/accounts.go:1812 | PORTED | src/NATS.Server/Auth/Account.cs:366 | RemoveServiceImport | | Account.addReverseRespMapEntry() | golang/nats-server/server/accounts.go:1858 | PORTED | src/NATS.Server/Auth/Account.cs:752 | AddReverseRespMapEntry | | Account.checkForReverseEntries() | golang/nats-server/server/accounts.go:1872 | PARTIAL | src/NATS.Server/Auth/Account.cs:761 | Lookup exists; missing recursive check + interest validation | | Account.serviceImportShadowed() | golang/nats-server/server/accounts.go:2015 | PORTED | src/NATS.Server/Auth/Account.cs:786 | ServiceImportShadowed | | Account.serviceImportExists() | golang/nats-server/server/accounts.go:2031 | MISSING | — | Check if service import exists by dest account + from | | Account.addServiceImport() | golang/nats-server/server/accounts.go:2041 | PARTIAL | src/NATS.Server/Auth/Account.cs:338 | Core add logic; missing claim/activation token handling | | Account.unsubscribeInternal() | golang/nats-server/server/accounts.go:2130 | MISSING | — | Internal subscription cleanup | | Account.subscribeServiceImportResponse() | golang/nats-server/server/accounts.go:2137 | MISSING | — | Response subscription for service import | | Account.subscribeInternalEx() | golang/nats-server/server/accounts.go:2141 | MISSING | — | Internal subscription with routing flag | | Account.addServiceImportSub() | golang/nats-server/server/accounts.go:2156 | MISSING | — | Wire up service import subscription | | Account.removeAllServiceImportSubs() | golang/nats-server/server/accounts.go:2190 | MISSING | — | Remove all service import subscriptions | | Account.addAllServiceImportSubs() | golang/nats-server/server/accounts.go:2215 | MISSING | — | Add all service import subscriptions | | shouldSample() | golang/nats-server/server/accounts.go:2279 | PORTED | src/NATS.Server/Imports/LatencyTracker.cs:28 | LatencyTracker.ShouldSample | | Account.processServiceImportResponse() | golang/nats-server/server/accounts.go:2358 | MISSING | — | Handle incoming response for service import | | Account.createRespWildcard() | golang/nats-server/server/accounts.go:2380 | MISSING | — | Create wildcard reply prefix | | isTrackedReply() | golang/nats-server/server/accounts.go:2391 | MISSING | — | Check if reply is for a tracked service | | Account.newServiceReply() | golang/nats-server/server/accounts.go:2398 | PARTIAL | src/NATS.Server/Imports/ResponseRouter.cs:19 | GenerateReplyPrefix exists | | serviceExport.setResponseThresholdTimer() | golang/nats-server/server/accounts.go:2446 | MISSING | — | Timer for response threshold | | serviceExport.clearResponseThresholdTimer() | golang/nats-server/server/accounts.go:2454 | MISSING | — | Clear response threshold timer | | serviceExport.checkExpiredResponses() | golang/nats-server/server/accounts.go:2465 | MISSING | — | Clean up expired responses | | Account.ServiceExportResponseThreshold() | golang/nats-server/server/accounts.go:2510 | PORTED | src/NATS.Server/Auth/Account.cs:468 | GetServiceResponseThreshold | | Account.SetServiceExportResponseThreshold() | golang/nats-server/server/accounts.go:2522 | PORTED | src/NATS.Server/Auth/Account.cs:461 | SetServiceResponseThreshold | | Account.SetServiceExportAllowTrace() | golang/nats-server/server/accounts.go:2549 | MISSING | — | Enable tracing on service export | | Account.addRespServiceImport() | golang/nats-server/server/accounts.go:2562 | PORTED | src/NATS.Server/Imports/ResponseRouter.cs:33 | CreateResponseImport | | Account.AddStreamImportWithClaim() | golang/nats-server/server/accounts.go:2598 | PARTIAL | src/NATS.Server/Auth/Account.cs:371 | AddStreamImport exists; claim validation missing | | Account.AddMappedStreamImport() | golang/nats-server/server/accounts.go:2629 | PORTED | src/NATS.Server/Auth/Account.cs:371 | AddStreamImport with 'to' parameter | | Account.isStreamImportDuplicate() | golang/nats-server/server/accounts.go:2694 | MISSING | — | Duplicate stream import detection | | Account.AddStreamImport() | golang/nats-server/server/accounts.go:2704 | PORTED | src/NATS.Server/Auth/Account.cs:371 | | | IsPublicExport var | golang/nats-server/server/accounts.go:2709 | MISSING | — | Sentinel for public export | | Account.AddStreamExport() | golang/nats-server/server/accounts.go:2713 | PORTED | src/NATS.Server/Auth/Account.cs:323 | AddStreamExport | | Account.checkStreamImportAuthorized() | golang/nats-server/server/accounts.go:2746 | PARTIAL | src/NATS.Server/Imports/ExportAuth.cs:12 | IsAuthorized checks approved accounts; missing token/claim validation | | Account.checkAuth() | golang/nats-server/server/accounts.go:2761 | PARTIAL | src/NATS.Server/Imports/ExportAuth.cs:12 | Basic approval; missing token validation and wildcard matching | | Account.checkStreamExportApproved() | golang/nats-server/server/accounts.go:2782 | PARTIAL | src/NATS.Server/Imports/ExportAuth.cs:12 | Via IsAuthorized; missing wildcard subject matching | | Account.checkServiceExportApproved() | golang/nats-server/server/accounts.go:2809 | PARTIAL | src/NATS.Server/Imports/ExportAuth.cs:12 | Via IsAuthorized; missing wildcard subject matching | | Account.getServiceExport() | golang/nats-server/server/accounts.go:2837 | PORTED | src/NATS.Server/Auth/Account.cs:267 | GetExactServiceExport | | Account.getWildcardServiceExport() | golang/nats-server/server/accounts.go:2849 | PORTED | src/NATS.Server/Auth/Account.cs:279 | GetWildcardServiceExport | | Account.streamActivationExpired() | golang/nats-server/server/accounts.go:2860 | PARTIAL | src/NATS.Server/Auth/Account.cs:613 | IsActivationExpired exists; missing auto-removal and re-check logic | | Account.serviceActivationExpired() | golang/nats-server/server/accounts.go:2895 | PARTIAL | src/NATS.Server/Auth/Account.cs:613 | IsActivationExpired exists; missing auto-removal | | Account.activationExpired() | golang/nats-server/server/accounts.go:2920 | PARTIAL | src/NATS.Server/Auth/Account.cs:620 | GetExpiredActivations + RemoveExpiredActivations | | isRevoked() | golang/nats-server/server/accounts.go:2929 | PORTED | src/NATS.Server/Auth/Account.cs:52 | IsUserRevoked | | Account.checkActivation() | golang/nats-server/server/accounts.go:2943 | PARTIAL | src/NATS.Server/Auth/Account.cs:598 | CheckActivationExpiry exists; missing issuer trust chain validation | | Account.isIssuerClaimTrusted() | golang/nats-server/server/accounts.go:2988 | MISSING | — | Validates issuer against account signing keys | | Account.checkStreamImportsEqual() | golang/nats-server/server/accounts.go:3011 | MISSING | — | Equality check for reload | | Account.checkStreamExportsEqual() | golang/nats-server/server/accounts.go:3036 | MISSING | — | Equality check for reload | | isStreamExportEqual() | golang/nats-server/server/accounts.go:3054 | MISSING | — | Stream export comparison | | Account.checkServiceExportsEqual() | golang/nats-server/server/accounts.go:3067 | MISSING | — | Equality check for reload | | isServiceExportEqual() | golang/nats-server/server/accounts.go:3085 | MISSING | — | Service export comparison | | isExportAuthEqual() | golang/nats-server/server/accounts.go:3121 | MISSING | — | Export auth comparison | | Account.checkServiceImportAuthorized() | golang/nats-server/server/accounts.go:3148 | PARTIAL | src/NATS.Server/Imports/ExportAuth.cs:12 | Via IsAuthorized; missing token validation | | Account.IsExpired() | golang/nats-server/server/accounts.go:3165 | PORTED | src/NATS.Server/Auth/Account.cs:523 | IsExpired property | | Account.expiredTimeout() | golang/nats-server/server/accounts.go:3170 | MISSING | — | Timer callback for expired accounts | | Account.setExpirationTimer() | golang/nats-server/server/accounts.go:3184 | PARTIAL | src/NATS.Server/Auth/Account.cs:553 | SetExpiration exists; actual Timer not created | | Account.clearExpirationTimer() | golang/nats-server/server/accounts.go:3189 | PARTIAL | src/NATS.Server/Auth/Account.cs:557 | ClearExpiration exists; no Timer to clear | | Account.checkUserRevoked() | golang/nats-server/server/accounts.go:3199 | PORTED | src/NATS.Server/Auth/Account.cs:52 | IsUserRevoked | | Account.failBearer() | golang/nats-server/server/accounts.go:3206 | MISSING | — | Check if account disallows bearer tokens | | Account.checkExpiration() | golang/nats-server/server/accounts.go:3213 | PARTIAL | src/NATS.Server/Auth/Account.cs:523 | IsExpired check exists; missing timer setup | | Account.hasIssuer() | golang/nats-server/server/accounts.go:3235 | MISSING | — | Check signing keys with scope | | Account.getLDSubject() | golang/nats-server/server/accounts.go:3249 | MISSING | — | Leaf node deny subject | | SetAccountResolver() | golang/nats-server/server/accounts.go:3267 | MISSING | — | Server-level resolver assignment | | AccountResolver() | golang/nats-server/server/accounts.go:3274 | MISSING | — | Server-level resolver getter | | Account.isClaimAccount() | golang/nats-server/server/accounts.go:3283 | MISSING | — | Check if account backed by JWT | | UpdateAccountClaims() | golang/nats-server/server/accounts.go:3290 | PARTIAL | src/NATS.Server/Auth/Account.cs:684 | UpdateAccountClaims exists; missing full claim application (exports/imports rebuild, signing keys, external auth, mappings) | | updateAccountClaimsWithRefresh() | golang/nats-server/server/accounts.go:3374 | PARTIAL | src/NATS.Server/Auth/Account.cs:684 | Diff-based update exists; missing export/import refresh, dependent account refresh | | buildInternalAccount() | golang/nats-server/server/accounts.go:3957 | MISSING | — | Build account from JWT claims | | buildPermissionsFromJwt() | golang/nats-server/server/accounts.go:3979 | PORTED | src/NATS.Server/Auth/JwtAuthenticator.cs:117 | Inline in JwtAuthenticator.Authenticate | | buildInternalNkeyUser() | golang/nats-server/server/accounts.go:4012 | MISSING | — | Build NkeyUser from JWT user claims | | fetchAccount() | golang/nats-server/server/accounts.go:4027 | MISSING | — | Fetch account JWT with NKey validation | | AccountResolver interface | golang/nats-server/server/accounts.go:4035 | PARTIAL | src/NATS.Server/Auth/Jwt/AccountResolver.cs:14 | IAccountResolver has Fetch/Store/IsReadOnly; missing Start, IsTrackingUpdate, Reload, Close | | MemAccResolver struct | golang/nats-server/server/accounts.go:4073 | PORTED | src/NATS.Server/Auth/Jwt/AccountResolver.cs:44 | MemAccountResolver | | MemAccResolver.Fetch() | golang/nats-server/server/accounts.go:4079 | PORTED | src/NATS.Server/Auth/Jwt/AccountResolver.cs:53 | FetchAsync | | MemAccResolver.Store() | golang/nats-server/server/accounts.go:4087 | PORTED | src/NATS.Server/Auth/Jwt/AccountResolver.cs:60 | StoreAsync | | URLAccResolver struct | golang/nats-server/server/accounts.go:4097 | MISSING | — | HTTP-based account resolver | | NewURLAccResolver() | golang/nats-server/server/accounts.go:4104 | MISSING | — | URL resolver constructor | | URLAccResolver.Fetch() | golang/nats-server/server/accounts.go:4123 | MISSING | — | HTTP GET for account JWT | | DirAccResolver struct | golang/nats-server/server/accounts.go:4143 | MISSING | — | Directory-based resolver with NATS sync | | DirAccResolver.Start() | golang/nats-server/server/accounts.go:4352 | MISSING | — | Start directory resolver with NATS subscriptions | | DirAccResolver.Fetch() | golang/nats-server/server/accounts.go:4533 | MISSING | — | Fetch from directory store | | DirAccResolver.Store() | golang/nats-server/server/accounts.go:4548 | MISSING | — | Store to directory | | NewDirAccResolver() | golang/nats-server/server/accounts.go:4574 | MISSING | — | Dir resolver constructor | | CacheDirAccResolver struct | golang/nats-server/server/accounts.go:4594 | MISSING | — | Caching directory resolver | | Server.fetch() | golang/nats-server/server/accounts.go:4599 | MISSING | — | Server-level account fetch via NATS | | NewCacheDirAccResolver() | golang/nats-server/server/accounts.go:4664 | MISSING | — | Cache dir resolver constructor | | CacheDirAccResolver.Start() | golang/nats-server/server/accounts.go:4679 | MISSING | — | Start cache resolver with NATS sync | --- ## 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/Auth/ src/NATS.Server/Imports/ -name '*.cs' -type f -exec cat {} + | wc -l # Re-count .NET test LOC for this module find tests/NATS.Server.Tests/Auth/ tests/NATS.Server.Tests/Accounts/ -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 auth-and-accounts batch 4: added auth-option normalization in `AuthService.Build` for orphan account assignment (`$G`) and response-permission defaults (`MaxMsgs`/`Expires` + publish allow-list initialization), with targeted tests (`AuthServiceParityBatch4Tests`). Reclassified `assignGlobalAccountToOrphanUsers` and `validateResponsePermissions` to PORTED and updated `buildNkeysAndUsersFromOptions` residual notes. | codex | | 2026-02-25 | File created with LLM analysis instructions | auto | | 2026-02-25 | Gap inventory populated: 5 Go files analyzed (7,260 LOC), 239 symbols classified across auth.go, auth_callout.go, nkey.go, jwt.go, accounts.go. Summary: 64 PORTED, 38 PARTIAL, 128 MISSING, 9 NOT_APPLICABLE, 0 DEFERRED | auto | | 2026-02-25 | Executed auth-and-accounts batch 1: added parity fields to `NKeyUser` and `User`, added auth callout constants to `ExternalAuthCalloutAuthenticator`, added targeted tests (`AuthModelAndCalloutConstantsParityTests`), and reclassified 5 rows (3 MISSING + 2 PARTIAL) to PORTED | codex | | 2026-02-25 | Executed auth-and-accounts batch 2: added account parity APIs/constants for interest and response/service-import accounting (`ClientInfoHdr`, `SubscriptionInterest`, `Interest`, `NumPendingAllResponses`, `NumPendingResponses`, `NumServiceImports`), introduced `ResponseServiceImportRemovalReason`, added `RemoveRespServiceImport(..., reason)`, and added targeted tests (`AccountResponseAndInterestParityBatch1Tests`). Reclassified 7 rows to PORTED and updated `removeRespServiceImport` notes. | codex | | 2026-02-25 | Executed auth-and-accounts batch 3: added TLS auth parity helpers (`GetTlsAuthDcs`, `DnsAltNameLabels`, `DnsAltNameMatches`), extended TLS-map auth matching for SAN email/DNS/URI + DC-augmented RDN, validated pinned-cert helper parity (`TlsHelper.MatchesPinnedCert`), and added targeted tests (`TlsMapAuthParityBatch1Tests`). Reclassified 4 rows to PORTED and updated `checkClientTLSCertSubject` notes. | codex |