# JetStream — Gap Analysis > This file tracks what has and hasn't been ported from Go to .NET for the **JetStream** 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 JetStream - **This is the largest module** (55,228 Go source LOC, 30% ported). Break analysis into sub-areas: 1. Core orchestration (`jetstream.go`, `jetstream_api.go`) 2. Stream lifecycle (`stream.go`) — retention policies: Limits, Interest, WorkQueue 3. Consumer state machine (`consumer.go`) — push vs pull, ack policies (None, All, Explicit) 4. Storage engine (`filestore.go`, `memstore.go`) — S2 compression (IronSnappy NuGet), AEAD encryption (ChaCha20Poly1305/AesGcm) 5. Cluster coordination (`jetstream_cluster.go`) — depends on RAFT module - Go's `filestore.go` alone is 12,600 lines — the single largest implementation file. - The storage interface (`store.go`) defines the contract; analyze this first to understand the abstraction. --- ## Go Reference Files (Source) - `golang/nats-server/server/jetstream.go` — Orchestration, API subject handlers (`$JS.API.*`) - `golang/nats-server/server/jetstream_api.go` — JetStream API handlers (~5,300 lines) - `golang/nats-server/server/jetstream_events.go` — JetStream event publishing - `golang/nats-server/server/jetstream_errors.go` — Error definitions - `golang/nats-server/server/jetstream_errors_generated.go` — Generated error codes - `golang/nats-server/server/jetstream_batching.go` — Batch processing - `golang/nats-server/server/jetstream_versioning.go` — Version compatibility - `golang/nats-server/server/stream.go` — Stream lifecycle, retention policies (~8,000 lines) - `golang/nats-server/server/consumer.go` — Consumer state machine (~6,000 lines). Push vs pull, ack policies. - `golang/nats-server/server/store.go` — Storage abstraction interface - `golang/nats-server/server/filestore.go` — Block-based persistent storage (~12,600 lines). S2 compression, encryption. - `golang/nats-server/server/memstore.go` — In-memory storage with TTL - `golang/nats-server/server/dirstore.go` — Directory-based storage - `golang/nats-server/server/disk_avail.go` — Disk space checking - `golang/nats-server/server/jetstream_cluster.go` — Clustered JetStream (~10,900 lines) ## Go Reference Files (Tests) - `golang/nats-server/server/jetstream_test.go` - `golang/nats-server/server/jetstream_consumer_test.go` - `golang/nats-server/server/jetstream_errors_test.go` - `golang/nats-server/server/jetstream_batching_test.go` - `golang/nats-server/server/jetstream_versioning_test.go` - `golang/nats-server/server/jetstream_helpers_test.go` - `golang/nats-server/server/jetstream_jwt_test.go` - `golang/nats-server/server/jetstream_tpm_test.go` - `golang/nats-server/server/jetstream_sourcing_scaling_test.go` - `golang/nats-server/server/jetstream_benchmark_test.go` - `golang/nats-server/server/filestore_test.go` - `golang/nats-server/server/memstore_test.go` - `golang/nats-server/server/dirstore_test.go` - `golang/nats-server/server/store_test.go` - `golang/nats-server/server/jetstream_cluster_1_test.go` through `_4_test.go` - `golang/nats-server/server/jetstream_super_cluster_test.go` - `golang/nats-server/server/jetstream_leafnode_test.go` - `golang/nats-server/server/jetstream_cluster_long_test.go` ## .NET Implementation Files (Source) - `src/NATS.Server/JetStream/` — Root files (orchestration) - `src/NATS.Server/JetStream/Api/` — API handlers - `src/NATS.Server/JetStream/Consumers/` — Consumer state machine - `src/NATS.Server/JetStream/Models/` — StreamConfig, ConsumerConfig, etc. - `src/NATS.Server/JetStream/MirrorSource/` — Stream mirroring & sourcing - `src/NATS.Server/JetStream/Publish/` — Publish handling - `src/NATS.Server/JetStream/Snapshots/` — State snapshots - `src/NATS.Server/JetStream/Validation/` — Config validation - `src/NATS.Server/JetStream/Storage/` — FileStore, MemStore, encryption - `src/NATS.Server/JetStream/Cluster/` — Clustered JetStream ## .NET Implementation Files (Tests) - `tests/NATS.Server.Tests/JetStream/` — Root tests - `tests/NATS.Server.Tests/JetStream/Api/` - `tests/NATS.Server.Tests/JetStream/Cluster/` - `tests/NATS.Server.Tests/JetStream/Consumers/` - `tests/NATS.Server.Tests/JetStream/MirrorSource/` - `tests/NATS.Server.Tests/JetStream/Snapshots/` - `tests/NATS.Server.Tests/JetStream/Storage/` - `tests/NATS.Server.Tests/JetStream/Streams/` --- ## Gap Inventory ### jetstream.go — Core Orchestration (~2866 lines) | Go Symbol | Go File:Line | Status | .NET Equivalent | Notes | |-----------|:-------------|--------|:----------------|-------| | JetStreamConfig (struct) | golang/nats-server/server/jetstream.go:42 | PORTED | src/NATS.Server/Configuration/JetStreamOptions.cs | Added missing config fields: `SyncInterval`, `SyncAlways`, `CompressOk`, `UniqueTag`, `Strict` (plus parser wiring) | | JetStreamStats (struct) | golang/nats-server/server/jetstream.go:55 | PORTED | src/NATS.Server/JetStream/JetStreamParityModels.cs (`JetStreamStats`) | Added server-usage model with `Memory`, `Store`, `ReservedMemory`, `ReservedStore`, `Accounts`, `HaAssets`, and `Api` | | JetStreamAccountLimits (struct) | golang/nats-server/server/jetstream.go:65 | PORTED | src/NATS.Server/JetStream/JetStreamParityModels.cs (`JetStreamAccountLimits`), src/NATS.Server/Configuration/JetStreamOptions.cs | Added missing limits fields: `MaxAckPending`, `MemoryMaxStreamBytes`, `StoreMaxStreamBytes`, `MaxBytesRequired`, and tier map support | | JetStreamTier (struct) | golang/nats-server/server/jetstream.go:76 | PORTED | src/NATS.Server/JetStream/JetStreamParityModels.cs (`JetStreamTier`) | Added per-tier model (`Name`, `Memory`, `Store`, `Streams`, `Consumers`) | | JetStreamAccountStats (struct) | golang/nats-server/server/jetstream.go:87 | PARTIAL | src/NATS.Server/JetStream/Api/JetStreamApiResponse.cs:105 | JetStreamAccountInfo has Streams/Consumers counts only. Missing: memory/store usage, tiers, domain, API stats | | JetStreamAPIStats (struct) | golang/nats-server/server/jetstream.go:95 | PORTED | src/NATS.Server/JetStream/JetStreamParityModels.cs (`JetStreamApiStats`) | Added API stats model with `Level`, `Total`, `Errors`, `Inflight` | | jetStream (internal struct) | golang/nats-server/server/jetstream.go:103 | PARTIAL | src/NATS.Server/JetStream/JetStreamService.cs:11 | JetStreamService covers lifecycle. Missing: apiInflight/apiTotal/apiErrors atomics, memUsed/storeUsed tracking, accounts map, apiSubs, cluster, oos/shuttingDown state | | jsAccount (internal struct) | golang/nats-server/server/jetstream.go:151 | MISSING | — | Per-account JetStream state (streams map, usage tracking, cluster usage updates) not modeled | | jsaUsage (internal struct) | golang/nats-server/server/jetstream.go:181 | MISSING | — | Per-account mem/store usage tracking | | EnableJetStream (Server method) | golang/nats-server/server/jetstream.go:188 | PARTIAL | src/NATS.Server/JetStream/JetStreamService.cs:95 | StartAsync handles StoreDir creation and API subject registration. Missing: dynamic config, system memory detection, encryption init, cluster init | | jsKeyGen (Server method) | golang/nats-server/server/jetstream.go:240 | MISSING | — | HMAC-SHA256 key generation for JetStream encryption | | decryptMeta (Server method) | golang/nats-server/server/jetstream.go:257 | MISSING | — | Encrypted metafile decryption (AEAD with cipher fallback) | | checkStoreDir (Server method) | golang/nats-server/server/jetstream.go:320 | MISSING | — | Legacy store directory migration logic | | initJetStreamEncryption (Server method) | golang/nats-server/server/jetstream.go:382 | MISSING | — | TPM-backed encryption key initialization | | enableJetStream (Server method) | golang/nats-server/server/jetstream.go:414 | PARTIAL | src/NATS.Server/JetStream/JetStreamService.cs:95 | Core startup covered. Missing: gcbOutMax, system account setup, JS banner, encryption notice, internal subscriptions setup, cluster mode init | | canExtendOtherDomain (Server method) | golang/nats-server/server/jetstream.go:533 | MISSING | — | Leaf node domain extension check | | restartJetStream (Server method) | golang/nats-server/server/jetstream.go:556 | MISSING | — | Re-enable JetStream during config reload | | setupJetStreamExports (Server method) | golang/nats-server/server/jetstream.go:590 | MISSING | — | System account service export setup | | handleOutOfSpace (Server method) | golang/nats-server/server/jetstream.go:613 | MISSING | — | OOS handling: disable JS + advisory | | DisableJetStream (Server method) | golang/nats-server/server/jetstream.go:643 | PARTIAL | src/NATS.Server/JetStream/JetStreamService.cs:141 | DisposeAsync clears subjects. Missing: cluster meta leader transfer, RAFT node cleanup | | enableJetStreamAccounts (Server method) | golang/nats-server/server/jetstream.go:690 | MISSING | — | Multi-account JS enablement with parallel task workers | | enableAllJetStreamServiceImportsAndMappings (Account method) | golang/nats-server/server/jetstream.go:714 | MISSING | — | Per-account service imports and domain mappings | | configJetStream (Server method) | golang/nats-server/server/jetstream.go:771 | MISSING | — | Per-account JS config (enable/update/disable) | | configAllJetStreamAccounts (Server method) | golang/nats-server/server/jetstream.go:809 | MISSING | — | Walk all accounts and restore JetStream state | | JetStreamEnabled (Server method) | golang/nats-server/server/jetstream.go:904 | PORTED | src/NATS.Server/NatsServer.cs:159 (`JetStreamEnabled`) | Server-level JetStream enabled check now exposed and backed by service running state | | JetStreamEnabledForDomain (Server method) | golang/nats-server/server/jetstream.go:909 | MISSING | — | Domain-wide JS availability check | | signalPullConsumers (Server method) | golang/nats-server/server/jetstream.go:930 | MISSING | — | Shutdown signal to R1 pull consumers | | shutdownJetStream (Server method) | golang/nats-server/server/jetstream.go:977 | PARTIAL | src/NATS.Server/JetStream/JetStreamService.cs:141 | Basic cleanup in DisposeAsync. Missing: account removal, cluster qch signaling | | JetStreamConfig (Server method) | golang/nats-server/server/jetstream.go:1055 | PORTED | src/NATS.Server/NatsServer.cs:161 (`JetStreamConfig`) | Returns a copy of configured JetStream options (store dir, limits, domain) | | StoreDir (Server method) | golang/nats-server/server/jetstream.go:1065 | PORTED | src/NATS.Server/NatsServer.cs:177 (`StoreDir`) | Server now exposes current configured JetStream store directory | | JetStreamNumAccounts (Server method) | golang/nats-server/server/jetstream.go:1074 | MISSING | — | Enabled account count | | JetStreamReservedResources (Server method) | golang/nats-server/server/jetstream.go:1085 | MISSING | — | Reserved mem/store bytes | | Account.EnableJetStream | golang/nats-server/server/jetstream.go:1107 | MISSING | — | Per-account JS enablement with limits, store dir, cluster usage | | Account.UpdateJetStreamLimits | golang/nats-server/server/jetstream.go:1736 | MISSING | — | Update account limits with delta checking | | Account.JetStreamUsage | golang/nats-server/server/jetstream.go:1834 | MISSING | — | Full account usage stats with tier breakdown | | Account.DisableJetStream | golang/nats-server/server/jetstream.go:1961 | MISSING | — | Per-account JS disable | | Account.lookupStream | golang/nats-server/server/jetstream.go:1717 | MISSING | — | Stream lookup by name (via jsa) | | Account.streams / filteredStreams | golang/nats-server/server/jetstream.go:1681 | MISSING | — | All/filtered stream enumeration | | jsAccount.updateUsage | golang/nats-server/server/jetstream.go:2187 | MISSING | — | Storage usage delta tracking | | jsAccount.checkAndSyncUsage | golang/nats-server/server/jetstream.go:2108 | MISSING | — | Usage drift detection and correction | | jsAccount.sendClusterUsageUpdate | golang/nats-server/server/jetstream.go:2250 | MISSING | — | Binary-encoded cluster usage publication | | jsAccount.remoteUpdateUsage | golang/nats-server/server/jetstream.go:2021 | MISSING | — | Process incoming remote usage updates | | jetStream.wouldExceedLimits | golang/nats-server/server/jetstream.go:2299 | MISSING | — | Server-level resource limit check | | jetStream.checkLimits | golang/nats-server/server/jetstream.go:2434 | MISSING | — | Account+server limit checking for stream config | | jetStream.checkBytesLimits | golang/nats-server/server/jetstream.go:2446 | MISSING | — | Byte-level limits check for mem/file storage | | jetStream.sufficientResources | golang/nats-server/server/jetstream.go:2549 | MISSING | — | System resource sufficiency check for new accounts | | jetStream.reserveStreamResources | golang/nats-server/server/jetstream.go:2606 | MISSING | — | Reserve MaxBytes for a stream | | jetStream.releaseStreamResources | golang/nats-server/server/jetstream.go:2627 | MISSING | — | Release reserved MaxBytes | | jetStream.usageStats | golang/nats-server/server/jetstream.go:2520 | MISSING | — | Server-wide JS usage stats | | jsAccount.selectLimits | golang/nats-server/server/jetstream.go:2337 | MISSING | — | Tier-based limit selection | | jsAccount.storageTotals | golang/nats-server/server/jetstream.go:2360 | MISSING | — | Aggregate mem/store totals | | jsAccount.reservedStorage | golang/nats-server/server/jetstream.go:1801 | MISSING | — | Reserved bytes by tier | | jsAccount.delete | golang/nats-server/server/jetstream.go:2481 | MISSING | — | Delete all JS resources for account | | dynJetStreamConfig (Server method) | golang/nats-server/server/jetstream.go:2659 | MISSING | — | Dynamic config: 75% sysmem, disk available | | isValidName | golang/nats-server/server/jetstream.go:2735 | PORTED | src/NATS.Server/JetStream/Validation/JetStreamConfigValidator.cs:10 | `IsValidName` enforces non-empty names, UTF-8 max 255 bytes, and rejects whitespace / `*` / `>`; applied in stream + consumer create paths | | friendlyBytes | golang/nats-server/server/jetstream.go:2723 | NOT_APPLICABLE | — | Logging helper; .NET has built-in formatting | | tierName | golang/nats-server/server/jetstream.go:2316 | MISSING | — | Compute tier name from replica count | | validateJetStreamOptions | golang/nats-server/server/jetstream.go:2767 | MISSING | — | Validates JS options (domain, cluster, etc.) | | fixCfgMirrorWithDedupWindow | golang/nats-server/server/jetstream.go:2848 | NOT_APPLICABLE | — | Bug fix for legacy config; not needed in new port | | JetStreamStoreDir (const) | golang/nats-server/server/jetstream.go:2649 | PORTED | src/NATS.Server/Configuration/JetStreamOptions.cs:8 | Added Go-parity constant `"jetstream"` | | JetStreamMaxStoreDefault (const) | golang/nats-server/server/jetstream.go:2651 | PORTED | src/NATS.Server/Configuration/JetStreamOptions.cs:9 | Added Go-parity default max store constant (`1 TiB`) | | JetStreamMaxMemDefault (const) | golang/nats-server/server/jetstream.go:2653 | PORTED | src/NATS.Server/Configuration/JetStreamOptions.cs:10 | Added Go-parity default max memory constant (`256 MiB`) | | Stream recovery logic (doStream/doConsumers) | golang/nats-server/server/jetstream.go:1223-1636 | MISSING | — | Full stream/consumer recovery from disk: metafile reading, checksum, encryption, versioning, subject repair | | keyGen (type) | golang/nats-server/server/jetstream.go:237 | MISSING | — | Key generation function signature for encryption | | resourcesExceededError (Server method) | golang/nats-server/server/jetstream.go:2743 | MISSING | — | Throttled error logging + meta leader stepdown | | handleWritePermissionError (Server method) | golang/nats-server/server/jetstream.go:2857 | MISSING | — | FS permission error handling | ### jetstream_api.go — API Handlers (~5165 lines) | Go Symbol | Go File:Line | Status | .NET Equivalent | Notes | |-----------|:-------------|--------|:----------------|-------| | JSApi* subject constants (50+) | golang/nats-server/server/jetstream_api.go:36-312 | PORTED | src/NATS.Server/JetStream/Api/JetStreamApiSubjects.cs:1 | All major API subjects defined. Minor: some template variants (T-suffixed) not needed in .NET | | JSAdvisory* prefix constants (25+) | golang/nats-server/server/jetstream_api.go:229-311 | PARTIAL | src/NATS.Server/JetStream/Api/AdvisoryPublisher.cs:1 | Stream create/delete/update, consumer create/delete covered. Missing: snapshot, restore, leader elected, quorum lost, batch abandoned, out-of-storage, server removed, API limit, pause, pinned, unpinned advisory prefixes | | JSMaxDescriptionLen (const) | golang/nats-server/server/jetstream_api.go:352 | PORTED | src/NATS.Server/JetStream/Api/JetStreamApiLimits.cs:10 | Added Go-parity constant and enforced in stream create/update validation (`StreamManager`) | | JSMaxMetadataLen (const) | golang/nats-server/server/jetstream_api.go:356 | PORTED | src/NATS.Server/JetStream/Api/JetStreamApiLimits.cs:11 | Added Go-parity constant; metadata byte-size helper validates stream/consumer metadata against this limit | | JSMaxNameLen (const) | golang/nats-server/server/jetstream_api.go:360 | PORTED | src/NATS.Server/JetStream/Api/JetStreamApiLimits.cs:12 | Added Go-parity constant; name validation uses UTF-8 byte length limit through `JetStreamConfigValidator.IsValidName` | | JSDefaultRequestQueueLimit (const) | golang/nats-server/server/jetstream_api.go:364 | PORTED | src/NATS.Server/JetStream/Api/JetStreamApiLimits.cs:13 | Added Go-parity default request queue limit constant for JetStream API request orchestration | | ApiResponse (struct) | golang/nats-server/server/jetstream_api.go:369 | PARTIAL | src/NATS.Server/JetStream/Api/JetStreamApiResponse.cs:5 | Type field missing; error structure simplified | | ApiPaged / ApiPagedRequest (structs) | golang/nats-server/server/jetstream_api.go:395-404 | MISSING | — | Paged API request/response not implemented | | JSApiAccountInfoResponse | golang/nats-server/server/jetstream_api.go:407 | PARTIAL | src/NATS.Server/JetStream/Api/JetStreamApiResponse.cs:105 | Basic streams/consumers count. Missing: full JetStreamAccountStats embedding | | JSApiStreamCreateResponse | golang/nats-server/server/jetstream_api.go:415 | PARTIAL | src/NATS.Server/JetStream/Api/JetStreamApiResponse.cs:94 | StreamInfo returned. Missing: DidCreate field | | JSApiStreamDeleteResponse | golang/nats-server/server/jetstream_api.go:423 | PORTED | src/NATS.Server/JetStream/Api/JetStreamApiResponse.cs:17 | Success field present | | JSApiStreamInfoRequest / Response | golang/nats-server/server/jetstream_api.go:434-446 | PARTIAL | src/NATS.Server/JetStream/Api/JetStreamApiResponse.cs:94 | Basic info works. Missing: DeletedDetails, SubjectsFilter, paged response | | JSApiStreamNamesRequest / Response | golang/nats-server/server/jetstream_api.go:453-467 | PARTIAL | src/NATS.Server/JetStream/Api/JetStreamApiResponse.cs:11 | StreamNames returned. Missing: paging, subject filter | | JSApiStreamListRequest / Response | golang/nats-server/server/jetstream_api.go:469-485 | MISSING | — | Detailed stream list with paging, missing/offline | | JSApiStreamPurgeRequest / Response | golang/nats-server/server/jetstream_api.go:492-507 | PARTIAL | src/NATS.Server/JetStream/Api/JetStreamApiResponse.cs:76 | Purge response with count. Missing: filter/sequence/keep options in request | | JSApiStreamUpdateResponse | golang/nats-server/server/jetstream_api.go:519 | PARTIAL | src/NATS.Server/JetStream/Api/JetStreamApiResponse.cs:94 | Returns StreamInfo. Response type constant missing | | JSApiMsgDeleteRequest / Response | golang/nats-server/server/jetstream_api.go:528-538 | PORTED | src/NATS.Server/JetStream/Api/Handlers/StreamApiHandlers.cs | HandleMessageDelete implemented | | JSApiStreamSnapshotRequest / Response | golang/nats-server/server/jetstream_api.go:540-569 | PARTIAL | src/NATS.Server/JetStream/Api/JetStreamApiResponse.cs:125 | Basic snapshot modeled. Missing: DeliverSubject, NoConsumers, ChunkSize, WindowSize, CheckMsgs options | | JSApiStreamRestoreRequest / Response | golang/nats-server/server/jetstream_api.go:572-586 | PARTIAL | src/NATS.Server/JetStream/Api/Handlers/StreamApiHandlers.cs | HandleRestore exists. Missing: chunked delivery protocol | | JSApiStreamRemovePeerRequest / Response | golang/nats-server/server/jetstream_api.go:589-600 | PARTIAL | src/NATS.Server/JetStream/Api/Handlers/ClusterControlApiHandlers.cs | HandleStreamPeerRemove exists as stub | | JSApiStreamLeaderStepDownResponse | golang/nats-server/server/jetstream_api.go:603-608 | PARTIAL | src/NATS.Server/JetStream/Api/Handlers/ClusterControlApiHandlers.cs | HandleStreamLeaderStepdown exists | | JSApiConsumerLeaderStepDownResponse | golang/nats-server/server/jetstream_api.go:611-616 | PARTIAL | src/NATS.Server/JetStream/Api/Handlers/ClusterControlApiHandlers.cs | HandleConsumerLeaderStepdown exists | | JSApiLeaderStepdownRequest / Response | golang/nats-server/server/jetstream_api.go:619-629 | PARTIAL | src/NATS.Server/JetStream/Api/Handlers/ClusterControlApiHandlers.cs | HandleMetaLeaderStepdown exists | | JSApiMetaServerRemoveRequest / Response | golang/nats-server/server/jetstream_api.go:632-646 | PARTIAL | src/NATS.Server/JetStream/Api/Handlers/AccountControlApiHandlers.cs | HandleServerRemove exists as stub | | JSApiMetaServerStreamMoveRequest | golang/nats-server/server/jetstream_api.go:650-659 | PARTIAL | src/NATS.Server/JetStream/Api/Handlers/AccountControlApiHandlers.cs | HandleAccountStreamMove exists as stub | | JSApiAccountPurgeResponse | golang/nats-server/server/jetstream_api.go:663-667 | PARTIAL | src/NATS.Server/JetStream/Api/Handlers/AccountControlApiHandlers.cs | HandleAccountPurge exists as stub | | JSApiMsgGetRequest / Response | golang/nats-server/server/jetstream_api.go:670-699 | PARTIAL | src/NATS.Server/JetStream/Api/Handlers/StreamApiHandlers.cs | HandleMessageGet exists. Missing: Batch, MaxBytes, StartTime, MultiLastFor, UpToSeq/Time, NoHeaders | | JSApiConsumerCreateResponse | golang/nats-server/server/jetstream_api.go:704-709 | PARTIAL | src/NATS.Server/JetStream/Api/JetStreamApiResponse.cs:100 | ConsumerInfo returned. Response type constant missing | | JSApiConsumerDeleteResponse | golang/nats-server/server/jetstream_api.go:711-716 | PORTED | src/NATS.Server/JetStream/Api/Handlers/ConsumerApiHandlers.cs | HandleDelete implemented | | JSApiConsumerPauseRequest / Response | golang/nats-server/server/jetstream_api.go:718-729 | PORTED | src/NATS.Server/JetStream/Api/JetStreamApiResponse.cs:86 | PauseResponse with Paused/PauseUntil | | JSApiConsumerInfoResponse | golang/nats-server/server/jetstream_api.go:731-736 | PARTIAL | src/NATS.Server/JetStream/Api/JetStreamApiResponse.cs:100 | ConsumerInfo returned. Missing: type field | | JSApiConsumerNamesResponse | golang/nats-server/server/jetstream_api.go:742-748 | PARTIAL | src/NATS.Server/JetStream/Api/JetStreamApiResponse.cs:12 | ConsumerNames returned. Missing: paging | | JSApiConsumerListResponse | golang/nats-server/server/jetstream_api.go:750-758 | MISSING | — | Detailed consumer list with paging, missing/offline | | JSApiConsumerGetNextRequest | golang/nats-server/server/jetstream_api.go:761-768 | PARTIAL | src/NATS.Server/JetStream/Api/Handlers/ConsumerApiHandlers.cs | HandleNext exists. Missing: Expires, MaxBytes, Heartbeat, PriorityGroup | | JSApiConsumerResetRequest / Response | golang/nats-server/server/jetstream_api.go:771-781 | PARTIAL | src/NATS.Server/JetStream/Api/Handlers/ConsumerApiHandlers.cs | HandleReset exists as stub | | JSApiConsumerUnpinRequest / Response | golang/nats-server/server/jetstream_api.go:509-517 | PARTIAL | src/NATS.Server/JetStream/Api/Handlers/ConsumerApiHandlers.cs | HandleUnpin exists as stub | | generateJSMappingTable | golang/nats-server/server/jetstream_api.go:323-349 | MISSING | — | Domain prefix mapping table generation | | apiDispatch (jetStream method) | golang/nats-server/server/jetstream_api.go:795 | PARTIAL | src/NATS.Server/JetStream/Api/JetStreamApiRouter.cs:222 | Router.Route matches subjects to handlers. Missing: ClientInfoHdr check, inflight tracking, queue-based dispatch pool | | processJSAPIRoutedRequests | golang/nats-server/server/jetstream_api.go:882 | MISSING | — | Worker pool goroutine for processing routed API requests | | setJetStreamExportSubs | golang/nats-server/server/jetstream_api.go:913 | PARTIAL | src/NATS.Server/JetStream/JetStreamService.cs:16 | AllApiSubjects list covers subject registration. Missing: worker pool, Sublist-based routing | | sendAPIResponse / sendAPIErrResponse | golang/nats-server/server/jetstream_api.go:986-999 | MISSING | — | API response sending with audit advisory | | sendJetStreamAPIAuditAdvisory | golang/nats-server/server/jetstream_api.go:999+ | MISSING | — | JS API audit advisory publication | | jsAccountInfoRequest | golang/nats-server/server/jetstream_api.go:1238 | PARTIAL | src/NATS.Server/JetStream/Api/Handlers/AccountApiHandlers.cs | HandleInfo returns basic counts. Missing: full JetStreamAccountStats | | jsStreamCreateRequest | golang/nats-server/server/jetstream_api.go:1335 | PARTIAL | src/NATS.Server/JetStream/Api/Handlers/StreamApiHandlers.cs | HandleCreate parses config and creates. Missing: clustered forwarding, permission checks, JSRequiredApiLevel header check | | jsStreamUpdateRequest | golang/nats-server/server/jetstream_api.go:1452 | PARTIAL | src/NATS.Server/JetStream/Api/Handlers/StreamApiHandlers.cs | HandleUpdate exists. Missing: config diff validation, clustered proposal | | jsStreamNamesRequest | golang/nats-server/server/jetstream_api.go:1557 | PARTIAL | src/NATS.Server/JetStream/Api/Handlers/StreamApiHandlers.cs | HandleNames returns list. Missing: paging, subject filter | | jsStreamListRequest | golang/nats-server/server/jetstream_api.go:1690 | MISSING | — | Detailed stream list with full StreamInfo per entry | | jsStreamInfoRequest | golang/nats-server/server/jetstream_api.go:1809 | PARTIAL | src/NATS.Server/JetStream/Api/Handlers/StreamApiHandlers.cs | HandleInfo returns config+state. Missing: subject detail paging, deleted details | | jsStreamDeleteRequest | golang/nats-server/server/jetstream_api.go:3073 | PARTIAL | src/NATS.Server/JetStream/Api/Handlers/StreamApiHandlers.cs | HandleDelete works. Missing: clustered forwarding | | jsStreamPurgeRequest | golang/nats-server/server/jetstream_api.go:3572 | PARTIAL | src/NATS.Server/JetStream/Api/Handlers/StreamApiHandlers.cs | HandlePurge works. Missing: filter/sequence/keep options | | jsMsgDeleteRequest | golang/nats-server/server/jetstream_api.go:3147 | PARTIAL | src/NATS.Server/JetStream/Api/Handlers/StreamApiHandlers.cs | HandleMessageDelete works. Missing: NoErase option, clustered forwarding | | jsMsgGetRequest | golang/nats-server/server/jetstream_api.go:3272 | PARTIAL | src/NATS.Server/JetStream/Api/Handlers/StreamApiHandlers.cs | HandleMessageGet works for basic seq/subject. Missing: batch, multi-last, time-based queries | | jsStreamSnapshotRequest | golang/nats-server/server/jetstream_api.go:4007 | PARTIAL | src/NATS.Server/JetStream/Api/Handlers/StreamApiHandlers.cs | HandleSnapshot exists. Missing: chunk delivery, ack flow, window sizing | | jsStreamRestoreRequest | golang/nats-server/server/jetstream_api.go:3722 | PARTIAL | src/NATS.Server/JetStream/Api/Handlers/StreamApiHandlers.cs | HandleRestore exists. Missing: chunk receive protocol | | jsStreamLeaderStepDownRequest | golang/nats-server/server/jetstream_api.go:2034 | PARTIAL | src/NATS.Server/JetStream/Api/Handlers/ClusterControlApiHandlers.cs | Stub exists | | jsStreamRemovePeerRequest | golang/nats-server/server/jetstream_api.go:2275 | PARTIAL | src/NATS.Server/JetStream/Api/Handlers/ClusterControlApiHandlers.cs | Stub exists | | jsConsumerCreateRequest | golang/nats-server/server/jetstream_api.go:4244 | PARTIAL | src/NATS.Server/JetStream/Api/Handlers/ConsumerApiHandlers.cs | HandleCreate works for basic cases. Missing: JSApiConsumerCreateEx subject parsing, action (Create/Update), config validation depth | | jsConsumerNamesRequest | golang/nats-server/server/jetstream_api.go:4470 | PARTIAL | src/NATS.Server/JetStream/Api/Handlers/ConsumerApiHandlers.cs | HandleNames works. Missing: paging | | jsConsumerListRequest | golang/nats-server/server/jetstream_api.go:4597 | MISSING | — | Detailed consumer list with paging | | jsConsumerInfoRequest | golang/nats-server/server/jetstream_api.go:4707 | PARTIAL | src/NATS.Server/JetStream/Api/Handlers/ConsumerApiHandlers.cs | HandleInfo returns config. Missing: full ConsumerInfo (delivered, ack_floor, num_ack_pending, etc.) | | jsConsumerDeleteRequest | golang/nats-server/server/jetstream_api.go:4913 | PARTIAL | src/NATS.Server/JetStream/Api/Handlers/ConsumerApiHandlers.cs | HandleDelete works | | jsConsumerPauseRequest | golang/nats-server/server/jetstream_api.go:4991 | PARTIAL | src/NATS.Server/JetStream/Api/Handlers/ConsumerApiHandlers.cs | HandlePause works. Missing: PauseRemaining duration in response | | jsConsumerUnpinRequest | golang/nats-server/server/jetstream_api.go:3429 | PARTIAL | src/NATS.Server/JetStream/Api/Handlers/ConsumerApiHandlers.cs | HandleUnpin exists as stub | | jsLeaderServerRemoveRequest | golang/nats-server/server/jetstream_api.go:2383 | PARTIAL | src/NATS.Server/JetStream/Api/Handlers/AccountControlApiHandlers.cs | Stub exists | | jsLeaderServerStreamMoveRequest | golang/nats-server/server/jetstream_api.go:2514 | PARTIAL | src/NATS.Server/JetStream/Api/Handlers/AccountControlApiHandlers.cs | Stub exists | | jsLeaderServerStreamCancelMoveRequest | golang/nats-server/server/jetstream_api.go:2678 | PARTIAL | src/NATS.Server/JetStream/Api/Handlers/AccountControlApiHandlers.cs | Stub exists | | jsLeaderAccountPurgeRequest | golang/nats-server/server/jetstream_api.go:2793 | PARTIAL | src/NATS.Server/JetStream/Api/Handlers/AccountControlApiHandlers.cs | Stub exists | | jsLeaderStepDownRequest | golang/nats-server/server/jetstream_api.go:2893 | PARTIAL | src/NATS.Server/JetStream/Api/Handlers/ClusterControlApiHandlers.cs | Stub exists | | delayedAPIResponder | golang/nats-server/server/jetstream_api.go:1047 | MISSING | — | Delayed API response delivery with ordered linked list | | JSDirectMsgGet handler | golang/nats-server/server/jetstream_api.go:105-111 | PARTIAL | src/NATS.Server/JetStream/Api/Handlers/DirectApiHandlers.cs | HandleGet exists. Missing: batch support, multi-last, header stripping | | ILeaderForwarder interface | — | PORTED | src/NATS.Server/JetStream/Api/JetStreamApiRouter.cs:10 | .NET-native abstraction for leader forwarding | | JetStreamApiRouter | — | PORTED | src/NATS.Server/JetStream/Api/JetStreamApiRouter.cs:68 | Subject-to-handler routing with leader check | | ApiRateLimiter | — | PORTED | src/NATS.Server/JetStream/Api/ApiRateLimiter.cs:11 | Concurrency limiter + dedup cache | | ClusteredRequestProcessor | — | PORTED | src/NATS.Server/JetStream/Api/ClusteredRequestProcessor.cs:13 | Pending request correlation with TCS pattern | ### jetstream_events.go — Advisory Event Types (~366 lines) | Go Symbol | Go File:Line | Status | .NET Equivalent | Notes | |-----------|:-------------|--------|:----------------|-------| | publishAdvisory (Server method) | golang/nats-server/server/jetstream_events.go:23 | PARTIAL | src/NATS.Server/JetStream/Api/AdvisoryPublisher.cs:9 | AdvisoryPublisher publishes via delegate. Missing: interest check (SubList.HasInterest), gateway interest check, system account fallback | | JSAPIAudit (struct) | golang/nats-server/server/jetstream_events.go:50 | MISSING | — | API audit advisory type with Server, Client, Subject, Request, Response, Domain | | JSAPIAuditType (const) | golang/nats-server/server/jetstream_events.go:60 | MISSING | — | "io.nats.jetstream.advisory.v1.api_audit" | | ActionAdvisoryType (type + consts) | golang/nats-server/server/jetstream_events.go:63-69 | MISSING | — | CreateEvent/DeleteEvent/ModifyEvent enum | | JSStreamActionAdvisory (struct) | golang/nats-server/server/jetstream_events.go:72 | PARTIAL | src/NATS.Server/JetStream/Api/AdvisoryPublisher.cs:28 | StreamCreated/Deleted/Updated covered. Missing: Action field, Domain field, TypedEvent base | | JSConsumerActionAdvisory (struct) | golang/nats-server/server/jetstream_events.go:82 | PARTIAL | src/NATS.Server/JetStream/Api/AdvisoryPublisher.cs:75 | ConsumerCreated/Deleted covered. Missing: Action field, Domain field | | JSConsumerPauseAdvisory (struct) | golang/nats-server/server/jetstream_events.go:93 | MISSING | — | Consumer pause/unpause advisory | | JSConsumerAckMetric (struct) | golang/nats-server/server/jetstream_events.go:106 | MISSING | — | Ack latency metric | | JSConsumerDeliveryExceededAdvisory (struct) | golang/nats-server/server/jetstream_events.go:122 | MISSING | — | Max delivery exceeded advisory | | JSConsumerDeliveryNakAdvisory (struct) | golang/nats-server/server/jetstream_events.go:135 | MISSING | — | NAK advisory | | JSConsumerDeliveryTerminatedAdvisory (struct) | golang/nats-server/server/jetstream_events.go:149 | MISSING | — | Terminated message advisory | | JSSnapshotCreateAdvisory (struct) | golang/nats-server/server/jetstream_events.go:166 | MISSING | — | Snapshot start advisory | | JSSnapshotCompleteAdvisory (struct) | golang/nats-server/server/jetstream_events.go:177 | MISSING | — | Snapshot complete advisory | | JSRestoreCreateAdvisory (struct) | golang/nats-server/server/jetstream_events.go:191 | MISSING | — | Restore start advisory | | JSRestoreCompleteAdvisory (struct) | golang/nats-server/server/jetstream_events.go:201 | MISSING | — | Restore complete advisory | | JSDomainLeaderElectedAdvisory (struct) | golang/nats-server/server/jetstream_events.go:221 | MISSING | — | Domain leader election advisory | | JSStreamLeaderElectedAdvisory (struct) | golang/nats-server/server/jetstream_events.go:233 | MISSING | — | Stream leader election advisory | | JSStreamQuorumLostAdvisory (struct) | golang/nats-server/server/jetstream_events.go:247 | MISSING | — | Stream quorum lost advisory | | JSStreamBatchAbandonedAdvisory (struct) | golang/nats-server/server/jetstream_events.go:259 | MISSING | — | Batch abandoned advisory | | BatchAbandonReason (type + consts) | golang/nats-server/server/jetstream_events.go:268-274 | MISSING | — | timeout/large/incomplete reasons | | JSConsumerLeaderElectedAdvisory (struct) | golang/nats-server/server/jetstream_events.go:280 | MISSING | — | Consumer leader election advisory | | JSConsumerQuorumLostAdvisory (struct) | golang/nats-server/server/jetstream_events.go:295 | MISSING | — | Consumer quorum lost advisory | | JSConsumerGroupPinnedAdvisory (struct) | golang/nats-server/server/jetstream_events.go:307 | MISSING | — | Priority group pin advisory | | JSConsumerGroupUnpinnedAdvisory (struct) | golang/nats-server/server/jetstream_events.go:320 | MISSING | — | Priority group unpin advisory | | JSServerOutOfSpaceAdvisory (struct) | golang/nats-server/server/jetstream_events.go:335 | MISSING | — | Server out of space advisory | | JSServerRemovedAdvisory (struct) | golang/nats-server/server/jetstream_events.go:348 | MISSING | — | Server removed advisory | | JSAPILimitReachedAdvisory (struct) | golang/nats-server/server/jetstream_events.go:360 | MISSING | — | API queue limit reached advisory | ### jetstream_errors.go — Error Framework (~103 lines) | Go Symbol | Go File:Line | Status | .NET Equivalent | Notes | |-----------|:-------------|--------|:----------------|-------| | ErrorIdentifier (type) | golang/nats-server/server/jetstream_errors.go:29 | MISSING | — | uint16 error identifier type | | ErrorOption / Unless | golang/nats-server/server/jetstream_errors.go:12-19 | MISSING | — | Functional options for error creation | | IsNatsErr | golang/nats-server/server/jetstream_errors.go:32 | MISSING | — | Error identity checker by ErrorIdentifier | | ApiError (struct) | golang/nats-server/server/jetstream_errors.go:57 | PARTIAL | src/NATS.Server/JetStream/Api/JetStreamApiError.cs:3 | Code + Description present. Missing: ErrCode (uint16 error code), Error() method | | ErrorsData (struct) | golang/nats-server/server/jetstream_errors.go:64 | MISSING | — | Source data for generated errors | | ApiError.toReplacerArgs | golang/nats-server/server/jetstream_errors.go:79 | MISSING | — | Template replacement helper | ### jetstream_errors_generated.go — Error Codes (~3176 lines) | Go Symbol | Go File:Line | Status | .NET Equivalent | Notes | |-----------|:-------------|--------|:----------------|-------| | ErrorIdentifier constants (200+) | golang/nats-server/server/jetstream_errors_generated.go:7-400+ | PARTIAL | src/NATS.Server/JetStream/Publish/AtomicBatchPublishEngine.cs:14 | Only 9 atomic-batch error codes ported. Missing: ~190 other error identifiers (stream, consumer, cluster, etc.) | | ApiErrors map | golang/nats-server/server/jetstream_errors_generated.go:400+ | MISSING | — | Global map[ErrorIdentifier]*ApiError with HTTP codes and descriptions | | NewJS*Error factory functions (200+) | golang/nats-server/server/jetstream_errors_generated.go:varies | MISSING | — | Factory functions for each error type with template substitution | ### jetstream_versioning.go — Version Negotiation (~237 lines) | Go Symbol | Go File:Line | Status | .NET Equivalent | Notes | |-----------|:-------------|--------|:----------------|-------| | JSApiLevel (const) | golang/nats-server/server/jetstream_versioning.go:20 | PORTED | src/NATS.Server/JetStream/JsVersioning.cs:19 | Value 3 matches | | JSRequiredLevelMetadataKey (const) | golang/nats-server/server/jetstream_versioning.go:22 | PORTED | src/NATS.Server/JetStream/JsVersioning.cs:25 | "_nats.req.level" | | JSServerVersionMetadataKey (const) | golang/nats-server/server/jetstream_versioning.go:23 | PORTED | src/NATS.Server/JetStream/JsVersioning.cs:28 | "_nats.ver" | | JSServerLevelMetadataKey (const) | golang/nats-server/server/jetstream_versioning.go:24 | PORTED | src/NATS.Server/JetStream/JsVersioning.cs:31 | "_nats.level" | | getRequiredApiLevel | golang/nats-server/server/jetstream_versioning.go:28 | PORTED | src/NATS.Server/JetStream/JsVersioning.cs:37 | Exact behavior match | | supportsRequiredApiLevel | golang/nats-server/server/jetstream_versioning.go:36 | PORTED | src/NATS.Server/JetStream/JsVersioning.cs:48 | Exact behavior match | | setStaticStreamMetadata | golang/nats-server/server/jetstream_versioning.go:44 | PORTED | src/NATS.Server/JetStream/JsVersioning.cs:63 | All feature level checks match (TTL, counter, batch, schedule, persist) | | setDynamicStreamMetadata | golang/nats-server/server/jetstream_versioning.go:88 | PORTED | src/NATS.Server/JetStream/JsVersioning.cs:101 | Copy + add version/level | | copyStreamMetadata | golang/nats-server/server/jetstream_versioning.go:110 | PORTED | src/NATS.Server/JetStream/JsVersioning.cs:121 | Copy versioning fields | | setOrDeleteInStreamMetadata | golang/nats-server/server/jetstream_versioning.go:118 | PORTED | src/NATS.Server/JetStream/JsVersioning.cs:197 | Set or delete helper | | setStaticConsumerMetadata | golang/nats-server/server/jetstream_versioning.go:136 | PORTED | src/NATS.Server/JetStream/JsVersioning.cs:132 | PauseUntil + priority checks match | | setDynamicConsumerMetadata | golang/nats-server/server/jetstream_versioning.go:164 | PORTED | src/NATS.Server/JetStream/JsVersioning.cs:157 | Copy + add version/level | | setDynamicConsumerInfoMetadata | golang/nats-server/server/jetstream_versioning.go:181 | MISSING | — | Wraps setDynamicConsumerMetadata for ConsumerInfo | | copyConsumerMetadata | golang/nats-server/server/jetstream_versioning.go:198 | PORTED | src/NATS.Server/JetStream/JsVersioning.cs:176 | Copy versioning fields | | setOrDeleteInConsumerMetadata | golang/nats-server/server/jetstream_versioning.go:206 | PORTED | src/NATS.Server/JetStream/JsVersioning.cs:213 | Set or delete helper | | deleteDynamicMetadata | golang/nats-server/server/jetstream_versioning.go:222 | PORTED | src/NATS.Server/JetStream/JsVersioning.cs:187 | Remove version/level keys | | errorOnRequiredApiLevel | golang/nats-server/server/jetstream_versioning.go:229 | MISSING | — | Header-based API level rejection check | ### jetstream_batching.go — Batch Operations (~663 lines) | Go Symbol | Go File:Line | Status | .NET Equivalent | Notes | |-----------|:-------------|--------|:----------------|-------| | globalInflightBatches (var) | golang/nats-server/server/jetstream_batching.go:31 | MISSING | — | Global atomic counter across all streams | | batching (struct) | golang/nats-server/server/jetstream_batching.go:35 | PARTIAL | src/NATS.Server/JetStream/Publish/AtomicBatchPublishEngine.cs:91 | AtomicBatchPublishEngine uses ConcurrentDictionary. Missing: per-group store, timer-based cleanup | | batchGroup (struct) | golang/nats-server/server/jetstream_batching.go:40 | PARTIAL | src/NATS.Server/JetStream/Publish/AtomicBatchPublishEngine.cs:61 | InFlightBatch tracks messages. Missing: lseq, store (StreamStore), timer | | batchGroup.newBatchGroup | golang/nats-server/server/jetstream_batching.go:47 | PARTIAL | src/NATS.Server/JetStream/Publish/AtomicBatchPublishEngine.cs:150 | Batch creation in Process(). Missing: per-batch file store, server-configured timeout | | getBatchStoreDir | golang/nats-server/server/jetstream_batching.go:66 | MISSING | — | Compute batch store directory path | | newBatchStore | golang/nats-server/server/jetstream_batching.go:79 | MISSING | — | Create file or memory store for batch staging | | batchGroup.readyForCommit | golang/nats-server/server/jetstream_batching.go:104 | PARTIAL | src/NATS.Server/JetStream/Publish/AtomicBatchPublishEngine.cs:258 | Commit path exists. Missing: timer.Stop() check, FlushAllPending | | batchGroup.cleanup / cleanupLocked | golang/nats-server/server/jetstream_batching.go:113-125 | PARTIAL | src/NATS.Server/JetStream/Publish/AtomicBatchPublishEngine.cs:317 | EvictExpiredBatches. Missing: store.Delete, globalInflightBatches decrement | | batchGroup.stopLocked | golang/nats-server/server/jetstream_batching.go:128 | MISSING | — | Stop batch without cleanup | | batchStagedDiff (struct) | golang/nats-server/server/jetstream_batching.go:135 | MISSING | — | Staged diff for consistency checks: msgIds, counter, inflight, expectedPerSubject | | batchStagedDiff.commit | golang/nats-server/server/jetstream_batching.go:147 | MISSING | — | Commit staged diff to stream state | | batchApply (struct) | golang/nats-server/server/jetstream_batching.go:198 | MISSING | — | Cluster-level batch apply state | | batchApply.clearBatchStateLocked | golang/nats-server/server/jetstream_batching.go:209 | MISSING | — | Clear in-memory batch state | | batchApply.rejectBatchStateLocked | golang/nats-server/server/jetstream_batching.go:220 | MISSING | — | Reject batch and adjust CLFS | | checkMsgHeadersPreClusteredProposal | golang/nats-server/server/jetstream_batching.go:240 | MISSING | — | Pre-proposal header validation (~420 lines): expected-seq, per-subject-seq, msg ID dedup, counter CRDT, TTL, scheduling, rollup, discard-new checks | | AtomicBatchPublishEngine (.NET-only) | — | PORTED | src/NATS.Server/JetStream/Publish/AtomicBatchPublishEngine.cs:91 | .NET-native batch engine with stage/commit/error flow | | AtomicBatchPublishErrorCodes (.NET-only) | — | PORTED | src/NATS.Server/JetStream/Publish/AtomicBatchPublishEngine.cs:14 | 9 error codes matching Go generated codes | | BatchPublishRequest (.NET-only) | — | PORTED | src/NATS.Server/JetStream/Publish/AtomicBatchPublishEngine.cs:331 | Request model for batch messages | | AtomicBatchResult (.NET-only) | — | PORTED | src/NATS.Server/JetStream/Publish/AtomicBatchPublishEngine.cs:358 | Staged/Committed/Error result type | --- ## 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/JetStream/ -name '*.cs' -type f -exec cat {} + | wc -l # Re-count .NET test LOC for this module find tests/NATS.Server.Tests/JetStream/ -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')" ``` | `StorageType` (enum: FileStorage, MemoryStorage) | store.go:30-38 | PORTED | `Models/StreamConfig.cs` `StorageType` enum + `Models/JetStreamPolicies.cs` | Values mapped as `Memory`/`File` | | `ErrStoreClosed` | store.go:42 | MISSING | — | No sentinel error constants defined in .NET storage layer | | `ErrStoreMsgNotFound` | store.go:44 | MISSING | — | Same | | `ErrStoreEOF` | store.go:46 | MISSING | — | Same | | `ErrMaxMsgs` | store.go:48 | MISSING | — | Same | | `ErrMaxBytes` | store.go:50 | MISSING | — | Same | | `ErrMaxMsgsPerSubject` | store.go:52 | MISSING | — | Same | | `ErrStoreSnapshotInProgress` | store.go:55 | MISSING | — | Same | | `ErrMsgTooLarge` | store.go:57 | MISSING | — | Same | | `ErrStoreWrongType` | store.go:59 | MISSING | — | Same | | `ErrNoAckPolicy` | store.go:61 | MISSING | — | Same | | `ErrSequenceMismatch` | store.go:63 | MISSING | — | Same | | `ErrCorruptStreamState` | store.go:65 | MISSING | — | Same | | `ErrTooManyResults` | store.go:67 | MISSING | — | Same | | `StoreMsg` (struct) | store.go:71-78 | PORTED | `Storage/StoreMsg.cs` | Class with Subject, Header, Data, Sequence, Timestamp; Clear() matches Go's clear() | | `StoreMsg.copy()` | store.go:740-748 | MISSING | — | No buffer-reuse copy semantics; .NET class doesn't pool backing buffer | | `StoreMsg.clear()` | store.go:751-759 | PORTED | `StoreMsg.Clear()` | Simplified — no backing buffer pool | | `StorageUpdateHandler` (callback) | store.go:82-83 | MISSING | — | No callback registration model in .NET IStreamStore | | `StorageRemoveMsgHandler` (callback) | store.go:85 | MISSING | — | Same | | `ProcessJetStreamMsgHandler` (callback) | store.go:89 | MISSING | — | Same | | `StreamStore` (interface) | store.go:91-135 | PORTED | `Storage/IStreamStore.cs` | All 34 Go methods have signatures; default implementations throw NotSupportedException | | `StreamStore.StoreMsg` | store.go:92 | PORTED | `IStreamStore.StoreMsg` | Signature matches | | `StreamStore.StoreRawMsg` | store.go:93 | PORTED | `IStreamStore.StoreRawMsg` | Signature matches | | `StreamStore.SkipMsg` | store.go:94 | PORTED | `IStreamStore.SkipMsg` | Returns ulong only (Go returns (uint64, error)) | | `StreamStore.SkipMsgs` | store.go:95 | PORTED | `IStreamStore.SkipMsgs` | Signature matches | | `StreamStore.FlushAllPending` | store.go:96 | PORTED | `IStreamStore.FlushAllPending` | Returns Task (async adaptation) | | `StreamStore.LoadMsg` | store.go:97 | PORTED | `IStreamStore.LoadMsg` | Signature matches | | `StreamStore.LoadNextMsg` | store.go:98 | PORTED | `IStreamStore.LoadNextMsg` | Returns tuple `(StoreMsg, ulong Skip)` | | `StreamStore.LoadNextMsgMulti` | store.go:99 | MISSING | — | gsl.SimpleSublist not ported; no multi-filter load | | `StreamStore.LoadLastMsg` | store.go:100 | PORTED | `IStreamStore.LoadLastMsg` | Signature matches | | `StreamStore.LoadPrevMsg` | store.go:101 | PORTED | `IStreamStore.LoadPrevMsg` | Signature matches | | `StreamStore.LoadPrevMsgMulti` | store.go:102 | MISSING | — | gsl.SimpleSublist not ported | | `StreamStore.RemoveMsg` | store.go:103 | PORTED | `IStreamStore.RemoveMsg` | Returns bool only (Go returns (bool, error)) | | `StreamStore.EraseMsg` | store.go:104 | PORTED | `IStreamStore.EraseMsg` | Same note | | `StreamStore.Purge` | store.go:105 | PORTED | `IStreamStore.Purge` | Returns ulong only | | `StreamStore.PurgeEx` | store.go:106 | PORTED | `IStreamStore.PurgeEx` | Signature matches | | `StreamStore.Compact` | store.go:107 | PORTED | `IStreamStore.Compact` | Signature matches | | `StreamStore.Truncate` | store.go:108 | PORTED | `IStreamStore.Truncate` | Void (Go returns error) | | `StreamStore.GetSeqFromTime` | store.go:109 | PORTED | `IStreamStore.GetSeqFromTime` | Uses DateTime instead of time.Time | | `StreamStore.FilteredState` | store.go:110 | PORTED | `IStreamStore.FilteredState` | Signature matches | | `StreamStore.SubjectsState` | store.go:111 | PORTED | `IStreamStore.SubjectsState` | Returns Dictionary | | `StreamStore.SubjectsTotals` | store.go:112 | PORTED | `IStreamStore.SubjectsTotals` | Returns Dictionary | | `StreamStore.AllLastSeqs` | store.go:113 | PORTED | `IStreamStore.AllLastSeqs` | Returns ulong[] | | `StreamStore.MultiLastSeqs` | store.go:114 | PORTED | `IStreamStore.MultiLastSeqs` | Signature matches | | `StreamStore.SubjectForSeq` | store.go:115 | PORTED | `IStreamStore.SubjectForSeq` | Signature matches | | `StreamStore.NumPending` | store.go:116 | PORTED | `IStreamStore.NumPending` | Returns tuple `(ulong Total, ulong ValidThrough)` | | `StreamStore.NumPendingMulti` | store.go:117 | MISSING | — | gsl.SimpleSublist not ported | | `StreamStore.State` | store.go:118 | PORTED | `IStreamStore.State` | Returns `Storage.StreamState` | | `StreamStore.FastState` | store.go:119 | PORTED | `IStreamStore.FastState(ref)` | Uses `ref` parameter | | `StreamStore.EncodedStreamState` | store.go:120 | PORTED | `IStreamStore.EncodedStreamState` | Signature matches | | `StreamStore.SyncDeleted` | store.go:121 | MISSING | — | DeleteBlocks interface not ported | | `StreamStore.Type` | store.go:122 | PORTED | `IStreamStore.Type` | Signature matches | | `StreamStore.RegisterStorageUpdates` | store.go:123 | MISSING | — | No callback registration | | `StreamStore.RegisterStorageRemoveMsg` | store.go:124 | MISSING | — | No callback registration | | `StreamStore.RegisterProcessJetStreamMsg` | store.go:125 | MISSING | — | No callback registration | | `StreamStore.UpdateConfig` | store.go:126 | PORTED | `IStreamStore.UpdateConfig` | Signature matches | | `StreamStore.Delete` | store.go:127 | PORTED | `IStreamStore.Delete` | Signature matches | | `StreamStore.Stop` | store.go:128 | PORTED | `IStreamStore.Stop` | Signature matches | | `StreamStore.ConsumerStore` | store.go:129 | PORTED | `IStreamStore.ConsumerStore` | Returns IConsumerStore | | `StreamStore.AddConsumer` | store.go:130 | MISSING | — | Not in IStreamStore | | `StreamStore.RemoveConsumer` | store.go:131 | MISSING | — | Not in IStreamStore | | `StreamStore.Snapshot` | store.go:132 | PARTIAL | `Snapshots/StreamSnapshotService.cs` | TAR+S2 snapshot exists but not on IStreamStore interface; different signature | | `StreamStore.Utilization` | store.go:133 | MISSING | — | Not in IStreamStore | | `StreamStore.ResetState` | store.go:134 | PORTED | `IStreamStore.ResetState` | Signature matches | | `RetentionPolicy` (enum) | store.go:137-148 | PORTED | `Models/JetStreamPolicies.cs` `RetentionPolicy` | Limits, Interest, WorkQueue | | `RetentionPolicy.String()` | store.go:478-489 | NOT_APPLICABLE | — | .NET enum has ToString() by default | | `RetentionPolicy.MarshalJSON` | store.go:491-502 | NOT_APPLICABLE | — | System.Text.Json handles enum serialization | | `RetentionPolicy.UnmarshalJSON` | store.go:504-516 | NOT_APPLICABLE | — | Same | | `DiscardPolicy` (enum) | store.go:152-159 | PORTED | `Models/JetStreamPolicies.cs` `DiscardPolicy` | Old, New | | `DiscardPolicy.String/MarshalJSON/UnmarshalJSON` | store.go:518-550 | NOT_APPLICABLE | — | Handled by System.Text.Json | | `StorageType.String/MarshalJSON/UnmarshalJSON` | store.go:562-594 | NOT_APPLICABLE | — | Handled by System.Text.Json | | `AckPolicy` (enum: AckNone, AckAll, AckExplicit) | store.go:596-633 | PORTED | `Models/ConsumerConfig.cs` `AckPolicy` | None, Explicit, All | | `AckPolicy.MarshalJSON/UnmarshalJSON` | store.go:608-633 | NOT_APPLICABLE | — | Handled by System.Text.Json | | `ReplayPolicy` (enum) | store.go:635-666 | PORTED | `Models/JetStreamPolicies.cs` `ReplayPolicy` | Instant, Original | | `ReplayPolicy.MarshalJSON/UnmarshalJSON` | store.go:645-666 | NOT_APPLICABLE | — | Same | | `DeliverPolicy` (enum) | store.go:668-726 | PORTED | `Models/JetStreamPolicies.cs` `DeliverPolicy` | All, Last, New, ByStartSequence, ByStartTime, LastPerSubject | | `DeliverPolicy.MarshalJSON/UnmarshalJSON` | store.go:688-726 | NOT_APPLICABLE | — | Same | | `StreamState` (struct) | store.go:162-175 | PORTED | `Storage/StreamState.cs` | record struct with all fields | | `SimpleState` (struct) | store.go:178-187 | PORTED | `Storage/StreamState.cs` | record struct; internal firstNeedsUpdate/lastNeedsUpdate fields not ported | | `LostStreamData` (struct) | store.go:190-193 | PORTED | `Storage/StreamState.cs` `LostStreamData` class | Msgs and Bytes fields | | `SnapshotResult` (struct) | store.go:196-200 | PARTIAL | `Snapshots/StreamSnapshotService.cs` `SnapshotRestoreResult` | Different shape; no Reader/errCh; restore-oriented | | Stream state encoding constants | store.go:202-211 | PARTIAL | `Storage/ConsumerStateCodec.cs` | Magic/version constants exist for consumer state; stream state encoding constants not in dedicated file | | `DeleteBlock` (interface) | store.go:218-221 | MISSING | — | Not ported | | `DeleteBlocks` (slice type) | store.go:223 | MISSING | — | Not ported | | `StreamReplicatedState` (struct) | store.go:227-234 | MISSING | — | Not ported as standalone type | | `IsEncodedStreamState()` | store.go:237-239 | MISSING | — | Not ported | | `DecodeStreamState()` | store.go:243-305 | MISSING | — | Not ported | | `DeleteRange` (struct + methods) | store.go:308-328 | MISSING | — | Not ported | | `DeleteSlice` (type + methods) | store.go:331-347 | MISSING | — | Not ported | | `DeleteBlocks.NumDeleted()` | store.go:349-355 | MISSING | — | Not ported | | `ConsumerStore` (interface) | store.go:358-374 | PORTED | `Storage/IConsumerStore.cs` | All 13 methods ported | | `ConsumerStore.SetStarting` | store.go:359 | PORTED | `IConsumerStore.SetStarting` | Void (Go returns error) | | `ConsumerStore.UpdateStarting` | store.go:360 | PORTED | `IConsumerStore.UpdateStarting` | Matches | | `ConsumerStore.Reset` | store.go:361 | PORTED | `IConsumerStore.Reset` | Void (Go returns error) | | `ConsumerStore.HasState` | store.go:362 | PORTED | `IConsumerStore.HasState` | Matches | | `ConsumerStore.UpdateDelivered` | store.go:363 | PORTED | `IConsumerStore.UpdateDelivered` | Void (Go returns error) | | `ConsumerStore.UpdateAcks` | store.go:364 | PORTED | `IConsumerStore.UpdateAcks` | Void (Go returns error) | | `ConsumerStore.UpdateConfig` | store.go:365 | MISSING | — | Not on IConsumerStore | | `ConsumerStore.Update` | store.go:366 | PORTED | `IConsumerStore.Update` | Matches | | `ConsumerStore.State` | store.go:367 | PORTED | `IConsumerStore.State` | Returns `ConsumerState` (Go returns `(*ConsumerState, error)`) | | `ConsumerStore.BorrowState` | store.go:368 | PORTED | `IConsumerStore.BorrowState` | Matches | | `ConsumerStore.EncodedState` | store.go:369 | PORTED | `IConsumerStore.EncodedState` | Returns byte[] | | `ConsumerStore.Type` | store.go:370 | PORTED | `IConsumerStore.Type` | Matches | | `ConsumerStore.Stop` | store.go:371 | PORTED | `IConsumerStore.Stop` | Matches | | `ConsumerStore.Delete` | store.go:372 | PORTED | `IConsumerStore.Delete` | Matches | | `ConsumerStore.StreamDelete` | store.go:373 | PORTED | `IConsumerStore.StreamDelete` | Matches | | `SequencePair` (struct) | store.go:377-380 | PORTED | `Storage/ConsumerState.cs` `SequencePair` | record struct | | `ConsumerState` (struct) | store.go:383-394 | PORTED | `Storage/ConsumerState.cs` | Class with Delivered, AckFloor, Pending, Redelivered | | `Pending` (struct) | store.go:461-464 | PORTED | `Storage/ConsumerState.cs` `Pending` | record struct | | `encodeConsumerState()` | store.go:397-457 | PORTED | `Storage/ConsumerStateCodec.Encode()` | Wire-compatible binary encoding | | `isOutOfSpaceErr()` | store.go:728-730 | MISSING | — | No dedicated helper | | `errFirstSequenceMismatch` | store.go:733 | MISSING | — | Not ported | | `isClusterResetErr()` | store.go:735-737 | MISSING | — | Not ported | | `bytesToString()` | store.go:763-769 | NOT_APPLICABLE | — | .NET strings are native; no unsafe cast needed | | `stringToBytes()` | store.go:772-778 | NOT_APPLICABLE | — | Same | | `copyString()` | store.go:783-787 | NOT_APPLICABLE | — | .NET strings are immutable; always safe | | `isPermissionError()` | store.go:789-791 | MISSING | — | No dedicated helper | | `StreamConfigRequest` | stream.go:41-46 | MISSING | — | No `Pedantic` field in .NET; validation is in `JetStreamConfigValidator` | | `StreamConfig` (struct, ~80 fields) | stream.go:50-125 | PARTIAL | `Models/StreamConfig.cs` | Core fields ported; missing: `NoAck`, `Placement`, `Compression`, `MirrorDirect`, `DiscardNewPer`, `AllowRollup`, `ConsumerLimits`, `Mirror` as `StreamSource` (simplified to string), `Sources` as `[]*StreamSource` (simplified) | | `StreamConfig.clone()` | stream.go:129-161 | PARTIAL | `StreamManager.NormalizeConfig()` | Deep copy via manual property assignment; similar intent | | `StreamConsumerLimits` | stream.go:163-166 | MISSING | — | Not ported | | `SubjectTransformConfig` | stream.go:169-172 | PARTIAL | `StreamConfig.SubjectTransformSource/Dest` | Flattened into StreamConfig instead of nested object | | `RePublish` | stream.go:175-179 | PARTIAL | `StreamConfig.RePublishSource/Dest/HeadersOnly` | Flattened into StreamConfig instead of nested object | | `PersistModeType` (enum) | stream.go:182-202 | PORTED | `Models/StreamConfig.cs` `PersistMode` | Sync=0, Async=1 | | `PersistModeType.MarshalJSON/UnmarshalJSON` | stream.go:215-236 | NOT_APPLICABLE | — | System.Text.Json handles it | | `JSPubAckResponse` | stream.go:239-242 | PARTIAL | `Publish/PubAck.cs` | Different structure; no wrapping ApiError | | `PubAck` | stream.go:255-263 | PARTIAL | `Publish/PubAck.cs` | Has Stream/Seq; missing Domain, Duplicate, Value, BatchId, BatchSize | | `CounterValue` | stream.go:267-269 | MISSING | — | Not ported | | `CounterSources` | stream.go:273 | MISSING | — | Not ported | | `StreamInfo` | stream.go:276-287 | PARTIAL | `Api/JetStreamApiResponse.cs` `JetStreamStreamInfo` | Has Config+State; missing Created, Domain, Cluster, Mirror, Sources, Alternates, TimeStamp | | `ClusterInfo` | stream.go:304-312 | MISSING | — | Not ported as standalone API type | | `PeerInfo` | stream.go:316-325 | MISSING | — | Not ported as standalone API type | | `StreamSourceInfo` | stream.go:328-336 | PARTIAL | `MirrorSource/MirrorCoordinator.cs` `MirrorInfoResponse` | Simplified; missing External, SubjectTransforms | | `StreamSource` (struct) | stream.go:339-349 | PARTIAL | `Models/StreamConfig.cs` `StreamSourceConfig` | Has Name, FilterSubject, SourceAccount; missing OptStartSeq, OptStartTime, SubjectTransforms, External, iname | | `ExternalStream` | stream.go:352-355 | MISSING | — | Not ported | | `ExternalStream.Domain()` | stream.go:358-363 | MISSING | — | Not ported | | stream ingest constants | stream.go:366-384 | MISSING | — | streamDefaultMaxQueueMsgs/Bytes, batch inflight constants not ported | | `stream` (struct, ~110 fields) | stream.go:388-497 | PARTIAL | `StreamManager` + `StreamHandle` | StreamHandle is simplified record; stream struct's ~110 fields reduced to Config+Store; no mu/client/sysc/msgs/gets/ackq/lseq/ddmap/mirror/sources/node/etc | | `inflightSubjectRunningTotal` | stream.go:500-503 | MISSING | — | Not ported | | `msgCounterRunningTotal` | stream.go:508-511 | MISSING | — | Not ported | | `sourceInfo` (struct) | stream.go:514-533 | PARTIAL | `MirrorSource/SourceCoordinator.cs` | Simplified source tracking | | JS header constants (JSMsgId, JSExpectedStream, etc.) | stream.go:543-598 | PARTIAL | `Publish/PublishOptions.cs` | Some headers used in publish path; no centralized header constant file | | KV operation headers | stream.go:571-573 | MISSING | — | Not ported | | Scheduler headers | stream.go:577-580 | MISSING | — | Not ported | | Republish response headers | stream.go:584-591 | MISSING | — | Not ported | | Rollup constants | stream.go:595-597 | MISSING | — | Not ported | | Delete marker reason constants | stream.go:601-604 | MISSING | — | Not ported | | `ddentry` (struct) | stream.go:612-616 | PARTIAL | `Publish/PublishPreconditions.cs` `DedupeEntry` | Has Sequence+Timestamp; id handled as dictionary key | | `StreamMaxReplicas` | stream.go:619 | MISSING | — | Not ported as constant | | `addStream()` / `addStreamWithAssignment()` | stream.go:621-980 | PARTIAL | `StreamManager.CreateOrUpdate()` | High-level create/update logic present; missing: account limits checking, inflight WaitGroup, file store config auto-tuning, cluster mode handling, internal client setup, pubAck template, dedupe rebuild, advisory sending | | `StreamSource.composeIName()` | stream.go:986-1022 | MISSING | — | Source indexing not ported | | `StreamSource.setIndexName()` | stream.go:1026-1027 | MISSING | — | Same | | `stream.streamAssignment()` | stream.go:1030-1033 | MISSING | — | Not ported (cluster-specific) | | `stream.setStreamAssignment()` | stream.go:1036-1078 | MISSING | — | Not ported (cluster-specific) | | `stream.monitorQuitC()` | stream.go:1081-1091 | MISSING | — | Not ported | | `stream.signalMonitorQuit()` | stream.go:1095-1102 | MISSING | — | Not ported | | `stream.updateC()` | stream.go:1105-1111 | MISSING | — | Not ported | | `stream.IsLeader()` / `isLeader()` | stream.go:1115-1126 | PARTIAL | `JetStreamMetaGroup` has leader concept | Not on stream level | | `stream.setLeader()` | stream.go:1142-1185 | MISSING | — | No stream-level leader management | | `stream.startClusterSubs()` / `stopClusterSubs()` | stream.go:1189-1200 | MISSING | — | Not ported | | `stream.account()` | stream.go:1203-1219 | MISSING | — | No per-stream account binding | | `stream.maxMsgSize()` | stream.go:1223-1253 | PARTIAL | `StreamApiHandlers` has MaxMsgSize validation | Not a stream method | | `stream.autoTuneFileStorageBlockSize()` | stream.go:1259-1288 | MISSING | — | No block size auto-tuning | | `stream.rebuildDedupe()` | stream.go:1296-1326 | PARTIAL | `PublishPreconditions` | Basic dedupe exists; no rebuild-from-store on recovery | | `stream.lastSeqAndCLFS()` | stream.go:1330-1331 | MISSING | — | Cluster failure sequence tracking not ported | | `stream.getCLFS()` / `setCLFS()` | stream.go:1334-1346 | MISSING | — | Same | | `stream.lastSeq()` / `setLastSeq()` | stream.go:1349-1358 | MISSING | — | StreamHandle doesn't track lseq | | `stream.sendCreateAdvisory()` | stream.go:1361-1390 | MISSING | — | Advisory publishing not ported at stream level | | `stream.sendDeleteAdvisoryLocked()` | stream.go:1393-1413 | MISSING | — | Same | | `stream.sendUpdateAdvisoryLocked()` | stream.go:1416-1436 | MISSING | — | Same | | `stream.sendStreamBatchAbandonedAdvisory()` | stream.go:1439-1464 | MISSING | — | Same | | `stream.createdTime()` / `setCreatedTime()` | stream.go:1468-1479 | MISSING | — | Not tracked on StreamHandle | | `jsa.subjectsOverlap()` | stream.go:1485-1498 | PORTED | `StreamManager.ValidateConfigUpdate()` | Subject overlap check exists in ValidateConfigUpdate | | `checkStreamCfg()` | stream.go:1500-2084 | PARTIAL | `JetStreamConfigValidator.Validate()` + `StreamManager.CreateOrUpdate()` | Basic name/subject/retention validation; many checks missing: MaxBytes limits, per-account limits, subject transform validation, republish cycle detection done separately, pedantic mode, placement validation | | `jsa.configUpdateCheck()` | stream.go:2104-2236 | PARTIAL | `StreamManager.ValidateConfigUpdate()` | Immutable fields checked: storage, retention, mirror, sources, maxConsumers, replicas; missing: sealed/denyDelete/denyPurge unset checks, TTL status, counter setting, schedules, persist mode, limit adjustment, compression | | `stream.update()` / `updateWithAdvisory()` | stream.go:2240-2554 | PARTIAL | `StreamManager.CreateOrUpdate()` | Config update path exists but much simplified; missing: subscription management, dedupe timer reset, source consumer management, direct/mirror-direct subscription toggling, republish transform update, subject transform update, tier usage tracking, advisory sending | | `stream.purge()` / `purgeLocked()` | stream.go:2567-2642 | PARTIAL | `StreamManager.Purge()` + `PurgeEx()` | Basic purge and extended purge present; missing: sealed check, consumer purge propagation, preAck clearing | | `stream.deleteMsg()` / `removeMsg()` | stream.go:2645-2663 | PARTIAL | `StreamManager.DeleteMessage()` | Basic removal present; missing: preAck clearing | | `stream.eraseMsg()` | stream.go:2666-2678 | PARTIAL | `IStreamStore.EraseMsg` on interface | Interface defined but no erase implementation that overwrites with random data | | `stream.isMirror()` | stream.go:2681-2685 | PORTED | `StreamManager.GetMirrorInfo()` checks mirror | Inline check | | `stream.sourcesInfo()` | stream.go:2688-2695 | PARTIAL | `StreamManager.GetSourceInfos()` | Simplified | | `stream.sourceInfo()` | stream.go:2699-2735 | PARTIAL | `SourceCoordinator.GetSourceInfo()` | Simplified | | `stream.mirrorInfo()` | stream.go:2739-2743 | PARTIAL | `StreamManager.GetMirrorInfo()` | Simplified | | `stream.retryDisconnectedSyncConsumers()` | stream.go:2747-2778 | MISSING | — | Not ported | | source health constants | stream.go:2782-2785 | MISSING | — | Not ported | | `stream.processMirrorMsgs()` | stream.go:2788-2854 | PARTIAL | `MirrorCoordinator` | Simplified mirror message processing; no health check, stall detection, flow control | | `sourceInfo.isCurrentSub()` | stream.go:2859-2860 | MISSING | — | Not ported | | `stream.processInboundMirrorMsg()` | stream.go:2863-3014 | PARTIAL | `MirrorCoordinator.OnOriginAppendAsync()` | Greatly simplified; no sequence tracking, flow control, heartbeat handling, skip logic | | `stream.setMirrorErr()` | stream.go:3017-3022 | MISSING | — | Not ported | | `stream.cancelMirrorConsumer()` | stream.go:3027-3032 | MISSING | — | Not ported | | `stream.retryMirrorConsumer()` | stream.go:3038-3044 | MISSING | — | Not ported | | `stream.skipMsgs()` | stream.go:3048-3073 | MISSING | — | No skip with NRG proposal | | `calculateRetryBackoff()` | stream.go:3084-3089 | MISSING | — | Not ported | | `stream.scheduleSetupMirrorConsumerRetry()` | stream.go:3098-3118 | MISSING | — | Not ported | | `stream.setupMirrorConsumer()` | stream.go:3125-3416 | MISSING | — | Full mirror consumer setup with API-based consumer creation, subject transforms, delivery tracking not ported | | `stream.streamSource()` | stream.go:3419-3424 | MISSING | — | Not ported | | `stream.retrySourceConsumerAtSeq()` | stream.go:3429-3435 | MISSING | — | Not ported | | `stream.cancelSourceConsumer()` | stream.go:3439-3443 | MISSING | — | Not ported | | `stream.cancelSourceInfo()` | stream.go:3451-3470 | MISSING | — | Not ported | | `stream.setupSourceConsumer()` | stream.go:3478-3500+ | MISSING | — | Full source consumer setup not ported (continues past line 4000) | | Status | Count | | PORTED | 55 | | PARTIAL | 30 | | MISSING | 75 | | NOT_APPLICABLE | 12 | | DEFERRED | 0 | | **Total** | **172** | | `(si *sourceInfo) genSourceHeader` | stream.go:4010 | MISSING | — | Generates 2.10-style source header (stream name, seq, filter, transform). No .NET equivalent; SourceCoordinator tracks seq differently. | | `streamAndSeqFromAckReply` | stream.go:4041 | MISSING | — | Parses stream/seq from `$JS.ACK` reply tokens. No wire-level reply parsing in .NET. | | `streamAndSeq` | stream.go:4058 | MISSING | — | Extracts stream name, index name, sequence from source header string. No .NET equivalent. | | `(mset *stream) setStartingSequenceForSources` | stream.go:4079 | PARTIAL | `SourceCoordinator.LastOriginSequence` | Go scans stored messages backwards to find starting seq per source using gsl.SimpleSublist. .NET SourceCoordinator has simpler resume-from-last logic. | | `(mset *stream) resetSourceInfo` | stream.go:4172 | PARTIAL | `StreamManager.RebuildReplicationCoordinators` | Go resets all sourceInfo structs with transforms. .NET rebuilds coordinators but lacks per-source subject transforms array. | | `(mset *stream) startingSequenceForSources` | stream.go:4207 | PARTIAL | `SourceCoordinator.LastOriginSequence` | Go does reverse scan with SimpleSublist filtering. .NET relies on simpler incremental tracking. | | `(mset *stream) setupSourceConsumers` | stream.go:4318 | PARTIAL | `SourceCoordinator.StartSyncLoop / StartPullSyncLoop` | Go creates internal consumers per source with proper starting sequences. .NET has background sync loops but no internal consumer creation. | | `(mset *stream) subscribeToStream` | stream.go:4348 | MISSING | — | Creates internal subscriptions for all stream subjects + mirror/source setup. .NET uses StreamManager.Capture instead of subscription-based ingest. | | `(mset *stream) subscribeToDirect` | stream.go:4406 | PARTIAL | `DirectApiHandlers.HandleGet` | Go creates queue subscriptions for `$JS.API.DIRECT.GET.{stream}`. .NET has API handler but no queue-subscription wiring. | | `(mset *stream) unsubscribeToDirect` | stream.go:4431 | MISSING | — | Unsubscribes direct get subscriptions. No .NET equivalent. | | `(mset *stream) subscribeToMirrorDirect` | stream.go:4443 | MISSING | — | Sets up direct-get queue subs for mirror stream name. Not implemented in .NET. | | `(mset *stream) unsubscribeToMirrorDirect` | stream.go:4474 | MISSING | — | Clears mirror direct-get subscriptions. Not implemented. | | `(mset *stream) stopSourceConsumers` | stream.go:4487 | PORTED | `SourceCoordinator.StopAsync` | .NET coordinator has async stop with cancellation. | | `(mset *stream) removeInternalConsumer` | stream.go:4494 | MISSING | — | Clears consumer name on sourceInfo. .NET doesn't track internal consumer names. | | `(mset *stream) unsubscribeToStream` | stream.go:4503 | MISSING | — | Unsubscribes from all stream subjects, mirrors, sources. .NET uses StreamManager.Delete instead. | | `(mset *stream) deleteInflightBatches` | stream.go:4535 | PARTIAL | `JetStreamPublisher.ClearBatches` | Go cleans up batch state with lock ordering. .NET has simplified ClearBatches on AtomicBatchPublishEngine. | | `(mset *stream) deleteBatchApplyState` | stream.go:4552 | MISSING | — | Cleans up raft batch-apply entries with pool return. No .NET equivalent (no RAFT batch apply). | | `(mset *stream) subscribeInternal` | stream.go:4563 | MISSING | — | Creates internal subscription with auto-incremented SID. .NET server doesn't use internal subscriptions for JetStream. | | `(mset *stream) queueSubscribeInternal` | stream.go:4577 | MISSING | — | Creates internal queue subscription. Not implemented in .NET. | | `(mset *stream) unsubscribeInternal` | stream.go:4594 | MISSING | — | Removes internal subscription by subject lookup. Not implemented. | | `(mset *stream) unsubscribe` | stream.go:4616 | MISSING | — | Unsubscribes by subscription pointer. Not implemented. | | `(mset *stream) setupStore` | stream.go:4623 | PARTIAL | `StreamManager.CreateStore` | Go wires up MemStore/FileStore with encryption, callbacks for storage updates, remove msg, and process inbound. .NET creates store but lacks callback wiring. | | `(mset *stream) storeUpdates` | stream.go:4682 | MISSING | — | Callback for store changes: updates consumer pending counts, JetStream account usage. No .NET equivalent. | | `(mset *stream) numMsgIds` | stream.go:4714 | MISSING | — | Returns count of tracked dedup entries. Not exposed in .NET. | | `(mset *stream) checkMsgId` | stream.go:4721 | PORTED | `PublishPreconditions.IsDuplicate` | .NET uses ConcurrentDictionary-based dedup check. | | `(mset *stream) purgeMsgIds` | stream.go:4731 | PORTED | `PublishPreconditions.TrimOlderThan` | Go uses timer-driven purge with GC compaction. .NET does cutoff-based trim. Lacks timer scheduling. | | `(mset *stream) storeMsgId` | stream.go:4778 | PORTED | `PublishPreconditions.Record` | .NET records msgId with timestamp. | | `(mset *stream) storeMsgIdLocked` | stream.go:4786 | PORTED | `PublishPreconditions.Record` | Internal locked variant; .NET uses ConcurrentDictionary. | | `getMsgId` | stream.go:4803 | MISSING | — | Fast lookup of `Nats-Msg-Id` header from raw bytes. .NET uses PublishOptions.MsgId instead. | | `getExpectedLastMsgId` | stream.go:4808 | MISSING | — | Fast lookup of `Nats-Expected-Last-Msg-Id`. .NET checks via PublishOptions.ExpectedLastMsgId. | | `getExpectedStream` | stream.go:4813 | MISSING | — | Fast lookup of `Nats-Expected-Stream`. Not implemented as wire parser. | | `getExpectedLastSeq` | stream.go:4818 | MISSING | — | Fast lookup of `Nats-Expected-Last-Sequence`. .NET uses PublishOptions.ExpectedLastSeq. | | `getRollup` | stream.go:4827 | MISSING | — | Fast lookup of `Nats-Rollup` header. No rollup support in .NET. | | `getExpectedLastSeqPerSubject` | stream.go:4836 | MISSING | — | Fast lookup of `Nats-Expected-Last-Subject-Sequence`. .NET uses PublishOptions but no wire parser. | | `getExpectedLastSeqPerSubjectForSubject` | stream.go:4845 | MISSING | — | Subject override for per-subject seq check. Not in .NET. | | `getMessageTTL` | stream.go:4852 | MISSING | — | Parses `Nats-Msg-TTL` header from raw bytes. Not implemented in .NET. | | `parseMessageTTL` | stream.go:4863 | MISSING | — | Parses TTL string ("never", duration, seconds). Not implemented. | | `getMessageIncr` | stream.go:4888 | MISSING | — | Parses `Nats-Msg-Incr` header for counter streams. Not implemented. | | `getMessageSchedule` | stream.go:4898 | MISSING | — | Parses `Nats-Msg-Schedule` header. Not implemented. | | `nextMessageSchedule` | stream.go:4907 | MISSING | — | Calculates next schedule from header + timestamp. Not implemented. | | `getMessageScheduleTTL` | stream.go:4917 | MISSING | — | Parses schedule TTL header. Not implemented. | | `getMessageScheduleTarget` | stream.go:4930 | MISSING | — | Parses schedule target header. Not implemented. | | `getMessageScheduleSource` | stream.go:4938 | MISSING | — | Parses schedule source header. Not implemented. | | `getMessageScheduler` | stream.go:4946 | MISSING | — | Parses scheduler header. Not implemented. | | `getBatchId` | stream.go:4954 | MISSING | — | Parses `Nats-Batch-Id` header from raw bytes. .NET uses PublishOptions.BatchId instead. | | `getBatchSequence` | stream.go:4962 | MISSING | — | Parses `Nats-Batch-Seq` header. .NET uses PublishOptions.BatchSeq. | | `(mset *stream) IsClustered` | stream.go:4971 | NOT_APPLICABLE | — | Checks if RAFT node is present. .NET uses JetStreamMetaGroup for cluster simulation but not per-stream RAFT. | | `(mset *stream) isClustered` | stream.go:4978 | NOT_APPLICABLE | — | Lock-held variant. | | `inMsg` struct | stream.go:4983 | MISSING | — | Internal message queue struct (subj, reply, hdr, msg, sourceInfo, msgTrace). .NET doesn't have internal msg queue. | | `inMsgPool` | stream.go:4994 | MISSING | — | sync.Pool for inMsg objects. Not needed without internal queue. | | `(im *inMsg) returnToPool` | stream.go:4999 | MISSING | — | Returns inMsg to pool. | | `(mset *stream) queueInbound` | stream.go:5004 | MISSING | — | Enqueues inbound message with rate limit error on overflow. .NET uses direct method calls instead. | | `dgPool` / `directGetReq` | stream.go:5019 | MISSING | — | Pool and struct for deferred direct get requests. | | `(mset *stream) processDirectGetRequest` | stream.go:5033 | PARTIAL | `DirectApiHandlers.HandleGet` | Go handles full JSApiMsgGetRequest with validation. .NET only supports seq-based lookup. Missing: LastFor, NextFor, MultiLastFor, StartTime, Batch, MaxBytes. | | `(mset *stream) processDirectGetLastBySubjectRequest` | stream.go:5087 | MISSING | — | Direct get by subject appended to request subject. Not implemented in .NET. | | `(mset *stream) getDirectMulti` | stream.go:5158 | MISSING | — | Multi-subject direct get with batch response + EOB. Not implemented. | | `(mset *stream) getDirectRequest` | stream.go:5270 | PARTIAL | `DirectApiHandlers.HandleGet` | Go handles seq, NextFor, LastFor, batch, MaxBytes, NoHeaders. .NET only handles seq lookup. | | Direct get header constants (`dg`, `dgb`, `eob`, `eobm`) | stream.go:5150 | MISSING | — | Wire format header templates for direct get responses. | | `(mset *stream) processInboundJetStreamMsg` | stream.go:5409 | MISSING | — | Entry point: copies msg, adds trace, queues inbound. .NET uses StreamManager.Capture. | | Error sentinel variables | stream.go:5428 | PARTIAL | — | `errLastSeqMismatch`, `errMsgIdDuplicate`, etc. .NET uses error codes in PubAck instead. | | `(mset *stream) processJetStreamMsg` | stream.go:5437 | PARTIAL | `JetStreamPublisher.TryCaptureWithOptions + StreamManager.Capture` | Core message processing. Go: full consistency checks, subject transform, sealed/sealed check, header extraction, dedup, TTL, counter increment, schedule validation, rollup, interest retention skip, store. .NET: basic dedup + expected-last-seq + capture. Missing: rollup, TTL, counters, schedules, per-subject seq, interest retention skip, sealed check at msg level, clustered RAFT propose. | | `jsPubMsg` struct | stream.go:6770 | MISSING | — | Internal pub message with dest subject, reply, store msg, consumer ref. | | `newJSPubMsg` | stream.go:6786 | MISSING | — | Constructor with pool allocation. | | `jsPubMsgPool` | stream.go:6784 | MISSING | — | sync.Pool for jsPubMsg. | | `(pm *jsPubMsg) returnToPool` | stream.go:6815 | MISSING | — | Pool return. | | `(pm *jsPubMsg) size` | stream.go:6826 | MISSING | — | Size calculation for flow control. | | `jsOutQ` struct | stream.go:6834 | MISSING | — | Typed output queue wrapper around ipQueue. | | `(q *jsOutQ) sendMsg` | stream.go:6838 | MISSING | — | Sends message on output queue. | | `(q *jsOutQ) send` | stream.go:6844 | MISSING | — | Pushes jsPubMsg to queue. | | `StoredMsg` struct | stream.go:6858 | PORTED | `StoredMessage` | .NET StoredMessage record has Subject, Sequence, Payload, TimestampUtc. | | `(mset *stream) setupSendCapabilities` | stream.go:6869 | MISSING | — | Creates output queue and launches internalLoop goroutine. | | `(mset *stream) accName` | stream.go:6881 | NOT_APPLICABLE | — | Returns account name. .NET uses Account directly. | | `(mset *stream) name / nameLocked` | stream.go:6892 | NOT_APPLICABLE | — | Returns stream name. .NET uses StreamHandle.Config.Name. | | `(mset *stream) internalLoop` | stream.go:6907 | MISSING | — | Main event loop: outq dispatch, inbound msg processing, direct gets, ack queue for interest retention. Complex select{} loop. Not implemented in .NET. | | `(mset *stream) resetAndWaitOnConsumers` | stream.go:7037 | MISSING | — | Steps down and waits for consumer monitor goroutines. | | `(mset *stream) delete` | stream.go:7059 | PARTIAL | `StreamManager.Delete` | Go calls stop(true, true) with advisory. .NET removes from ConcurrentDictionary. | | `(mset *stream) stop` | stream.go:7067 | PARTIAL | `StreamManager.Delete` | Go: full shutdown sequence (mark closed, signal monitor, unsubscribe, cleanup consumers, advisories, RAFT node stop/delete, dedup timer cleanup, queue unregister, store delete/stop). .NET: simple dictionary removal. | | `(mset *stream) getMsg` | stream.go:7249 | PORTED | `StreamManager.GetMessage` | .NET loads from store by sequence. | | `(mset *stream) getConsumers` | stream.go:7266 | PARTIAL | `ConsumerManager` | Go returns copy of consumer list. .NET has ConsumerManager but not per-stream consumer list. | | `(mset *stream) numPublicConsumers` | stream.go:7273 | MISSING | — | Count of non-DIRECT consumers. Not in .NET. | | `(mset *stream) getPublicConsumers` | stream.go:7278 | MISSING | — | Filters out DIRECT consumers. Not in .NET. | | `(mset *stream) getDirectConsumers` | stream.go:7292 | MISSING | — | Returns only DIRECT consumers. Not in .NET. | | `(mset *stream) numDirectConsumers` | stream.go:7471 | MISSING | — | Counts DIRECT consumers. Not in .NET. | | `(mset *stream) state` | stream.go:7487 | PORTED | `StreamManager.GetStateAsync` | .NET delegates to store.GetStateAsync. | | `(mset *stream) stateWithDetail` | stream.go:7490 | PARTIAL | `StreamManager.GetStateAsync` | Go has fast path (FastState) vs detailed (State). .NET always returns full state. | | `(mset *stream) Store` | stream.go:7509 | PORTED | `StreamHandle.Store` | Direct property access in .NET record. | | `(mset *stream) setConsumer` | stream.go:7367 | MISSING | — | Registers consumer on stream with filter tracking and consumer sublist (gsl). .NET uses separate ConsumerManager. | | `(mset *stream) removeConsumer` | stream.go:7388 | MISSING | — | Removes consumer from stream's maps and sublists. .NET uses ConsumerManager. | | `(mset *stream) swapSigSubs` | stream.go:7416 | MISSING | — | Updates signal subscriptions when consumer filter changes. Complex lock ordering. Not in .NET. | | `(mset *stream) lookupConsumer` | stream.go:7464 | PARTIAL | `ConsumerManager` | Go looks up by name on stream. .NET uses separate manager. | | `(mset *stream) partitionUnique` | stream.go:7516 | MISSING | — | Checks consumer filter partitions don't overlap. Not implemented. | | `(mset *stream) potentialFilteredConsumers` | stream.go:7541 | MISSING | — | Checks if filtered consumer matching is needed. Not in .NET. | | `checkInterestStateT/J` constants | stream.go:7307 | MISSING | — | Timer interval/jitter for interest state checks. | | `(mset *stream) checkInterestState` | stream.go:7320 | PARTIAL | `InterestRetentionPolicy.ShouldRetain` | Go checks ack floors across all consumers and compacts. .NET has simpler per-seq interest check. | | `(mset *stream) isInterestRetention` | stream.go:7353 | PORTED | Config check | .NET checks `stream.Config.Retention == RetentionPolicy.Interest`. | | `(mset *stream) numConsumers` | stream.go:7360 | PARTIAL | `ConsumerManager` | Go returns from stream's consumer map. .NET has ConsumerManager. | | `(mset *stream) noInterest` | stream.go:7556 | PARTIAL | `InterestRetentionPolicy.ShouldRetain` | .NET has simplified interest check without pre-ack support. | | `(mset *stream) noInterestWithSubject` | stream.go:7562 | PARTIAL | `InterestRetentionPolicy.ShouldRetain` | .NET uses SubjectMatch filtering. | | `(mset *stream) checkForInterest` | stream.go:7567 | PARTIAL | `InterestRetentionPolicy.ShouldRetain` | Go loads message for subject, checks filtered consumers. .NET simpler. | | `(mset *stream) checkForInterestWithSubject` | stream.go:7588 | PARTIAL | `InterestRetentionPolicy.ShouldRetain` | Go iterates consumers with needAck check. .NET iterates registered interests. | | `(mset *stream) hasPreAck` | stream.go:7605 | MISSING | — | Pre-ack tracking for messages acked before stored. Not in .NET. | | `(mset *stream) hasAllPreAcks` | stream.go:7619 | MISSING | — | Checks if all consumers pre-acked a sequence. Not in .NET. | | `(mset *stream) clearAllPreAcks` | stream.go:7631 | MISSING | — | Clears pre-ack map for a sequence. Not in .NET. | | `(mset *stream) clearAllPreAcksBelowFloor` | stream.go:7636 | MISSING | — | Clears pre-acks below a floor sequence. Not in .NET. | | `(mset *stream) registerPreAckLock` | stream.go:7645 | MISSING | — | Lock-acquiring pre-ack registration. Not in .NET. | | `(mset *stream) registerPreAck` | stream.go:7654 | MISSING | — | Registers pre-ack for consumer/sequence. Not in .NET. | | `(mset *stream) clearPreAck` | stream.go:7669 | MISSING | — | Clears single consumer pre-ack. Not in .NET. | | `(mset *stream) ackMsg` | stream.go:7685 | PARTIAL | `InterestRetentionPolicy + AckProcessor` | Go: full ack processing with pre-ack, clustered proposal, interest-raise-first. .NET has basic interest check + removal. | | `(mset *stream) snapshot` | stream.go:7764 | PORTED | `StreamSnapshotService.CreateTarSnapshotAsync` | .NET uses TAR+S2 snapshot service. | | `(a *Account) RestoreStream` | stream.go:7776 | PARTIAL | `StreamSnapshotService.RestoreTarSnapshotAsync` | Go: full restore with config validation, limit checks, directory management, consumer restore. .NET: simplified restore (purge + replay). Missing: config validation, consumer restore, limit checks. | | `(mset *stream) checkForOrphanMsgs` | stream.go:7979 | MISSING | — | Checks for dangling messages on interest retention streams at startup. Not implemented. | | `(mset *stream) checkConsumerReplication` | stream.go:8007 | MISSING | — | Verifies consumer replication matches stream for interest retention. Not in .NET. | | `(mset *stream) checkInMonitor` | stream.go:8029 | NOT_APPLICABLE | — | Atomic flag for monitor goroutine. .NET has no stream monitor loop. | | `(mset *stream) clearMonitorRunning` | stream.go:8041 | NOT_APPLICABLE | — | Clears monitor flag + cleanup. | | `(mset *stream) isMonitorRunning` | stream.go:8051 | NOT_APPLICABLE | — | Checks monitor flag. | | `(mset *stream) trackReplicationTraffic` | stream.go:8058 | NOT_APPLICABLE | — | Adjusts account stats for replication. .NET has no account-level replication stats. | | Status | Count | | PORTED | 11 | | PARTIAL | 24 | | MISSING | 73 | | NOT_APPLICABLE | 8 | | DEFERRED | 0 | | **Total** | **116** | | `ConsumerInfo` | consumer.go:55 | PARTIAL | `JetStreamConsumerInfo` (Api/JetStreamApiResponse.cs) | .NET has a simplified version inside API response; missing most fields (Delivered, AckFloor, NumAckPending, NumRedelivered, NumWaiting, NumPending, Cluster, PushBound, Paused, PauseRemaining, TimeStamp, PriorityGroups) | | `consumerInfoClusterResponse` | consumer.go:77 | MISSING | — | Cluster-internal response type for consumer info propagation | | `PriorityGroupState` | consumer.go:82 | MISSING | — | Reported in ConsumerInfo; no equivalent struct | | `ConsumerConfig` | consumer.go:88 | PORTED | `ConsumerConfig` (Models/ConsumerConfig.cs) | All major fields present. Minor: missing `Description`, `SampleFrequency`, `Replicas`, `MemoryStorage`, `Direct`, `InactiveThreshold`, `HeadersOnly`, `Name` (vs Durable), `DeliverGroup` | | `SequenceInfo` | consumer.go:143 | MISSING | — | Used in ConsumerInfo; no standalone equivalent | | `CreateConsumerRequest` | consumer.go:149 | PARTIAL | Parsed inline in `ConsumerApiHandlers.ParseConfig` | Missing `Action` and `Pedantic` fields | | `ConsumerAction` enum | consumer.go:156 | MISSING | — | ActionCreateOrUpdate/ActionUpdate/ActionCreate not modeled | | `ConsumerAction.MarshalJSON/UnmarshalJSON` | consumer.go:188-213 | MISSING | — | JSON serialization for consumer actions | | `ConsumerNakOptions` | consumer.go:216 | PARTIAL | Parsed inline in `AckProcessor.ProcessAck` | Delay extraction exists but not as a typed struct | | `PriorityPolicy` enum | consumer.go:221 | PORTED | `PriorityPolicy` (Models/ConsumerConfig.cs:80) | Has None, Overflow, PinnedClient. Missing `Prioritized` variant | | `PriorityPolicy.MarshalJSON/UnmarshalJSON` | consumer.go:261-290 | NOT_APPLICABLE | — | .NET uses enum serialization | | `DeliverPolicy` enum | consumer.go:293 | PORTED | `DeliverPolicy` (Models/JetStreamPolicies.cs:16) | All 6 variants present | | `AckPolicy` enum | consumer.go:330 | PORTED | `AckPolicy` (Models/ConsumerConfig.cs:69) | All 3 variants present | | `ReplayPolicy` enum | consumer.go:354 | PORTED | `ReplayPolicy` (Models/JetStreamPolicies.cs:27) | Both variants present | | `AckAck/AckOK/AckNak/AckProgress/AckNext/AckTerm` | consumer.go:376-388 | PORTED | `AckType` enum + `AckProcessor.ParseAckType` (Consumers/AckProcessor.cs:127) | All ack types recognized; AckNext (+NXT) handled implicitly | | `ackTermLimitsReason/ackTermUnackedLimitsReason` | consumer.go:391-394 | MISSING | — | Reason strings for limit-based terminations | | `JSPullRequestPendingMsgs/Bytes/NatsPinId` | consumer.go:43-46 | PARTIAL | Used in heartbeat headers in PushConsumerEngine | PinId header not used | | `JsPullRequestRemainingBytesT` | consumer.go:53 | MISSING | — | Template for 409 Batch Completed response | | `validGroupName` regex | consumer.go:49 | MISSING | — | Group name validation regex `^[a-zA-Z0-9/_=-]{1,16}$` | | `consumer` struct | consumer.go:409 | PARTIAL | `ConsumerHandle` (ConsumerManager.cs:331) | ConsumerHandle is a simplified record with NextSequence, Paused, PauseUntilUtc, Pending, PushFrames, AckProcessor. Missing: most of the ~80 fields (leader, client, sysc, sid, sseq, dseq, adflr, asflr, subjf, filters, dsubj, qgroup, lss, rlimit, ackSub, reqSub, etc.) | | `subjectFilter` struct | consumer.go:529 | PORTED | `CompiledFilter` (Consumers/PullConsumerEngine.cs:15) | Different approach but equivalent functionality | | `subjectFilters` type | consumer.go:535 | PORTED | `CompiledFilter.FromConfig` | Encapsulated in CompiledFilter | | `proposal` struct | consumer.go:547 | MISSING | — | RAFT proposal linked list for clustered consumers | | `JsAckWaitDefault` (30s) | consumer.go:554 | PORTED | `ConsumerConfig.AckWaitMs = 30_000` | Default set in ConsumerConfig | | `JsDeleteWaitTimeDefault` (5s) | consumer.go:557 | PARTIAL | `DeliveryInterestTracker(TimeSpan.FromSeconds(30))` | Different default (30s vs 5s) | | `JsFlowControlMaxPending` (32MB) | consumer.go:559 | PARTIAL | `MaxFlowControlPending = 2` (frames, not bytes) | Different unit/approach | | `JsDefaultMaxAckPending` (1000) | consumer.go:561 | MISSING | — | Not applied as default in ConsumerConfig | | `JsDefaultPinnedTTL` (2min) | consumer.go:564 | MISSING | — | Not applied as default | | `setConsumerConfigDefaults` | consumer.go:568 | MISSING | — | 110-line function setting MaxDeliver=-1, MaxWaiting defaults, AckWait defaults, BackOff override, MaxAckPending defaults, MaxRequestBatch limits, PinnedTTL default. No .NET equivalent | | `checkConsumerCfg` | consumer.go:680 | MISSING | — | 270-line comprehensive validation function. No .NET equivalent (JetStreamConfigValidator only validates streams) | | `ConsumerConfig.replicas` | consumer.go:397 | MISSING | — | Replicas calculation against parent stream | | `deliveryFormsCycle` | referenced at consumer.go:743 | MISSING | — | Cycle detection for push delivery subjects | | `stream.addConsumer` | consumer.go:956 | PARTIAL | `ConsumerManager.CreateOrUpdate` | Simplified: no WorkQueue checks, no replica validation, no RAFT, no event subjects | | `stream.addConsumerWithAction` | consumer.go:952 | PARTIAL | `ConsumerManager.CreateOrUpdate` | Action parameter not used | | `stream.addConsumerWithAssignment` | consumer.go:960 | PARTIAL | `ConsumerManager.CreateOrUpdate` | 370-line function. .NET has ~30 lines. Missing: durable update detection, WorkQueue partition uniqueness, consumer limit checks, store creation, ack subscription setup, subject filter compilation, stored state restoration, cluster assignment, rate limit setup, advisory sending | | `consumer.updateInactiveThreshold` | consumer.go:1335 | MISSING | — | Jitter calculation for ephemeral delete timers | | `consumer.updatePauseState` | consumer.go:1358 | PARTIAL | `ConsumerManager.Pause(stream, durable, DateTime)` | Timer-based resume exists but simpler implementation | | `consumer.consumerAssignment` | consumer.go:1383 | MISSING | — | No RAFT consumer assignment tracking | | `consumer.setConsumerAssignment` | consumer.go:1389 | MISSING | — | | | `consumer.monitorQuitC` | consumer.go:1407 | MISSING | — | Monitor goroutine quit channel | | `consumer.signalMonitorQuit` | consumer.go:1422 | MISSING | — | | | `consumer.updateC` | consumer.go:1431 | MISSING | — | | | `consumer.checkQueueInterest` | consumer.go:1439 | MISSING | — | Queue group interest checking | | `consumer.clearNode` | consumer.go:1459 | MISSING | — | RAFT node cleanup | | `consumer.IsLeader/isLeader` | consumer.go:1469 | MISSING | — | No leader election concept | | `consumer.setLeader` | consumer.go:1478 | MISSING | — | 240-line leader transition handling (subscribe internal ack/req/reset subs, flow control, push mode interest, delete timer, pause state, replay mode, start goroutines). Entirely missing | | `consumer.subscribeInternal` | consumer.go:1723 | MISSING | — | Internal subscription for ack/request subjects | | `consumer.unsubscribe` | consumer.go:1743 | MISSING | — | | | `consumer.handleClusterConsumerInfoRequest` | consumer.go:1718 | MISSING | — | Cluster info request handler | | `consumer.sendAdvisory` | consumer.go:1752 | MISSING | — | Generic advisory sender with interest check | | `consumer.sendDeleteAdvisoryLocked` | consumer.go:1771 | MISSING | — | Delete advisory | | `consumer.sendPinnedAdvisoryLocked` | consumer.go:1788 | MISSING | — | Pinned consumer advisory | | `consumer.sendUnpinnedAdvisoryLocked` | consumer.go:1807 | MISSING | — | Unpinned consumer advisory | | `consumer.sendCreateAdvisory` | consumer.go:1827 | MISSING | — | Create advisory | | `consumer.sendPauseAdvisoryLocked` | consumer.go:1847 | MISSING | — | Pause/unpause advisory | | `consumer.hasDeliveryInterest` | consumer.go:1886 | PARTIAL | `DeliveryInterestTracker` (Consumers/DeliveryInterestTracker.cs) | .NET tracks subscriber count; Go checks sublist + gateway interest | | `Server.hasGatewayInterest` | consumer.go:1908 | MISSING | — | Gateway interest checking | | `consumer.updateDeliveryInterest` | consumer.go:1925 | PARTIAL | `DeliveryInterestTracker.OnSubscribe/OnUnsubscribe` | Simplified; no delete timer or queue group handling | | `consumer.deleteNotActive` | consumer.go:1977 | PARTIAL | `DeliveryInterestTracker.ShouldDelete` | Go has 160-line function with pull-mode elapsed checking, pending ack consideration, clustered delete proposal with retry. .NET is a simple boolean property | | `consumer.watchGWinterest` | consumer.go:2141 | MISSING | — | Gateway interest polling timer | | `Account.checkNewConsumerConfig` | consumer.go:2274 | MISSING | — | 55-line validation of which config fields can be updated | | `consumer.updateConfig` | consumer.go:2333 | MISSING | — | 145-line config update handler (DeliverSubject, MaxAckPending, AckWait, RateLimit, SampleFrequency, MaxDeliver, InactiveThreshold, PauseUntil, FilterSubjects). Not ported | | `consumer.updateDeliverSubject` | consumer.go:2480 | MISSING | — | Push consumer delivery subject change | | `consumer.updateDeliverSubjectLocked` | consumer.go:2489 | MISSING | — | | | `configsEqualSansDelivery` | consumer.go:2506 | MISSING | — | Config comparison ignoring delivery subject | | `jsAckMsg` struct | consumer.go:2519 | MISSING | — | Ack message struct with pooling | | `jsAckMsgPool` | consumer.go:2526 | MISSING | — | sync.Pool for ack messages | | `consumer.pushAck` | consumer.go:2552 | MISSING | — | Wire handler that pushes to ackMsgs queue | | `consumer.processAck` | consumer.go:2558 | PORTED | `AckProcessor.ProcessAck(ulong, ReadOnlySpan)` | All ack types dispatched (+ACK, -NAK, +TERM, +WPI, +NXT). Missing: ackReplyInfo parsing from subject, ack reply handling | | `consumer.progressUpdate` | consumer.go:2604 | PORTED | `AckProcessor.ProcessProgress` | Resets deadline | | `consumer.updateSkipped` | consumer.go:2616 | MISSING | — | RAFT proposal for skip | | `consumer.resetStartingSeq` | consumer.go:2628 | PARTIAL | `ConsumerManager.ResetToSequence` | Simplified; missing deliver policy validation, RAFT proposal, interest stream cleanup | | `consumer.resetLocalStartingSeq` | consumer.go:2691 | PARTIAL | `ConsumerManager.ResetToSequence` | Clears pending and resets sequence | | `consumer.loopAndForwardProposals` | consumer.go:2700 | MISSING | — | RAFT proposal forwarding goroutine | | `consumer.propose` | consumer.go:2761 | MISSING | — | Add to proposal linked list | | `consumer.updateDelivered` | consumer.go:2778 | MISSING | — | RAFT-aware delivery tracking (proposes or stores locally) | | `consumer.addAckReply` | consumer.go:2799 | MISSING | — | Pending ack reply tracking for replicated consumers | | `consumer.addReplicatedQueuedMsg` | consumer.go:2808 | MISSING | — | Pending delivery tracking for replicated consumers | | `consumer.updateAcks` | consumer.go:2829 | MISSING | — | RAFT-aware ack update (proposes or stores locally) | | `consumer.addClusterPendingRequest` | consumer.go:2854 | PARTIAL | `PullConsumerEngine.ProposeWaitingRequest` | Simplified cluster pending tracking | | `consumer.removeClusterPendingRequest` | consumer.go:2866 | PARTIAL | `PullConsumerEngine.RemoveClusterPending` | | | `consumer.setPendingRequestsOk` | consumer.go:2877 | MISSING | — | Version-gated pending request support | | `consumer.checkAndSetPendingRequestsOk` | consumer.go:2890 | MISSING | — | Peer version checking | | `consumer.checkPendingRequests` | consumer.go:2915 | MISSING | — | Leadership change pending request notification | | `consumer.releaseAnyPendingRequests` | consumer.go:2931 | MISSING | — | Release waiting requests on delete/stop | | `consumer.processNak` | consumer.go:2950 | PORTED | `AckProcessor.ProcessNak` | Delay extraction, backoff, redelivery queue. Missing: NAK advisory sending, JSON delay parsing (`ConsumerNakOptions`), duration string parsing | | `consumer.processTerm` | consumer.go:3030 | PORTED | `AckProcessor.ProcessTerm` | Removes from pending. Missing: TERM advisory, reason extraction | | `ackWaitDelay` constant | consumer.go:3060 | MISSING | — | 1ms delay added to ack wait timer | | `consumer.ackWait` | consumer.go:3063 | MISSING | — | AckWait + delay calculation | | `consumer.checkRedelivered` | consumer.go:3072 | MISSING | — | Sanity check for rdc below ack floor | | `consumer.readStoredState` | consumer.go:3091 | MISSING | — | Restore state from store | | `consumer.applyState` | consumer.go:3107 | MISSING | — | Apply ConsumerState to consumer fields | | `consumer.setStoreState` | consumer.go:3135 | MISSING | — | Set state from snapshot restore | | `consumer.writeStoreState/Unlocked` | consumer.go:3147-3172 | MISSING | — | Persist state to store | | `consumer.initialInfo` | consumer.go:3176 | MISSING | — | One-shot initial info | | `consumer.clearInitialInfo` | consumer.go:3189 | MISSING | — | | | `consumer.info` | consumer.go:3196 | PARTIAL | `ConsumerManager.GetInfo` | .NET returns only config; Go returns full ConsumerInfo with delivery/ack state, pending counts, waiting count, cluster info, priority groups | | `consumer.infoWithSnap` | consumer.go:3200 | MISSING | — | | | `consumer.infoWithSnapAndReply` | consumer.go:3204 | MISSING | — | 120-line info builder with store state, cluster info | | `consumer.signalNewMessages` | consumer.go:3330 | PORTED | `PushConsumerEngine.Signal(ConsumerSignal.NewMessage)` | Channel-based signaling | | `consumer.shouldSample` | consumer.go:3339 | PORTED | `SampleTracker.ShouldSample` (Consumers/SampleTracker.cs) | Stochastic sampling | | `consumer.sampleAck` | consumer.go:3352 | PARTIAL | `SampleTracker.RecordLatency` | Records sample but does not send advisory | | `consumer.processAckMsg` | consumer.go:3382 | PARTIAL | `AckProcessor.AckSequence` / `AckProcessor.AckAll` | Core ack processing for Explicit/All/None policies. Missing: sample sending, cluster replication, retention policy notification, floor advancement algorithm differs | | `consumer.hasMaxDeliveries` | consumer.go:2172 | PORTED | `AckProcessor.ScheduleRedelivery` (max deliver check) | Checks and handles exceeded sequences | | `consumer.forceExpirePending` | consumer.go:2203 | MISSING | — | Force all pending to redeliver queue | | `consumer.deliveryCount` | referenced | MISSING | — | Per-sequence delivery count lookup | | `consumer.notifyDeliveryExceeded` | referenced at 2180 | MISSING | — | Max delivery exceeded advisory | | `consumer.setRateLimit` | consumer.go:2246 | PORTED | `TokenBucketRateLimiter` (Consumers/TokenBucketRateLimiter.cs) | Token bucket rate limiter. Go uses `golang.org/x/time/rate`; .NET has custom impl | | `consumer.setRateLimitNeedsLocks` | consumer.go:2228 | MISSING | — | Lock-safe rate limit update | | `ConsumerState` struct (store.go) | referenced | PORTED | `ConsumerState` (Storage/ConsumerState.cs) | All fields: Delivered, AckFloor, Pending, Redelivered | | `SequencePair` struct (store.go) | referenced | PORTED | `SequencePair` (Storage/ConsumerState.cs:8) | | | `Pending` struct (store.go) | referenced | PORTED | `Pending` (Storage/ConsumerState.cs:17) | | | `ConsumerStore` interface | referenced | PORTED | `IConsumerStore` (Storage/IConsumerStore.cs) | All methods: SetStarting, UpdateStarting, Reset, HasState, UpdateDelivered, UpdateAcks, Update, State, BorrowState, EncodedState, Type, Stop, Delete, StreamDelete | | `consumerFileStore` | referenced | PORTED | `ConsumerFileStore` (Storage/ConsumerFileStore.cs) | File-backed store with background flusher | | `encodeConsumerState/decodeConsumerState` | referenced | PORTED | `ConsumerStateCodec` (Storage/ConsumerStateCodec.cs) | Go-compatible binary codec with varint encoding | | Status | Count | | PORTED | 23 | | PARTIAL | 21 | | MISSING | 70 | | NOT_APPLICABLE | 1 | | **Total** | **115** | | `consumer.isFiltered()` | consumer.go:3524 | PARTIAL | `CompiledFilter` in PullConsumerEngine.cs | .NET has CompiledFilter but lacks stream-subject comparison logic (checks subjf vs mset.cfg.Subjects) | | `consumer.needAck()` | consumer.go:3571 | PARTIAL | `InterestRetentionPolicy.ShouldRetain()` | .NET covers interest retention case but missing full ack-policy switch (AckNone/AckAll/AckExplicit), filtered match, and leader/follower state borrow | | `consumer.isFilteredMatch()` | consumer.go:4490 | PORTED | `FilterSkipTracker.ShouldDeliver()`, `CompiledFilter.Matches()` | Both use SubjectMatch for token-based matching; Go has tokenized optimization | | `consumer.isEqualOrSubsetMatch()` | consumer.go:4515 | MISSING | — | Checks if filter subject is equal to or subset of consumer's filter subjects; used in config validation | | `PriorityGroup` (struct) | consumer.go:3635 | PORTED | `PriorityGroupManager` + `PullWaitingRequest.Priority` | Go struct fields (Group, MinPending, MinAckPending, Id, Priority) mapped across .NET types | | `consumer.setPinnedTimer()` | consumer.go:4000 | PARTIAL | `PriorityGroupManager.AssignPinId()` | .NET assigns pin ID but lacks TTL timer + unpinned advisory on timeout | | `consumer.assignNewPinId()` | consumer.go:4020 | PARTIAL | `PriorityGroupManager.AssignPinId()` | .NET assigns but missing pinnedTS tracking and pinned advisory send | | `consumer.unassignPinId()` | consumer.go:4032 | PORTED | `PriorityGroupManager.UnassignPinId()` | Timer stop + pin clear | | `waitingRequest` (struct) | consumer.go:3690 | PORTED | `PullWaitingRequest` + `PullRequest` | Represented as records; Go has linked list pointers, .NET uses separate queue | | `waitingDelivery` (struct) | consumer.go:3731 | MISSING | — | Tracks pending replicated deliveries (seq, pending msgs/bytes); needed for clustered pull consumers | | `waitQueue` (struct) | consumer.go:3753 | PORTED | `PullRequestWaitQueue` + `WaitingRequestQueue` | Two .NET implementations: priority-based and FIFO | | `newWaitQueue()` | consumer.go:3761 | PORTED | `PullRequestWaitQueue` constructor | | | `waitQueue.insertSorted()` | consumer.go:3771 | PORTED | `PullRequestWaitQueue.Enqueue()` | Stable priority insertion sort | | `waitQueue.addPrioritized()` | consumer.go:3783 | PORTED | `PullRequestWaitQueue.Enqueue()` | | | `waitQueue.add()` | consumer.go:3798 | PORTED | `WaitingRequestQueue.Enqueue()` | FIFO append | | `waitQueue.isFull()` | consumer.go:3821 | PORTED | `PullRequestWaitQueue` max size check | | | `waitQueue.isEmpty()` | consumer.go:3828 | PORTED | `WaitingRequestQueue.IsEmpty` | | | `waitQueue.len()` | consumer.go:3835 | PORTED | `WaitingRequestQueue.Count` | | | `waitQueue.peek()` | consumer.go:3843 | PORTED | `PullRequestWaitQueue.Peek()` | | | `waitQueue.cycle()` | consumer.go:3850 | MISSING | — | Removes head and re-adds to tail; used for round-robin cycling | | `waitQueue.popOrPopAndRequeue()` | consumer.go:3860 | PORTED | `PullRequestWaitQueue.PopAndRequeue()` | Dispatches based on priority policy | | `waitQueue.pop()` | consumer.go:3873 | PORTED | `PullRequestWaitQueue.Dequeue()` | | | `waitQueue.popAndRequeue()` | consumer.go:3892 | PORTED | `PullRequestWaitQueue.PopAndRequeue()` | | | `insertAtPosition()` | consumer.go:3925 | PORTED | (inline in PullRequestWaitQueue.Enqueue) | Priority insertion logic | | `waitQueue.removeCurrent()` | consumer.go:3959 | PORTED | (implicit in Dequeue) | | | `waitQueue.remove()` | consumer.go:3964 | PARTIAL | — | Go supports arbitrary removal from linked list; .NET queue doesn't have arbitrary mid-list removal | | `consumer.pendingRequests()` | consumer.go:3988 | PARTIAL | `PullConsumerEngine.GetClusterPendingRequests()` | Returns pending as reply-keyed map vs collection | | `consumer.nextWaiting()` | consumer.go:4044 | PARTIAL | `PullRequestWaitQueue.PopAndRequeue()` | .NET has basic pop; Go has full priority pin-check logic, max-bytes rejection, expiry, interest checking | | `nextReqFromMsg()` | consumer.go:3652 | PARTIAL | `PullFetchRequest` parsing in ConsumerApiHandlers | .NET parses batch from JSON; Go parses batch, maxBytes, noWait, expires, heartbeat, priorityGroup | | `nextMsgReq` (struct) | consumer.go:4186 | NOT_APPLICABLE | — | Pool-based message wrapper; .NET uses different request pipeline | | `consumer.processNextMsgReq()` | consumer.go:4219 | PARTIAL | `ConsumerApiHandlers.HandleNext()` | .NET has basic batch fetch; Go validates push vs pull, API level, and queues to ipQueue | | `consumer.processNextMsgRequest()` | consumer.go:4276 | PARTIAL | `PullConsumerEngine.FetchAsync()` | .NET handles basic fetch; Go handles maxRequestBatch/Expires/MaxBytes limits, priority group validation, noWait, interest tracking, queue add | | `consumer.processResetReq()` | consumer.go:4241 | PORTED | `ConsumerManager.ResetToSequence()` + `ConsumerApiHandlers.HandleReset()` | API handler + manager reset | | `trackDownAccountAndInterest()` | consumer.go:4419 | NOT_APPLICABLE | — | Account export response tracking for cross-account pull requests; not yet needed in .NET | | `consumer.deliveryCount()` | consumer.go:4439 | PORTED | `RedeliveryTracker._deliveryCounts` access | Via dictionary lookup | | `consumer.incDeliveryCount()` | consumer.go:4452 | PORTED | `RedeliveryTracker.IncrementDeliveryCount()` | | | `consumer.decDeliveryCount()` | consumer.go:4463 | MISSING | — | Adjusts delivery count down on failed delivery; not present in .NET tracker | | `consumer.notifyDeliveryExceeded()` | consumer.go:4471 | PARTIAL | `AckProcessor.ExceededSequences` + `DeliveryExceededPolicy` | .NET tracks exceeded sequences; Go sends advisory event via sendAdvisory | | `consumer.getNextMsg()` | consumer.go:4540 | PARTIAL | `PullConsumerEngine.FetchAsync()` | .NET handles basic store load + filter match; Go has full redelivery queue processing, max pending check, skip list, multi-filter LoadNextMsg/LoadNextMsgMulti | | `consumer.processWaiting()` | consumer.go:4645 | PARTIAL | `WaitingRequestQueue.RemoveExpired()` | .NET only removes expired; Go also checks interest, sends heartbeats, handles noWait EOS, replicated deliveries | | `consumer.checkWaitingForInterest()` | consumer.go:4745 | MISSING | — | Checks if any waiting requests still have interest | | `consumer.hbTimer()` | consumer.go:4751 | PORTED | `PushConsumerEngine.StartIdleHeartbeatTimer()` | | | `consumer.checkAckFloor()` | consumer.go:4762 | MISSING | — | Corrects ack floor drift when stream FirstSeq advances past consumer asflr; processes stale pending entries | | `consumer.processInboundAcks()` | consumer.go:4854 | PARTIAL | `AckProcessor.ProcessAck()` is synchronous | Go runs as dedicated goroutine with ack floor drift ticker; .NET processes inline | | `consumer.processInboundNextMsgReqs()` | consumer.go:4902 | PARTIAL | `ConsumerApiHandlers.HandleNext()` | Go runs as dedicated goroutine draining ipQueue; .NET handles synchronously per API call | | `consumer.suppressDeletion()` | consumer.go:4926 | MISSING | — | Resets inactivity timer on ack activity to prevent premature ephemeral deletion | | `consumer.loopAndGatherMsgs()` | consumer.go:4948 | PARTIAL | `PushConsumerEngine.LoopAndGatherMsgsAsync()` | .NET has basic gather loop; Go has full pause check, push active check, pull wait check, replay delay, rate limit, flow control, heartbeat timer | | `consumer.sendIdleHeartbeat()` | consumer.go:5222 | PORTED | `PushConsumerEngine.SendIdleHeartbeatCallback()` | Includes stall header and pending counts | | `consumer.ackReply()` | consumer.go:5234 | MISSING | — | Formats `$JS.ACK.{stream}.{consumer}.{dc}.{sseq}.{dseq}.{ts}.{pending}` reply subject | | `consumer.setMaxPendingBytes()` | consumer.go:5239 | MISSING | — | Sets flow control byte limits; used in testing | | `consumer.checkNumPending()` | consumer.go:5252 | MISSING | — | Sanity-checks cached num pending against store state | | `consumer.numPending()` | consumer.go:5271 | MISSING | — | Returns cached pending count (npc clamped to 0) | | `consumer.checkNumPendingOnEOF()` | consumer.go:5281 | MISSING | — | Resets npc/npf when consumer has caught up to stream end | | `consumer.streamNumPendingLocked()` | consumer.go:5294 | MISSING | — | Acquires lock and delegates to streamNumPending | | `consumer.streamNumPending()` | consumer.go:5303 | MISSING | — | Forces recalculation of num pending from store | | `consumer.calculateNumPending()` | consumer.go:5319 | MISSING | — | Calculates num pending using NumPending/NumPendingMulti with filter awareness | | `convertToHeadersOnly()` | consumer.go:5336 | MISSING | — | Strips msg payload and replaces with Nats-Msg-Size header for HeadersOnly consumers | | `consumer.deliverMsg()` | consumer.go:5364 | PARTIAL | `PushConsumerEngine.Enqueue()` + `RunDeliveryLoopAsync()` | .NET enqueues frames and sends via loop; Go handles dseq tracking, pending tracking, flow control, replicated delivery, rate limiting, interest ack for AckNone | | `consumer.replicateDeliveries()` | consumer.go:5428 | NOT_APPLICABLE | — | Cluster-specific: checks if deliveries must be replicated before sending | | `consumer.needFlowControl()` | consumer.go:5432 | PARTIAL | `PushConsumerEngine.IsFlowControlStalled` | .NET tracks pending FC count; Go tracks pbytes vs maxpb threshold | | `consumer.processFlowControl()` | consumer.go:5448 | PARTIAL | `PushConsumerEngine.AcknowledgeFlowControl()` | .NET decrements count; Go also doubles maxpb (slow start ramp), updates pbytes accounting | | `consumer.fcReply()` | consumer.go:5476 | MISSING | — | Generates unique flow control reply subject: `_FCR_.{stream}.{consumer}.{rand4}` | | `consumer.sendFlowControl()` | consumer.go:5495 | PARTIAL | FC frame in `PushConsumerEngine.RunDeliveryLoopAsync()` | .NET sends FC frame; Go also tracks fcsz/fcid state | | `consumer.trackPending()` | consumer.go:5507 | PARTIAL | `AckProcessor.Register()` | .NET registers with ackWaitMs; Go also handles backoff array indexing and ptmr reset | | `consumer.creditWaitingRequest()` | consumer.go:5541 | MISSING | — | Credits back a failed delivery in the wait queue (n++, d--) | | `consumer.didNotDeliver()` | consumer.go:5554 | MISSING | — | Handles failed delivery: decrements delivery count, checks push/pull mode, queues redelivery, checks delivery interest | | `consumer.addToRedeliverQueue()` | consumer.go:5595 | PORTED | `RedeliveryTracker.Schedule()` | .NET uses PriorityQueue; Go uses rdq slice + rdqi set | | `consumer.hasRedeliveries()` | consumer.go:5603 | PORTED | `RedeliveryTracker.GetDue()` returns non-empty | | | `consumer.getNextToRedeliver()` | consumer.go:5607 | PORTED | `RedeliveryTracker.GetDue()` enumeration | | | `consumer.onRedeliverQueue()` | consumer.go:5625 | PORTED | `RedeliveryTracker.IsTracking()` (via _deliveryCounts) | | | `consumer.removeFromRedeliverQueue()` | consumer.go:5631 | PORTED | `RedeliveryTracker.Acknowledge()` | Removes from entries and delivery counts | | `consumer.checkPending()` | consumer.go:5651 | PARTIAL | `AckProcessor.TryGetExpired()` | .NET checks one expired at a time; Go scans all pending with backoff, checks awl interlock, removes stale below fseq/asflr, sorts expired, adjusts timestamps | | `consumer.seqFromReply()` | consumer.go:5770 | MISSING | — | Extracts delivery sequence from ack reply subject | | `consumer.streamSeqFromReply()` | consumer.go:5776 | MISSING | — | Extracts stream sequence from ack reply subject | | `parseAckReplyNum()` | consumer.go:5782 | MISSING | — | Fast ASCII digit parser for ack reply tokens | | `replyInfo()` | consumer.go:5798 | MISSING | — | Parses all 5 fields from `$JS.ACK.{stream}.{consumer}.{dc}.{sseq}.{dseq}.{ts}.{pending}` | | `ackReplyInfo()` | consumer.go:5821 | MISSING | — | Parses sseq, dseq, dc from ack reply subject | | `lastSeqSkipList` (struct) | consumer.go:5849 | MISSING | — | Holds skip list for DeliverLastPerSubject startup | | `consumer.hasSkipListPending()` | consumer.go:5856 | MISSING | — | Checks if skip list has pending entries | | `consumer.selectStartingSeqNo()` | consumer.go:5861 | PARTIAL | `PullConsumerEngine.ResolveInitialSequenceAsync()` | .NET handles DeliverAll/Last/New/ByStartSeq/ByStartTime/LastPerSubject; Go also handles filtered subjects for DeliverLast, MaxMsgsPer optimization for LastPerSubject, MultiLastSeqs, time-based filtered skip-ahead, empty/future/past clamping | | `consumer.nextSeq()` | consumer.go:5841 | PORTED | `ConsumerHandle.NextSequence` property | | | `isDurableConsumer()` | consumer.go:5969 | PORTED | `ConsumerConfig.Ephemeral` flag (inverse) | | | `consumer.isDurable()` | consumer.go:5973 | PORTED | `!config.Ephemeral` | | | `consumer.isPushMode()` | consumer.go:5978 | PORTED | `ConsumerConfig.Push` property | | | `consumer.isPullMode()` | consumer.go:5982 | PORTED | `!config.Push` | | | `consumer.String()` | consumer.go:5987 | NOT_APPLICABLE | — | Trivial name getter | | `createConsumerName()` | consumer.go:5994 | PORTED | `Guid.NewGuid()` in ConsumerManager | Different generation approach but same purpose | | `stream.deleteConsumer()` | consumer.go:5999 | PORTED | `ConsumerManager.Delete()` | | | `consumer.getStream()` | consumer.go:6003 | PORTED | `ConsumerHandle.Stream` property | | | `consumer.streamName()` | consumer.go:6010 | PORTED | `ConsumerHandle.Stream` property | | | `consumer.isActive()` | consumer.go:6021 | PARTIAL | `ConsumerHandle.Paused` (inverse) | .NET uses Paused; Go checks active flag + mset != nil | | `consumer.hasNoLocalInterest()` | consumer.go:6029 | PARTIAL | `DeliveryInterestTracker.HasInterest` (inverse) | .NET tracks subscriber count; Go checks sublist for delivery subject | | `consumer.purge()` | consumer.go:6040 | MISSING | — | Called when parent stream is purged; adjusts sseq/asflr, clears stale pending, filters wider purge, updates store state | | `stopAndClearTimer()` | consumer.go:6124 | NOT_APPLICABLE | — | Go timer helper; .NET uses Timer.Dispose() | | `consumer.stop()` | consumer.go:6135 | PARTIAL | `PushConsumerEngine.StopDeliveryLoop()` + `StopGatherLoop()` | .NET stops loops; Go has full stopWithFlags with advisory, cleanup, subscription unsubscribe | | `consumer.deleteWithoutAdvisory()` | consumer.go:6139 | MISSING | — | Deletes without sending advisory | | `consumer.delete()` | consumer.go:6144 | PORTED | `ConsumerManager.Delete()` | | | `consumer.isClosed()` | consumer.go:6149 | MISSING | — | Checks closed flag under lock | | `consumer.stopWithFlags()` | consumer.go:6155 | MISSING | — | Full lifecycle shutdown: cluster assignment check, advisory, subscription cleanup, client close, interest clear, store delete/stop, RAFT node stop, directory cleanup | | `consumer.cleanupNoInterestMessages()` | consumer.go:6334 | MISSING | — | Removes messages with no remaining consumer interest; used on interest-policy stream consumer deletion | | `deliveryFormsCycle()` | consumer.go:6400 | MISSING | — | Validates that delivery subject doesn't create a cycle with stream subjects | | `consumer.switchToEphemeral()` | consumer.go:6410 | MISSING | — | Converts durable to ephemeral on startup recovery; clears Durable, updates inactive threshold | | `consumer.requestNextMsgSubject()` | consumer.go:6430 | MISSING | — | Returns the subject for next-message pull requests | | `consumer.decStreamPending()` | consumer.go:6434 | MISSING | — | Decrements cached num pending on message deletion; processes pending term if message was pending | | `consumer.account()` | consumer.go:6459 | NOT_APPLICABLE | — | Account getter | | `consumer.signalSubs()` | consumer.go:6468 | MISSING | — | Creates sublist filter subjects for stream signal registration | | `consumer.processStreamSignal()` | consumer.go:6494 | PARTIAL | `PushConsumerEngine.Signal(ConsumerSignal.NewMessage)` | .NET signals via channel; Go checks leader, updates npc, checks push active / pull waiting | | `subjectSliceEqual()` | consumer.go:6517 | NOT_APPLICABLE | — | Utility set equality; trivial | | `gatherSubjectFilters()` | consumer.go:6536 | NOT_APPLICABLE | — | Utility to collect filter strings; trivial | | `consumer.shouldStartMonitor()` | consumer.go:6546 | MISSING | — | Atomically checks/sets monitor running flag with WaitGroup | | `consumer.clearMonitorRunning()` | consumer.go:6560 | MISSING | — | Clears monitor running flag and decrements WaitGroup | | `consumer.isMonitorRunning()` | consumer.go:6571 | MISSING | — | Checks monitor running flag | | `consumer.checkStateForInterestStream()` | consumer.go:6582 | MISSING | — | For interest/WQ streams: walks sequences from FirstSeq to ack floor, acks already-consumed messages; handles pending above floor; adjusts chkflr | | `consumer.resetPtmr()` | consumer.go:6692 | PARTIAL | Timer in `AckProcessor.TryGetExpired()` loop | Go uses AfterFunc timer; .NET checks expiry on poll | | `consumer.stopAndClearPtmr()` | consumer.go:6701 | PARTIAL | — | Go stops timer and clears; .NET has no dedicated pending timer | | `consumer.resetPendingDeliveries()` | consumer.go:6706 | MISSING | — | Recycles pending and waiting delivery pool entries; clustered delivery cleanup | | Status | Count | | PORTED | 31 | | PARTIAL | 28 | | MISSING | 37 | | NOT_APPLICABLE | 9 | | DEFERRED | 0 | | **Total** | **105** | | `memStore` struct | memstore.go:33 | PARTIAL | `MemStore` class in MemStore.cs:16 | .NET uses `Dictionary` instead of `stree.SubjectTree[SimpleState]`; no `scb`/`rmcb`/`pmsgcb` callbacks; no `ageChk` timer; no `scheduling`; no `sdm` | | `newMemStore` | memstore.go:54 | PORTED | `MemStore(StreamConfig)` constructor, MemStore.cs:111 | TTL init present; FirstSeq handled; no `ats.Register()` | | `UpdateConfig` | memstore.go:86 | PARTIAL | `IStreamStore.UpdateConfig`, MemStore.cs:799 | TTL create/destroy ported; per-subject limit enforcement ported; missing: age timer start/stop, `enforceMsgLimit`/`enforceBytesLimit` on config change, scheduling support | | `recoverTTLState` | memstore.go:147 | MISSING | -- | TTL state recovery from existing messages not implemented | | `recoverMsgSchedulingState` | memstore.go:171 | MISSING | -- | Message scheduling not implemented | | `storeRawMsg` (internal) | memstore.go:195 | PARTIAL | `StoreInternal`, MemStore.cs:872 | Core store logic ported (seq check, per-subject tracking, msg/bytes limits); missing: `DiscardNew` policy checks, `DiscardNewPer` check, TTL tracking in THW, age timer management, scheduling support | | `StoreRawMsg` | memstore.go:329 | PARTIAL | `IStreamStore.StoreRawMsg`, MemStore.cs:299 | Delegates to StoreInternal; missing: `scb` callback, `receivedAny` first-message age check | | `StoreMsg` | memstore.go:350 | PORTED | `IStreamStore.StoreMsg`, MemStore.cs:287 | Core logic matches Go | | `SkipMsg` | memstore.go:368 | PORTED | `IStreamStore.SkipMsg`, MemStore.cs:308 | Sequence check and dmap handling present | | `SkipMsgs` | memstore.go:395 | PORTED | `IStreamStore.SkipMsgs`, MemStore.cs:332 | Multi-skip ported | | `FlushAllPending` | memstore.go:424 | PORTED | `IStreamStore.FlushAllPending`, MemStore.cs:358 | No-op in both | | `RegisterStorageUpdates` | memstore.go:431 | MISSING | -- | Storage update callback not implemented | | `RegisterStorageRemoveMsg` | memstore.go:439 | MISSING | -- | Removal callback not implemented | | `RegisterProcessJetStreamMsg` | memstore.go:446 | MISSING | -- | JetStream msg processing callback not implemented | | `GetSeqFromTime` | memstore.go:454 | PORTED | `IStreamStore.GetSeqFromTime`, MemStore.cs:571 | Binary search with gap awareness ported | | `FilteredState` | memstore.go:531 | PORTED | `IStreamStore.FilteredState`, MemStore.cs:639 | Delegates to internal method | | `filteredStateLocked` | memstore.go:540 | PARTIAL | `FilteredStateLocked`, MemStore.cs (internal) | Core matching ported; lacks `lastPerSubject` partial-scan optimization, `firstNeedsUpdate`/`lastNeedsUpdate` lazy recalculation | | `SubjectsState` | memstore.go:748 | PORTED | `IStreamStore.SubjectsState`, MemStore.cs:643 | Implemented with Dictionary instead of SubjectTree | | `AllLastSeqs` | memstore.go:780 | PORTED | `IStreamStore.AllLastSeqs`, MemStore.cs:679 | Sorted last sequences | | `filterIsAll` | memstore.go:811 | MISSING | -- | Helper to check if filters == stream subjects | | `MultiLastSeqs` | memstore.go:828 | PORTED | `IStreamStore.MultiLastSeqs`, MemStore.cs:691 | Matching logic ported | | `SubjectsTotals` | memstore.go:881 | PORTED | `IStreamStore.SubjectsTotals`, MemStore.cs:664 | Per-subject counts ported | | `NumPending` | memstore.go:913 | PORTED | `IStreamStore.NumPending`, MemStore.cs:747 | Delegates to filteredStateLocked | | `NumPendingMulti` | memstore.go:923 | MISSING | -- | Multi-subject sublist-based pending count not implemented | | `enforcePerSubjectLimit` | memstore.go:1072 | PORTED | `EnforcePerSubjectLimit`, MemStore.cs (internal) | Removes oldest per-subject messages | | `enforceMsgLimit` | memstore.go:1088 | PARTIAL | Inline in `StoreInternal` | Logic present but not separated into a named method; `DiscardOld` policy not checked | | `enforceBytesLimit` | memstore.go:1102 | PARTIAL | Inline in `StoreInternal` | Similar to enforceMsgLimit | | `startAgeChk` | memstore.go:1116 | MISSING | -- | Age check timer not implemented | | `resetAgeChk` | memstore.go:1126 | MISSING | -- | Age check timer reset not implemented | | `cancelAgeChk` | memstore.go:1194 | MISSING | -- | Age check cancellation not implemented | | `expireMsgs` | memstore.go:1203 | MISSING | -- | Full MaxAge + TTL expiration loop not implemented | | `shouldProcessSdm` | memstore.go:1322 | MISSING | -- | Subject delete marker tracking not implemented | | `shouldProcessSdmLocked` | memstore.go:1329 | MISSING | -- | SDM tracking internals | | `handleRemovalOrSdm` | memstore.go:1362 | MISSING | -- | SDM/removal handler | | `runMsgScheduling` | memstore.go:1383 | MISSING | -- | Message scheduling not implemented | | `PurgeEx` | memstore.go:1422 | PORTED | `IStreamStore.PurgeEx`, MemStore.cs:481 | Subject-specific + keep + seq purge ported | | `Purge` | memstore.go:1471 | PORTED | `IStreamStore.Purge`, MemStore.cs:472 | Full purge ported | | `purge` (internal) | memstore.go:1475 | PORTED | `PurgeInternal`, MemStore.cs | Resets msgs/fss/dmap | | `Compact` | memstore.go:1509 | PORTED | `IStreamStore.Compact`, MemStore.cs:533 | Compaction ported | | `compact` (internal) | memstore.go:1513 | PORTED | `CompactInternal`, MemStore.cs | Removes messages before seq | | `reset` | memstore.go:1583 | PORTED | `IStreamStore.Truncate(0)`, MemStore.cs:540 | Full reset handled via Truncate(0) | | `Truncate` | memstore.go:1618 | PORTED | `IStreamStore.Truncate`, MemStore.cs:536 | Removes messages after seq | | `SubjectForSeq` | memstore.go:1678 | PORTED | `IStreamStore.SubjectForSeq`, MemStore.cs:736 | | | `LoadMsg` | memstore.go:1692 | PORTED | `IStreamStore.LoadMsg`, MemStore.cs:361 | | | `loadMsgLocked` | memstore.go:1697 | PORTED | Inline in LoadMsg | Lock parameter not needed (.NET uses `lock(_gate)`) | | `LoadLastMsg` | memstore.go:1724 | PORTED | `IStreamStore.LoadLastMsg`, MemStore.cs:393 | Wildcard + literal + fwcs paths | | `loadLastLocked` | memstore.go:1733 | PORTED | Inline in LoadLastMsg | | | `LoadNextMsgMulti` | memstore.go:1763 | MISSING | -- | Multi-subject sublist-based next message not implemented | | `LoadNextMsg` | memstore.go:1798 | PARTIAL | `IStreamStore.LoadNextMsg`, MemStore.cs:372 | Linear scan only; missing `shouldLinearScan` optimization, `nextWildcardMatchLocked`, `nextLiteralMatchLocked` | | `nextWildcardMatchLocked` | memstore.go:1810 | MISSING | -- | FSS-based wildcard bounds optimization | | `nextLiteralMatchLocked` | memstore.go:1852 | MISSING | -- | FSS-based literal bounds optimization | | `shouldLinearScan` | memstore.go:1868 | MISSING | -- | Linear scan decision helper | | `loadNextMsgLocked` | memstore.go:1877 | PARTIAL | Inline in LoadNextMsg | Core linear scan ported; FSS-based optimizations missing | | `LoadPrevMsg` | memstore.go:1925 | PORTED | `IStreamStore.LoadPrevMsg`, MemStore.cs:439 | Backward walk ported | | `LoadPrevMsgMulti` | memstore.go:1952 | MISSING | -- | Multi-subject sublist-based prev message not implemented | | `RemoveMsg` | memstore.go:1987 | PORTED | `IStreamStore.RemoveMsg`, MemStore.cs:454 | | | `EraseMsg` | memstore.go:1995 | PARTIAL | `IStreamStore.EraseMsg`, MemStore.cs:463 | Does not overwrite with random bytes (secure erase) | | `updateFirstSeq` | memstore.go:2004 | PORTED | Inline in `RemoveInternal` | Updates first seq on removal | | `removeSeqPerSubject` | memstore.go:2037 | PORTED | `RemoveSeqPerSubject`, MemStore.cs | Per-subject tracking update on remove | | `recalculateForSubj` | memstore.go:2070 | MISSING | -- | Lazy first/last recalculation for per-subject state (uses `firstNeedsUpdate`/`lastNeedsUpdate` flags) | | `removeMsg` (internal) | memstore.go:2110 | PARTIAL | `RemoveInternal`, MemStore.cs | Core removal ported; missing: TTL removal from THW, secure erase with random bytes, `scb` callback | | `deleteFirstMsgOrPanic` | memstore.go:1667 | PORTED | Inline in limit enforcement | | | `deleteFirstMsg` | memstore.go:1673 | PORTED | Inline in limit enforcement | | | `Type` | memstore.go:2167 | PORTED | `IStreamStore.Type`, MemStore.cs:797 | Returns `StorageType.Memory` | | `FastState` | memstore.go:2173 | PORTED | `IStreamStore.FastState`, MemStore.cs:781 | Populates ref struct | | `State` | memstore.go:2192 | PORTED | `IStreamStore.State`, MemStore.cs:757 | Full state with deleted list | | `Utilization` | memstore.go:2221 | MISSING | -- | Returns (total, reported) byte counts | | `memStoreMsgSize` | memstore.go:2231 | PORTED | `MsgSize`, MemStore.cs (internal) | Size calculation for accounting | | `memStoreMsgSizeRaw` | memstore.go:2227 | PORTED | Inline in MsgSize | | | `ResetState` | memstore.go:2236 | PORTED | `IStreamStore.ResetState`, MemStore.cs:846 | No-op (scheduling not implemented) | | `Delete` | memstore.go:2245 | PORTED | `IStreamStore.Delete`, MemStore.cs:831 | | | `Stop` | memstore.go:2249 | PORTED | `IStreamStore.Stop`, MemStore.cs:828 | | | `isClosed` | memstore.go:2272 | MISSING | -- | Closed check (msgs == nil) | | `EncodedStreamState` | memstore.go:2322 | PARTIAL | `IStreamStore.EncodedStreamState`, MemStore.cs:849 | Returns empty array stub — binary encoding not implemented | | `SyncDeleted` | memstore.go:2357 | MISSING | -- | Delete block synchronization for NRG not implemented | | `consumerMemStore` struct | memstore.go:2278 | MISSING | -- | In-memory consumer state store not implemented as a distinct type | | `ConsumerStore` (factory) | memstore.go:2286 | MISSING | -- | Consumer store creation stub; returns `NotSupportedException` | | `AddConsumer` | memstore.go:2301 | MISSING | -- | Consumer count tracking | | `RemoveConsumer` | memstore.go:2308 | MISSING | -- | Consumer count tracking | | `Snapshot` | memstore.go:2317 | NOT_APPLICABLE | -- | Returns "no impl" in Go too | | `consumerMemStore.Update` | memstore.go:2382 | MISSING | -- | Consumer state update | | `consumerMemStore.SetStarting` | memstore.go:2428 | MISSING | -- | Set starting sequence | | `consumerMemStore.UpdateStarting` | memstore.go:2437 | MISSING | -- | Update starting sequence | | `consumerMemStore.Reset` | memstore.go:2451 | MISSING | -- | Reset consumer state | | `consumerMemStore.HasState` | memstore.go:2459 | MISSING | -- | Check if state exists | | `consumerMemStore.UpdateDelivered` | memstore.go:2466 | MISSING | -- | Update delivery tracking | | `consumerMemStore.UpdateAcks` | memstore.go:2532 | MISSING | -- | Ack processing (AckAll / AckExplicit) | | `consumerMemStore.UpdateConfig` | memstore.go:2606 | MISSING | -- | Update consumer config | | `consumerMemStore.Stop` | memstore.go:2615 | MISSING | -- | Stop consumer store | | `consumerMemStore.Delete` | memstore.go:2624 | MISSING | -- | Delete consumer store | | `consumerMemStore.StreamDelete` | memstore.go:2628 | MISSING | -- | Stream-level delete | | `consumerMemStore.State` | memstore.go:2632 | MISSING | -- | Get consumer state | | `consumerMemStore.BorrowState` | memstore.go:2638 | MISSING | -- | Borrow state (no copy) | | `consumerMemStore.EncodedState` | memstore.go:2672 | MISSING | -- | Binary-encode consumer state | | `consumerMemStore.Type` | memstore.go:2700 | MISSING | -- | Returns MemoryStorage | | `FileStoreConfig` struct | filestore.go:55 | PORTED | `FileStoreConfig` class, FileStoreConfig.cs:12 | All fields ported (StoreDir, BlockSize, CacheExpire, SubjectStateExpire, SyncInterval, SyncAlways, AsyncFlush, Cipher, Compression); missing: `srv` internal reference | | `FileStreamInfo` | filestore.go:80 | MISSING | -- | Created + StreamConfig wrapper not defined | | `StoreCipher` enum | filestore.go:85 | PORTED | `StoreCipher` enum, AeadEncryptor.cs:22 | ChaCha, AES, NoCipher | | `StoreCompression` enum | filestore.go:106 | PORTED | `StoreCompression` enum, AeadEncryptor.cs:39 | NoCompression, S2Compression | | `StoreCompression.String/MarshalJSON/UnmarshalJSON` | filestore.go:113-151 | NOT_APPLICABLE | -- | .NET enums have built-in string conversion; JSON handled by System.Text.Json | | `FileConsumerInfo` | filestore.go:154 | MISSING | -- | Consumer info with Created + Name + Config | | `psi` struct | filestore.go:166 | MISSING | -- | Per-subject index (total, fblk, lblk) for PSIM tree | | `fileStore` struct | filestore.go:172 | PARTIAL | `FileStore` class, FileStore.cs:23 | .NET uses in-memory Dictionary instead of block-based PSIM; missing: `psim` SubjectTree, `bim` block index map, `tombs`, `ld`, callbacks (`scb`/`rmcb`/`pmsgcb`), age timer, sync timer, `prf`/`aek` encryption keys, `hh` highway hash, `qch`/`fsld` channels, async flush state | | `msgBlock` struct | filestore.go:217 | PARTIAL | `MsgBlock` class, MsgBlock.cs:24 | Basic block file + index + delete tracking ported; missing: encryption (`aek`/`bek`/`seed`/`nonce`), compression (`cmp`), FSS subject tree, cache expiry timer (`ctmr`), write-error tracking (`werr`), `fch`/`qch` channels, loading/flusher flags, compact/sync flags | | `cache` struct | filestore.go:270 | PARTIAL | `_cache` Dictionary in MsgBlock.cs:49 | .NET uses Dictionary; Go uses byte-buffer + index array | | `msgId` struct | filestore.go:278 | MISSING | -- | Sequence+timestamp pair | | Constants (magic, version, dirs, sizes) | filestore.go:283-377 | PARTIAL | -- | Some constants like block sizes defined differently in `FileStoreOptions`; most Go constants (magic bytes, dir names, scan patterns, thresholds) not ported | | `newFileStore` | filestore.go:379 | PARTIAL | `FileStore` constructor, FileStore.cs | .NET creates directory + first block; missing: `dynBlkSize`, encryption key setup, highway hash init, full recovery pipeline | | `newFileStoreWithCreated` | filestore.go:383 | PARTIAL | (merged into constructor) | Core directory setup present; missing: `prf`/`oldprf` key gen, AEK recovery, full state recovery, TTL/scheduling recovery, tombstone processing, meta file write, sync timer, flush state loop | | `lockAllMsgBlocks`/`unlockAllMsgBlocks` | filestore.go:641-653 | NOT_APPLICABLE | -- | .NET uses different concurrency model | | `UpdateConfig` | filestore.go:655 | PARTIAL | `IStreamStore.UpdateConfig` in FileStore.cs | Basic config update present; missing: meta file write, TTL/scheduling, limits enforcement, async flush toggle | | `dynBlkSize` | filestore.go:763 | MISSING | -- | Dynamic block size calculation based on retention/maxBytes/encryption | | `genEncryptionKey` | filestore.go:800 | PORTED | `AeadEncryptor.Encrypt/Decrypt`, AeadEncryptor.cs:73 | ChaCha20-Poly1305 and AES-GCM both supported | | `genEncryptionKeys` | filestore.go:816 | MISSING | -- | Key encryption key generation with PRF not ported | | `genBlockEncryptionKey` | filestore.go:864 | MISSING | -- | Block-level stream cipher (ChaCha20/AES-CTR) not ported | | `recoverAEK` | filestore.go:878 | MISSING | -- | AEK recovery from key file | | `setupAEK` | filestore.go:907 | MISSING | -- | AEK initial setup | | `writeStreamMeta` | filestore.go:929 | MISSING | -- | Stream metadata + checksum write | | `loadEncryptionForMsgBlock` | filestore.go:1078 | MISSING | -- | Per-block encryption key loading | | `convertCipher` | filestore.go:1318 | MISSING | -- | Cipher conversion between ChaCha/AES | | `convertToEncrypted` | filestore.go:1407 | MISSING | -- | Plaintext to encrypted block conversion | | `getMsgBlockBuf`/`recycleMsgBlockBuf` | filestore.go:996-1032 | MISSING | -- | Pooled block buffers (Go sync.Pool); .NET uses GC | | `msgHdrSize`/`checksumSize`/`emptyRecordLen` | filestore.go:1034-1038 | PARTIAL | `MessageRecord.Encode/Decode` in MessageRecord.cs | .NET uses its own binary format (not identical to Go's 22-byte header) | | `noTrackSubjects` | filestore.go:1041 | MISSING | -- | Helper to check if subject tracking is needed | | `initMsgBlock` | filestore.go:1046 | PARTIAL | `MsgBlock.Create`, MsgBlock.cs:174 | Basic block init; missing highway hash, cache/sync config | | `checkAndLoadEncryption` | filestore.go:1068 | MISSING | -- | Encryption check on block load | | `ensureLastChecksumLoaded` | filestore.go:1139 | MISSING | -- | Lazy last checksum load | | `recoverMsgBlock` | filestore.go:1148 | PARTIAL | `MsgBlock.Recover`, MsgBlock.cs:188 | Basic file scan + index rebuild present; missing: encryption handling, index file check, FSS population, lost data tracking | | `lostData`/`addLostData`/`removeFromLostData` | filestore.go:1224-1275 | MISSING | -- | Lost data tracking not implemented | | `rebuildState`/`rebuildStateLocked` | filestore.go:1277-1315 | PARTIAL | -- | FileStore does a simplified rebuild; Go's full block-by-block state aggregation not ported | | `rebuildStateLocked` (msgBlock) | filestore.go:1454 | MISSING | -- | Per-block state rebuild from raw buffer | | `rebuildStateFromBufLocked` | filestore.go:1493 | MISSING | -- | Detailed record-by-record scan with tombstone/ebit/gap detection | | `warn`/`debug` (logging) | filestore.go:1708-1724 | NOT_APPLICABLE | -- | .NET uses ILogger | | `updateTrackingState` | filestore.go:1727 | MISSING | -- | Helper to aggregate per-block state | | `trackingStatesEqual` | filestore.go:1743 | MISSING | -- | State consistency check | | `recoverFullState` | filestore.go:1754 | MISSING | -- | Full state recovery from index.db | | `recoverTTLState` | filestore.go:2042 | MISSING | -- | TTL hash wheel recovery from thw.db | | `recoverMsgSchedulingState` | filestore.go:2123 | MISSING | -- | Message scheduling recovery from sched.db | | `lastChecksum` (msgBlock) | filestore.go:2204 | PARTIAL | `MsgBlock.LastChecksum`, MsgBlock.cs:155 | Checksum tracked on write; missing: load from encrypted block | | `cleanupOldMeta` | filestore.go:2236 | MISSING | -- | Remove legacy .idx/.fss files | | `recoverMsgs` | filestore.go:2263 | PARTIAL | Recovery in FileStore constructor | Basic block scan + re-open present; missing: per-block first/last propagation, tombstone handling, orphan key cleanup | | `expireMsgsOnRecover` | filestore.go:2401 | MISSING | -- | Startup age expiration | | `copyMsgBlocks` | filestore.go:2589 | NOT_APPLICABLE | -- | Go slice copy helper | | `GetSeqFromTime` (fs) | filestore.go:2600 | PARTIAL | In FileStore.cs | Basic implementation present; missing: block selection by timestamp, binary search across blocks | | `firstMatchingMulti` (mb) | filestore.go:2666 | MISSING | -- | Block-level multi-subject first match | | `firstMatching` (mb) | filestore.go:2806 | MISSING | -- | Block-level first matching with FSS optimization | | `prevMatchingMulti` (mb) | filestore.go:2948 | MISSING | -- | Block-level reverse multi-subject match | | `filteredPending` / `filteredPendingLocked` (mb) | filestore.go:3062-3188 | MISSING | -- | Per-block filtered pending count | | `FilteredState` (fs) | filestore.go:3191 | PARTIAL | In FileStore.cs | Simplified version; missing: per-block aggregation with FSS | | `checkSkipFirstBlock` | filestore.go:3241 | MISSING | -- | Block skip optimization using PSIM | | `checkSkipFirstBlockMulti` | filestore.go:3269 | MISSING | -- | Multi-subject block skip | | `selectSkipFirstBlock` | filestore.go:3287 | MISSING | -- | Block index selection helper | | `numFilteredPending` / `numFilteredPendingNoLast` / `numFilteredPendingWithLast` | filestore.go:3308-3410 | MISSING | -- | Optimized filtered pending using PSIM | | `SubjectsState` (fs) | filestore.go:3413 | PARTIAL | In FileStore.cs | In-memory implementation; missing: per-block FSS aggregation | | `AllLastSeqs` / `allLastSeqsLocked` (fs) | filestore.go:3495-3553 | PARTIAL | In FileStore.cs | In-memory implementation; missing: reverse block walk with FSS | | `filterIsAll` (fs) | filestore.go:3558 | MISSING | -- | Filter optimization helper | | `MultiLastSeqs` (fs) | filestore.go:3575 | PARTIAL | In FileStore.cs | In-memory implementation; missing: PSIM-based optimization, per-block walk | | `NumPending` (fs) | filestore.go:3710 | PARTIAL | In FileStore.cs | Simplified version; missing: PSIM optimization, block-level aggregation | | Status | MemStore | FileStore (1-4000) | Total | | PORTED | 33 | 4 | 37 | | PARTIAL | 11 | 16 | 27 | | MISSING | 36 | 35 | 71 | | NOT_APPLICABLE | 1 | 4 | 5 | | DEFERRED | 0 | 0 | 0 | | **Total** | **81** | **59** | **140** | | `fs.NumPendingMulti` | filestore.go:4051 | MISSING | -- | Multi-filter NumPending using SimpleSublist; consumer optimization path | | `fs.SubjectsTotals` | filestore.go:4387 | PORTED | `FileStore.SubjectsTotals` | .NET iterates _messages dict; Go uses psim trie Match | | `fs.subjectsTotalsLocked` | filestore.go:4394 | PORTED | (inlined in SubjectsTotals) | Internal locked helper | | `fs.RegisterStorageUpdates` | filestore.go:4412 | MISSING | -- | Callback registration for storage change notifications | | `fs.RegisterStorageRemoveMsg` | filestore.go:4424 | MISSING | -- | Callback registration for message removals (used by replicated streams) | | `fs.RegisterProcessJetStreamMsg` | filestore.go:4431 | MISSING | -- | Callback registration for new JetStream message processing | | `fs.hashKeyForBlock` | filestore.go:4439 | MISSING | -- | HighwayHash key derivation per block index | | `mb.setupWriteCache` | filestore.go:4443 | PARTIAL | `MsgBlock._cache` / `WriteCacheManager` | .NET has a cache dict + WriteCacheManager; Go has elastic pointer, cache expiry timer, and loadMsgs fallback | | `mb.finishedWithCache` | filestore.go:4477 | PARTIAL | `WriteCacheManager.EvictBlock` | .NET evicts on block rotation; Go weakens elastic reference when no pending writes | | `fs.newMsgBlockForWrite` | filestore.go:4485 | PARTIAL | `FileStore.RotateBlock` | .NET creates new MsgBlock; Go also handles flush of pending, cache recycling, HighwayHash init, encryption key gen, flush loop spin-up | | `fs.genEncryptionKeysForBlock` | filestore.go:4575 | MISSING | -- | Per-block AEAD key generation and keyfile writing; .NET uses per-payload AEAD instead | | `fs.storeRawMsg` (internal) | filestore.go:4599 | PARTIAL | `FileStore.StoreRawMsg` | .NET stores but lacks: per-subject max enforcement (MaxMsgsPer), DiscardNew checks, psim updates, enforceMsgLimit/enforceBytesLimit calls, per-message TTL wheel, message scheduling | | `fs.StoreRawMsg` (exported) | filestore.go:4759 | PORTED | `FileStore.StoreRawMsg` | Public wrapper with callback | | `fs.StoreMsg` | filestore.go:4779 | PORTED | `FileStore.StoreMsg` | Returns (seq, ts); .NET simplified | | `mb.skipMsg` | filestore.go:4801 | PARTIAL | `MsgBlock.WriteSkip` | .NET writes skip record; Go also handles empty-block meta update, dmap insert, ebit flag | | `fs.SkipMsg` | filestore.go:4829 | PORTED | `FileStore.SkipMsg` | Sequence mismatch check, first/last update | | `fs.SkipMsgs` | filestore.go:4868 | PORTED | `FileStore.SkipMsgs` | .NET iterates SkipMsg; Go batch-inserts into dmap with single write placeholder | | `fs.FlushAllPending` | filestore.go:4933 | PORTED | `FileStore.FlushAllPending` | .NET flushes active block + writes stream.state | | `fs.rebuildFirst` | filestore.go:4940 | MISSING | -- | Rebuilds first-block state after removal errors | | `fs.firstSeqForSubj` | filestore.go:4966 | MISSING | -- | Optimized first-seq lookup per subject using psim fblk/lblk | | `fs.enforceMsgLimit` | filestore.go:5033 | MISSING | -- | Drop oldest messages when MaxMsgs exceeded; bulk purge of full blocks | | `fs.enforceBytesLimit` | filestore.go:5061 | MISSING | -- | Drop oldest messages when MaxBytes exceeded; bulk purge of full blocks | | `fs.enforceMsgPerSubjectLimit` | filestore.go:5091 | MISSING | -- | Complex per-subject enforcement with psim skew detection and rebuild | | `fs.deleteFirstMsg` | filestore.go:5222 | MISSING | -- | Trivial wrapper: removeMsgViaLimits(firstSeq) | | `fs.removeMsgViaLimits` | filestore.go:5229 | MISSING | -- | Remove via limits (no forced index update) | | `fs.RemoveMsg` | filestore.go:5235 | PORTED | `FileStore.RemoveMsg` | .NET simplified: removes from dict + soft-deletes in block | | `fs.EraseMsg` | filestore.go:5239 | PORTED | `FileStore.EraseMsg` | .NET does secure erase via MsgBlock.Delete(secureErase: true) | | `fs.removePerSubject` | filestore.go:5245 | MISSING | -- | Decrements psim per-subject counter, removes from trie at zero | | `fs.removeMsg` (internal) | filestore.go:5267 | PARTIAL | `FileStore.RemoveMsg` / `FileStore.EraseMsg` | .NET simplified; Go handles: tombstone writing, cache loading, per-subject tracking, FIFO optimization, out-of-order dmap insert, inline compaction, secure erase, callback | | `fs.removeMsgFromBlock` | filestore.go:5307 | PARTIAL | `FileStore.DeleteInBlock` | .NET delegates to MsgBlock.Delete; Go handles full per-block accounting, tombstone write, secure erase, FIFO selectNextFirst, dmap, inline compact, empty-block removal | | `fs.removeMsgsInRange` | filestore.go:5509 | MISSING | -- | Bulk removal in [first,last] with block-level purge optimization | | `mb.shouldCompactInline` | filestore.go:5553 | MISSING | -- | Heuristic: compact when rbytes > minimum and 2x savings possible | | `mb.shouldCompactSync` | filestore.go:5561 | MISSING | -- | Sync-time compaction heuristic | | `mb.compact` | filestore.go:5567 | MISSING | -- | Full block rewrite compaction | | `mb.compactWithFloor` | filestore.go:5576 | MISSING | -- | Compaction with tombstone cleanup; handles compression, encryption, dmap pruning | | `mb.slotInfo` | filestore.go:5735 | MISSING | -- | Cache index slot lookup for record offset/length | | `fs.isClosed` | filestore.go:5778 | PORTED | `FileStore._stopped` | Atomic bool in Go; simple bool in .NET | | `mb.spinUpFlushLoop` | filestore.go:5783 | MISSING | -- | Goroutine-based flush loop per block | | `mb.spinUpFlushLoopLocked` | filestore.go:5791 | MISSING | -- | Locked version of flush loop startup | | `kickFlusher` | filestore.go:5805 | MISSING | -- | Non-blocking channel send to wake flush goroutine | | `mb.kickFlusher` | filestore.go:5815 | MISSING | -- | Per-block flusher kick | | `mb.setInFlusher` | filestore.go:5821 | MISSING | -- | Flusher state tracking | | `mb.clearInFlusher` | filestore.go:5827 | MISSING | -- | Flusher cleanup, close channels | | `mb.flushLoop` | filestore.go:5842 | MISSING | -- | Background goroutine: coalesce pending writes, flush, close FDs when no longer last block | | `mb.eraseMsg` | filestore.go:5890 | PARTIAL | `MsgBlock.Delete(secureErase)` | .NET overwrites payload with random bytes; Go overwrites full record including header, recalculates hash, updates both cache and disk | | `mb.truncate` | filestore.go:5940 | MISSING | -- | Truncate block to target sequence; handles decompression/re-encryption, eof calc, per-subject reset | | `mb.isEmpty` | filestore.go:6046 | PARTIAL | `MsgBlock.MessageCount == 0` | Go uses atomic first/last comparison | | `mb.selectNextFirst` | filestore.go:6051 | MISSING | -- | Walk dmap to find next non-deleted first sequence | | `fs.selectNextFirst` | filestore.go:6091 | MISSING | -- | Clean up empty leading blocks, advance first | | `mb.resetCacheExpireTimer` | filestore.go:6123 | MISSING | -- | Per-block timer reset for cache expiry | | `mb.startCacheExpireTimer` | filestore.go:6135 | MISSING | -- | Start per-block cache expiry timer | | `mb.clearCacheAndOffset` | filestore.go:6141 | PARTIAL | `MsgBlock.ClearCache` | .NET clears cache dict; Go also resets linear scan tracker | | `mb.clearCache` | filestore.go:6148 | PARTIAL | `MsgBlock.ClearCache` | .NET nulls cache dict; Go handles fss expiry, elastic pointer cleanup, buffer recycling | | `mb.expireCache` | filestore.go:6176 | PARTIAL | `WriteCacheManager` background tick | .NET uses 500ms periodic timer; Go uses per-block AfterFunc timer | | `mb.tryForceExpireCache` | filestore.go:6182 | MISSING | -- | Force expire by temporarily clearing timestamps | | `mb.tryForceExpireCacheLocked` | filestore.go:6189 | MISSING | -- | Locked force-expire | | `mb.tryExpireWriteCache` | filestore.go:6199 | MISSING | -- | Expire write cache on block rotation, return recyclable buffer | | `mb.expireCacheLocked` | filestore.go:6220 | PARTIAL | `WriteCacheManager` eviction logic | .NET time-based eviction; Go handles elastic pointer strengthening, pending write detection, fss separate expiry | | `fs.startAgeChk` | filestore.go:6277 | PARTIAL | `FileStore.RegisterTtl` + `HashWheel` | .NET uses HashWheel; Go uses AfterFunc timer | | `fs.resetAgeChk` | filestore.go:6287 | PARTIAL | (implicit in ExpireFromWheel) | Go has sophisticated next-expire calculation with minimum 250ms floor | | `fs.cancelAgeChk` | filestore.go:6355 | PARTIAL | (implicit in Dispose) | .NET disposes wheel; Go stops and nils timer | | `fs.expireMsgs` | filestore.go:6364 | PARTIAL | `FileStore.ExpireFromWheel` | .NET does basic seq removal; Go handles: negative TTL (never-expire), SDM processing, THW-based TTL expiry, sorted removal for SDM, age delta recalculation | | `fs.shouldProcessSdm` | filestore.go:6482 | MISSING | -- | Subject Delete Marker pending tracking | | `fs.shouldProcessSdmLocked` | filestore.go:6489 | MISSING | -- | Locked SDM state check with rate limiting | | `fs.handleRemovalOrSdm` | filestore.go:6522 | MISSING | -- | Either removes via callback or emits SDM header message | | `fs.runMsgScheduling` | filestore.go:6543 | MISSING | -- | Background message scheduling with repeating schedule support | | `fs.checkAndFlushLastBlock` | filestore.go:6581 | PARTIAL | `FileStore.FlushAllPending` | .NET flushes active block; Go also handles rebuild state on lost data | | `fs.checkMsgs` | filestore.go:6598 | MISSING | -- | Full checksum verification scan of all blocks | | `mb.enableForWriting` | filestore.go:6622 | NOT_APPLICABLE | -- | Go-specific: open file descriptor, spin up flusher; .NET MsgBlock always has FileStream open | | `mb.writeTombstone` | filestore.go:6646 | PARTIAL | `MsgBlock.WriteSkip` | .NET writes skip/tombstone via WriteSkip; Go uses tbit flag on sequence | | `mb.writeTombstoneNoFlush` | filestore.go:6652 | MISSING | -- | Tombstone write without flush (used in bulk operations) | | `mb.writeMsgRecord` | filestore.go:6660 | PORTED | `MsgBlock.Write` / `MsgBlock.WriteAt` | .NET has both auto-seq and explicit-seq variants | | `mb.writeMsgRecordLocked` | filestore.go:6669 | PARTIAL | `MsgBlock.WriteAt` (under lock) | .NET writes record + updates index/cache; Go handles: per-subject fss tracking, elastic pointer strengthening, buffer pool management, HighwayHash checksum, tombstone accounting, flush-in-place, flusher kick | | `mb.pendingWriteSize` | filestore.go:6821 | MISSING | -- | Go tracks pending = cache.buf - cache.wp; .NET writes synchronously | | `mb.pendingWriteSizeLocked` | filestore.go:6831 | MISSING | -- | Locked version | | `mb.closeFDs` | filestore.go:6843 | PARTIAL | `MsgBlock.Dispose` | .NET disposes FileStream; Go checks pending data first | | `mb.closeFDsLocked` | filestore.go:6849 | PARTIAL | `MsgBlock.Dispose` | .NET unconditional; Go returns error if pending data | | `mb.closeFDsLockedNoCheck` | filestore.go:6857 | PARTIAL | `MsgBlock.Dispose` | .NET disposes; Go just closes mfd | | `mb.bytesPending` | filestore.go:6867 | NOT_APPLICABLE | -- | Go write-cache pending slice; .NET writes synchronously (no pending buffer) | | `mb.blkSize` | filestore.go:6885 | PORTED | `MsgBlock.BytesUsed` | Returns total bytes including deleted | | `mb.updateAccounting` | filestore.go:6897 | PARTIAL | (inline in MsgBlock.Write/WriteAt) | .NET updates first/last/totalWritten; Go handles ebit, atomic first/last, rbytes/bytes split | | `fs.checkLastBlock` | filestore.go:6920 | PARTIAL | `FileStore.EnsureActiveBlock` | .NET rotates on IsSealed; Go checks rbytes + rl > BlockSize | | `fs.writeMsgRecord` | filestore.go:6933 | PARTIAL | `FileStore.StoreMsg` / `StoreRawMsg` | .NET writes via MsgBlock; Go checks msg too large, increments dirty counter | | `fs.writeTombstone` | filestore.go:6956 | MISSING | -- | fs-level tombstone with block size enforcement | | `fs.writeTombstoneNoFlush` | filestore.go:6968 | MISSING | -- | No-flush variant | | `mb.recompressOnDiskIfNeeded` | filestore.go:6978 | MISSING | -- | On-disk block recompression (S2) after block seal | | Status | Count | | PORTED | 14 | | PARTIAL | 27 | | MISSING | 37 | | NOT_APPLICABLE | 2 | | **Total** | **80** | | `mb.recompressOnDiskIfNeeded` (tail) | filestore.go:7000-7027 | PARTIAL | `FileStore.TransformForPersist` | .NET applies compression at write time (S2Codec); no post-hoc re-compression of existing blocks | | `mb.atomicOverwriteFile` | filestore.go:7030-7111 | MISSING | — | Go writes block to temp file then renames; .NET uses direct `RandomAccess.Write` with no atomic overwrite | | `mb.decompressIfNeeded` | filestore.go:7114-7132 | PORTED | `FileStore.RestorePayload` / `S2Codec.Decompress` | .NET decompresses via S2Codec in RestorePayload; metadata parsing differs | | `mb.encryptOrDecryptIfNeeded` | filestore.go:7135-7145 | PARTIAL | `AeadEncryptor.Encrypt/Decrypt` | .NET uses AEAD (ChaCha20/AES-GCM) not XOR stream cipher; no block-level BEK regeneration | | `mb.ensureRawBytesLoaded` | filestore.go:7148-7163 | MISSING | — | .NET MsgBlock tracks `_writeOffset` directly; no lazy raw-bytes stat from disk | | `fs.syncBlocks` | filestore.go:7166-7291 | MISSING | — | No periodic block sync timer; .NET uses `FileStream.Flush(flushToDisk: true)` on demand | | `fs.selectMsgBlock` | filestore.go:7296-7299 | PORTED | `FileStore.FindBlockForSequence` | Binary search over `_blocks` by sequence range | | `fs.selectMsgBlockWithIndex` | filestore.go:7302-7343 | PORTED | `FileStore.FindBlockForSequence` / `FindFirstBlockAtOrAfter` | Combined linear/binary search; .NET uses pure binary search | | `fs.selectMsgBlockForStart` (by time) | filestore.go:7348-7372 | MISSING | — | No time-based block selection; `GetSeqFromTime` does linear scan over `_messages` | | `mb.indexCacheBuf` | filestore.go:7376-7557 | MISSING | — | Go builds index+FSS from raw block buffer; .NET uses `Dictionary` index from `RebuildIndex` (much simpler) | | `mb.flushPendingMsgs` | filestore.go:7560-7572 | PARTIAL | `MsgBlock.Flush` | .NET calls `_file.Flush(flushToDisk: true)`; Go has pending write buffer with encryption and rebuild-on-error | | `mb.writeAt` | filestore.go:7577-7588 | PORTED | `RandomAccess.Write` in `MsgBlock.Write/WriteAt` | .NET uses `RandomAccess.Write` for positional I/O; Go uses `dios` semaphore for I/O gating | | `mb.flushPendingMsgsLocked` | filestore.go:7592-7694 | PARTIAL | `MsgBlock.Flush` | Go has complex pending-write logic with encryption, rebuild-on-error, sync-always mode; .NET is simpler flush | | `mb.clearLoading` | filestore.go:7697-7699 | NOT_APPLICABLE | — | Go loading-guard flag; .NET has no equivalent concurrent loading concern | | `mb.loadMsgs` / `mb.loadMsgsWithLock` | filestore.go:7703-7885 | PARTIAL | `MsgBlock.RebuildIndex` / `MsgBlock.Read` | Go loads full block into cache from disk with decrypt/decompress; .NET reads on-demand per-record | | `mb.cacheAlreadyLoaded` / `mb.cacheNotLoaded` | filestore.go:7711-7724 | NOT_APPLICABLE | — | Go weak-reference cache management; .NET uses simple `Dictionary?` cache | | `mb.fssNotLoaded` | filestore.go:7728-7730 | NOT_APPLICABLE | — | Go per-subject state tree tracking; .NET has no block-level FSS | | `mb.openBlock` | filestore.go:7734-7740 | NOT_APPLICABLE | — | Go opens block file with dios semaphore; .NET holds `FileStream` open for block lifetime | | `mb.loadBlock` | filestore.go:7745-7791 | PARTIAL | `MsgBlock.RebuildIndex` (disk read) | Go loads entire block file into pooled buffer; .NET reads record-by-record during recovery | | `mb.fetchMsg` / `mb.fetchMsgNoCopy` / `mb.fetchMsgEx` | filestore.go:7890-7940 | PORTED | `MsgBlock.Read` | .NET does cache-then-disk lookup; Go has copy/no-copy variants and expiry tracking | | Error sentinels (`errNoCache`, `errDeletedMsg`, etc.) | filestore.go:7943-7959 | PARTIAL | Exceptions in `MsgBlock.Read` / `FileStore.LoadMsg` | .NET uses `null` returns and `KeyNotFoundException` instead of sentinel errors | | Bit constants (`cbit`, `dbit`, `hbit`, `ebit`, `tbit`) | filestore.go:7973-7983 | PARTIAL | `MessageRecord.Deleted` flag | Go uses bit-packed flags in seq/rl fields; .NET uses explicit `Deleted` bool in `MessageRecord` | | `mb.cacheLookup` / `mb.cacheLookupNoCopy` / `mb.cacheLookupEx` | filestore.go:7988-8096 | PARTIAL | `MsgBlock.Read` (cache path) | .NET checks `_cache` dictionary then falls back to disk; Go has full slot-based index lookup with hash verification | | `fs.sizeForSeq` | filestore.go:8101-8112 | MISSING | — | No per-sequence size calculation helper | | `fs.msgForSeq` / `fs.msgForSeqLocked` | filestore.go:8117-8160 | PORTED | `FileStore.LoadMsg` | .NET does hash-map lookup then binary-search block fallback | | `mb.msgFromBuf` / `mb.msgFromBufNoCopy` / `mb.msgFromBufEx` | filestore.go:8165-8300 | PARTIAL | `MessageRecord.Decode` | Go parses wire-format from raw buffer with header support; .NET uses `MessageRecord.Decode` with simpler format | | `fs.LoadMsg` | filestore.go:8308-8350 | PORTED | `FileStore.LoadMsg` | Both implement sequence-based message loading with block selection | | `fs.LoadLastMsg` | filestore.go:8352-8400 | PORTED | `FileStore.LoadLastMsg` | Both support subject-filtered last-message lookup | | `mb.firstMatching` | filestore.go:8402-8470 | MISSING | — | Go's per-block first-matching scan with FSS optimization; .NET filters `_messages` dictionary | | `mb.prevMatchingMulti` | filestore.go:~8470-8500 | MISSING | — | Go's per-block reverse multi-filter scan; .NET has no equivalent | | `fs.LoadNextMsg` | filestore.go:8508-8577 | PORTED | `FileStore.LoadNextMsg` | Both support filtered forward scan; Go uses psim skip-ahead, .NET uses LINQ | | `fs.LoadPrevMsg` | filestore.go:8579-8631 | PORTED | `FileStore.LoadPrevMsg` | Both walk backward from start; .NET iterates `_messages` dictionary | | `fs.LoadPrevMsgMulti` | filestore.go:8633-8670 | MISSING | — | No multi-filter reverse load in .NET | | `fs.Type` | filestore.go:8673-8675 | PORTED | `FileStore.Type()` | Returns `StorageType.File` | | `fs.numSubjects` | filestore.go:8679-8681 | PARTIAL | `FileStore.State()` counts subjects | .NET counts in `State()` method; no dedicated `numSubjects` helper | | `fs.numConsumers` | filestore.go:8684-8688 | MISSING | — | No consumer count tracking in FileStore; Go uses `cmu`+`cfs` | | `fs.FastState` | filestore.go:8692-8711 | PORTED | `FileStore.FastState` | Both populate minimal state struct without allocating deleted arrays | | `fs.State` | filestore.go:8714-8756 | PORTED | `FileStore.State` | Both return full state with deleted sequences and subject counts | | `fs.Utilization` | filestore.go:8758-8768 | MISSING | — | No total/reported bytes utilization tracking across blocks | | `fileStoreMsgSizeRaw` / `fileStoreMsgSize` / `fileStoreMsgSizeEstimate` | filestore.go:8770-8785 | MISSING | — | Go wire-format size calculation; .NET uses `MessageRecord.Encode` length | | `fs.ResetState` | filestore.go:8788-8794 | PORTED | `FileStore.ResetState` | .NET is no-op (re-derives from blocks on construction); Go clears scheduling inflight | | `mb.sinceLastActivity` | filestore.go:8797-8812 | MISSING | — | Go tracks lwts/lrts/llts/lsts for cache expiry timing; .NET uses `WriteCacheManager` TTL | | `mb.sinceLastWriteActivity` | filestore.go:8816-8825 | MISSING | — | No separate write-activity tracking | | `checkNewHeader` | filestore.go:8827-8833 | MISSING | — | Go index file magic/version check; .NET uses JSON for stream state | | `mb.readIndexInfo` | filestore.go:8836-8935 | MISSING | — | Go reads binary index (.idx) files with varint encoding; .NET rebuilds from block scan | | `fs.cacheLoads` | filestore.go:8938-8948 | MISSING | — | Cache load counter; no equivalent metric in .NET | | `fs.cacheSize` | filestore.go:8951-8971 | MISSING | — | Cache size tracking; .NET `WriteCacheManager.TotalCachedBytes` is approximate | | `fs.dmapEntries` | filestore.go:8974-8982 | MISSING | — | Total dmap entries across blocks; .NET `SequenceSet` per block but no aggregate | | `subjectsEqual` / `subjectsAll` / `compareFn` | filestore.go:8986-9001 | PARTIAL | `FileStore.SubjectMatchesFilter` | .NET has unified `SubjectMatchesFilter`; Go has separate comparator functions | | `fs.PurgeEx` | filestore.go:9005-9194 | PORTED | `FileStore.PurgeEx` | Both support subject-filtered purge with keep/seq limits; Go has tombstone writing, .NET is simpler | | `fs.Purge` / `fs.purge` | filestore.go:9198-9313 | PORTED | `FileStore.Purge` | Both clear all messages; Go does atomic directory rename with tombstone, .NET does `_messages.Clear()` + block dispose | | `fs.recoverPartialPurge` | filestore.go:9316-9337 | MISSING | — | Crash recovery for partial purge (ndir/pdir cleanup); .NET has no partial-purge recovery | | `fs.Compact` / `fs.compact` | filestore.go:9342-9591 | PORTED | `FileStore.Compact` | Both remove messages below seq; Go has block-level compaction with encryption/compression, .NET is in-memory | | `fs.reset` | filestore.go:9594-9641 | PARTIAL | `FileStore.Truncate(0)` | .NET `Truncate(0)` clears everything; Go `reset()` resets to zero state with psim/sdm cleanup | | `mb.tombs` / `mb.tombsLocked` | filestore.go:9644-9682 | MISSING | — | Tombstone record scanning from raw block buffer; .NET has no tombstone concept | | `mb.numPriorTombs` / `mb.numPriorTombsLocked` | filestore.go:9685-9738 | MISSING | — | Count tombstones for prior blocks; .NET has no tombstone tracking | | `fs.Truncate` | filestore.go:9741-9925 | PORTED | `FileStore.Truncate` | Both remove messages above seq; Go has block-level truncation with tombstone writes, .NET is in-memory | | `fs.lastSeq` | filestore.go:9927-9932 | PORTED | `FileStore._last` field | Direct field access vs Go lock-guarded helper | | `fs.numMsgBlocks` | filestore.go:9935-9939 | PORTED | `FileStore.BlockCount` | Property returns `_blocks.Count` | | `fs.addMsgBlock` | filestore.go:9943-9947 | PORTED | `FileStore.RotateBlock` (inline) | .NET adds to `_blocks` list and sets `_activeBlock` | | `fs.removeMsgBlockFromList` | filestore.go:9951-9964 | PARTIAL | `FileStore.DisposeAllBlocks` | .NET only bulk-removes blocks; no individual block removal from list | | `fs.removeMsgBlock` | filestore.go:9968-9988 | MISSING | — | Individual block removal with tombstone writes for last-seq preservation | | `fs.forceRemoveMsgBlock` | filestore.go:9992-9995 | MISSING | — | Force removal (dirty close + remove from list) | | `fs.purgeMsgBlock` | filestore.go:9999-10054 | MISSING | — | Per-block purge with per-subject tracking cleanup and scheduling metadata | | `mb.dirtyClose` | filestore.go:10058-10062 | PARTIAL | `MsgBlock.Dispose` | .NET disposes file handle; Go clears cache, stops timers, closes channels | | `mb.dirtyCloseWithRemove` | filestore.go:10065-10103 | PARTIAL | `MsgBlock.Dispose` + `FileStore.CleanBlockFiles` | Go closes and optionally removes files; .NET separates disposal from file cleanup | | `mb.removeSeqPerSubject` | filestore.go:10107-10145 | MISSING | — | Per-block per-subject state tracking with lazy first/last recalculation | | `mb.recalculateForSubj` | filestore.go:10149-10259 | MISSING | — | Scan cache buffer to recalculate first/last seq for a subject within a block | | `fs.resetGlobalPerSubjectInfo` | filestore.go:10262-10271 | MISSING | — | Rebuild psim (global per-subject index map) from all blocks | | `mb.resetPerSubjectInfo` / `mb.generatePerSubjectInfo` | filestore.go:10274-10339 | MISSING | — | Per-block subject tree (FSS) generation from block scan | | `mb.ensurePerSubjectInfoLoaded` | filestore.go:10343-10356 | MISSING | — | Lazy FSS loading guard; .NET has no block-level subject tracking | | `fs.populateGlobalPerSubjectInfo` | filestore.go:10360-10383 | MISSING | — | Populate psim from block FSS during recovery | | `mb.close` | filestore.go:10386-10424 | PORTED | `MsgBlock.Dispose` | Both close file handles; Go also flushes pending, clears FSS, stops timers | | `fs.closeAllMsgBlocks` | filestore.go:10426-10430 | PORTED | `FileStore.DisposeAllBlocks` | Both iterate and close/dispose all blocks | | `fs.Delete` | filestore.go:10432-10511 | PORTED | `FileStore.Delete` | Both stop and remove all data; Go has async directory removal with `dios` gating | | `fs.setSyncTimer` / `fs.cancelSyncTimer` | filestore.go:10514-10532 | MISSING | — | Periodic sync timer with jitter; .NET has no equivalent timer | | Full state magic/version constants | filestore.go:10537-10541 | MISSING | — | Binary full-state format versioning; .NET uses JSON | | `fs.flushStreamStateLoop` | filestore.go:10544-10571 | MISSING | — | Background goroutine for periodic state flush; .NET writes on `FlushAllPending` call | | `timestampNormalized` | filestore.go:10574-10579 | PARTIAL | Inline in `FileStore` methods | .NET normalizes via `DateTimeOffset.ToUnixTimeMilliseconds` | | `fs.writeFullState` / `fs._writeFullState` | filestore.go:10583-10831 | PARTIAL | `FileStore.WriteStreamStateAsync` | Go writes binary-encoded full state with psim, block metadata, checksums; .NET writes JSON snapshot | | `fs.writeTTLState` | filestore.go:10833-10845 | MISSING | — | TTL state persistence; .NET uses in-memory `HashWheel` only | | `fs.writeMsgSchedulingState` | filestore.go:10847-10859 | MISSING | — | Message scheduling state persistence | | `fs.Stop` / `fs.stop` | filestore.go:10862-10940 | PORTED | `FileStore.Stop` | Both flush and close; Go also stops consumer stores, cancels timers, writes full state | | `fs.streamSnapshot` | filestore.go:10945-10997+ | PARTIAL | `FileStore.CreateSnapshotAsync` | .NET creates JSON snapshot; Go creates S2-compressed tar archive with block files | | Status | Count | | PORTED | 26 | | PARTIAL | 19 | | MISSING | 32 | | NOT_APPLICABLE | 5 | | DEFERRED | 0 | | **Total** | **82** | | `mb.dirtyClose()` | filestore.go:10058 | PARTIAL | `MsgBlock.Dispose()` | .NET disposes file handle; Go also stops cache timer, closes fds without flush, optionally removes files | | `mb.dirtyCloseWithRemove(remove)` | filestore.go:10065 | PARTIAL | `MsgBlock.Dispose()` | .NET has no remove-on-close path; Go removes .blk and key files when `remove=true` | | `mb.removeSeqPerSubject(subj, seq)` | filestore.go:10107 | MISSING | — | Per-subject index tracking within a block; .NET MsgBlock has no per-subject FSS tree | | `mb.recalculateForSubj(subj, ss)` | filestore.go:10149 | MISSING | — | Scans cache to recalculate first/last seq for a subject within a block | | `mb.resetPerSubjectInfo()` | filestore.go:10274 | MISSING | — | Clears and regenerates per-subject info for a block | | `mb.generatePerSubjectInfo()` | filestore.go:10281 | MISSING | — | Builds per-subject SimpleState tree (fss) from block cache | | `mb.ensurePerSubjectInfoLoaded()` | filestore.go:10343 | MISSING | — | Lazy-loads per-subject info when first needed | | `mb.close(sync)` | filestore.go:10386 | PARTIAL | `MsgBlock.Dispose()` | .NET disposes only; Go flushes pending, clears cache, syncs fd, marks closed | | `fs.resetGlobalPerSubjectInfo()` | filestore.go:10262 | MISSING | — | Clears and rebuilds global PSIM (per-subject index map) from all blocks | | `fs.populateGlobalPerSubjectInfo(mb)` | filestore.go:10360 | MISSING | — | Populates global PSIM from a single block's fss tree | | `fs.closeAllMsgBlocks(sync)` | filestore.go:10426 | PORTED | `FileStore.DisposeAllBlocks()` | .NET iterates blocks and disposes; Go supports optional sync | | `fs.Delete(inline)` | filestore.go:10432 | PORTED | `FileStore.Delete(bool)` | .NET calls Stop + Directory.Delete; Go has more elaborate rename-then-remove, storage callback, purge directory handling | | `fs.setSyncTimer()` | filestore.go:10514 | MISSING | — | Periodic sync timer for dirty blocks; .NET has no equivalent background sync timer | | `fs.cancelSyncTimer()` | filestore.go:10527 | MISSING | — | Cancels the periodic sync timer | | `fullStateMagic` / `fullStateVersion` | filestore.go:10537 | MISSING | — | Binary state file versioning constants | | `fs.flushStreamStateLoop(qch, done)` | filestore.go:10544 | PARTIAL | `FileStore.WriteStreamStateAsync()` | .NET has on-demand JSON state write; Go has a goroutine loop that periodically writes binary index.db | | `timestampNormalized(t)` | filestore.go:10574 | MISSING | — | Utility: returns UnixNano or 0 for zero time | | `fs.writeFullState()` | filestore.go:10583 | PARTIAL | `FileStore.WriteStreamStateAsync()` | .NET writes JSON snapshot; Go writes binary format with PSIM, per-block dmaps, block checksums, encryption, highway hash verification | | `fs.forceWriteFullState()` | filestore.go:10588 | PARTIAL | `FileStore.WriteStreamStateAsync()` | .NET has no force vs non-force distinction; Go skips if not dirty (non-forced) or too complex | | `fs._writeFullState(force)` | filestore.go:10599 | PARTIAL | `FileStore.WriteStreamStateAsync()` | .NET is a simplified JSON version; Go has full binary encoding with PSIM, block iteration, encryption, consistency checking | | `fs.writeTTLState()` | filestore.go:10833 | MISSING | — | Persists TTL tracking state to ttl.state file | | `fs.writeMsgSchedulingState()` | filestore.go:10847 | MISSING | — | Persists message scheduling state to disk | | `fs.Stop()` | filestore.go:10862 | PORTED | `FileStore.Stop()` | .NET flushes active block and disposes; Go also writes full state, cancels timers, stops consumer stores, unregisters ATS | | `fs.stop(delete, writeState)` | filestore.go:10867 | PARTIAL | `FileStore.Stop()` / `FileStore.Delete()` | .NET has separate Stop/Delete; Go combines into one method with flags for delete and writeState | | `fs.streamSnapshot(w, includeConsumers, errCh)` | filestore.go:10945 | MISSING | — | Streams tar+S2 snapshot through a pipe; .NET has `StreamSnapshotService` at a higher level but no block-level streaming snapshot | | `fs.Snapshot(deadline, checkMsgs, includeConsumers)` | filestore.go:11129 | MISSING | — | Creates `SnapshotResult` with pipe reader; .NET `CreateSnapshotAsync` in IStreamStore returns byte[] | | `fs.fileStoreConfig()` | filestore.go:11171 | NOT_APPLICABLE | — | Simple getter under lock; .NET options are immutable | | `fs.readLockAllMsgBlocks()` | filestore.go:11179 | NOT_APPLICABLE | — | Go RWMutex pattern; .NET uses per-block RWLockSlim | | `fs.readUnlockAllMsgBlocks()` | filestore.go:11187 | NOT_APPLICABLE | — | Go RWMutex pattern; .NET uses per-block RWLockSlim | | `fs.EncodedStreamState(failed)` | filestore.go:11194 | PARTIAL | `FileStore.EncodedStreamState(ulong)` | .NET returns empty array (stub); Go returns full binary encoding with deleted blocks | | `fs.deleteBlocks()` | filestore.go:11253 | MISSING | — | Returns DeleteBlocks (ranges + avl seqsets) representing interior deletes between and within blocks | | `fs.deleteMap()` | filestore.go:11293 | MISSING | — | Unions all block dmaps into a single SequenceSet | | `fs.SyncDeleted(dbs)` | filestore.go:11313 | MISSING | — | Synchronises deleted state from a peer's DeleteBlocks — used for NRG cluster replication | | `fs.ConsumerStore(name, created, cfg)` | filestore.go:11379 | PARTIAL | `FileStore.ConsumerStore(name, created, cfg)` | .NET creates ConsumerFileStore with state file; Go also handles encryption key setup, meta file writing, cipher conversion, memory-storage override | | `o.convertCipher()` | filestore.go:11511 | MISSING | — | Converts consumer state between ChaCha and AES ciphers | | `o.kickFlusher()` | filestore.go:11570 | PORTED | (implicit via `_dirty` flag + background flusher) | .NET uses periodic polling; Go uses channel-based kick | | `o.setInFlusher()` | filestore.go:11581 | PORTED | `ConsumerFileStore.InFlusher` property | | | `o.clearInFlusher()` | filestore.go:11588 | PORTED | `ConsumerFileStore.RunFlusherAsync()` finally block | | | `o.inFlusher()` | filestore.go:11595 | PORTED | `ConsumerFileStore.InFlusher` | | | `o.flushLoop(fch, qch)` | filestore.go:11602 | PORTED | `ConsumerFileStore.RunFlusherAsync()` | .NET uses Task.Delay polling; Go uses channel-based select with minTime throttle | | `o.SetStarting(sseq)` | filestore.go:11658 | PARTIAL | `ConsumerFileStore.SetStarting(sseq)` | .NET sets AckFloor only; Go also sets Delivered.Stream, AckFloor.Stream, encodes + writes to disk immediately | | `o.UpdateStarting(sseq)` | filestore.go:11671 | PARTIAL | `ConsumerFileStore.UpdateStarting(sseq)` | .NET sets AckFloor only; Go checks sseq > Delivered.Stream, handles AckNone policy, kicks flusher | | `o.Reset(sseq)` | filestore.go:11687 | PORTED | `ConsumerFileStore.Reset(sseq)` | | | `o.HasState()` | filestore.go:11695 | PARTIAL | `ConsumerFileStore.HasState()` | .NET checks in-memory only; Go also checks disk file existence | | `o.UpdateDelivered(dseq, sseq, dc, ts)` | filestore.go:11708 | PARTIAL | `ConsumerFileStore.UpdateDelivered(...)` | .NET has basic pending/redelivery tracking; Go has more nuanced handling: skips if dseq <= AckFloor, updates existing pending entries, respects MaxDeliver, stores dc-1 in redelivered | | `o.UpdateAcks(dseq, sseq)` | filestore.go:11777 | PARTIAL | `ConsumerFileStore.UpdateAcks(dseq, sseq)` | .NET has simplified ack floor advancement; Go handles AckAll policy separately, uses original dseq from pending, walks forward for floor advancement | | `o.EncodedState()` | filestore.go:11857 | PORTED | `ConsumerFileStore.EncodedState()` | | | `o.encodeState()` | filestore.go:11863 | PORTED | `ConsumerStateCodec.Encode()` | | | `o.UpdateConfig(cfg)` | filestore.go:11872 | MISSING | — | Updates consumer config and rewrites meta file | | `o.Update(state)` | filestore.go:11883 | PARTIAL | `ConsumerFileStore.Update(state)` | .NET does direct replacement; Go validates ranges, deep-copies pending/redelivered, checks for outdated updates | | `o.encryptState(buf)` | filestore.go:11932 | MISSING | — | AEAD encryption of consumer state | | `dios` semaphore + `init()` | filestore.go:11948 | NOT_APPLICABLE | — | Go disk I/O semaphore to limit OS thread blocking; .NET uses async I/O natively | | `o.writeState(buf)` | filestore.go:11969 | PARTIAL | `ConsumerFileStore.FlushState()` | .NET writes synchronously with File.WriteAllBytes; Go has encryption, writing flag, dios semaphore, atomic write | | `o.updateConfig(cfg)` | filestore.go:12005 | MISSING | — | Internal config update for ephemeral recovery | | `o.writeConsumerMeta()` | filestore.go:12014 | MISSING | — | Writes meta.json + meta.sum with optional encryption and key file | | `checkConsumerHeader(hdr)` | filestore.go:12070 | PORTED | `ConsumerStateCodec.Decode()` (inline check) | Magic byte and version check done inline in .NET decode | | `o.copyPending()` | filestore.go:12082 | PORTED | `ConsumerFileStore.State()` (inline copy) | Done inline in State() method | | `o.copyRedelivered()` | filestore.go:12090 | PORTED | `ConsumerFileStore.State()` (inline copy) | Done inline in State() method | | `o.Type()` | filestore.go:12099 | PORTED | `ConsumerFileStore.Type()` | | | `o.State()` | filestore.go:12103 | PORTED | `ConsumerFileStore.State()` | | | `o.BorrowState()` | filestore.go:12109 | PORTED | `ConsumerFileStore.BorrowState()` | | | `o.stateWithCopy(doCopy)` | filestore.go:12113 | PARTIAL | `ConsumerFileStore.State()` / `BorrowState()` | .NET always returns from memory; Go falls back to disk read with decryption | | `o.stateWithCopyLocked(doCopy)` | filestore.go:12120 | PARTIAL | `ConsumerFileStore.State()` / `BorrowState()` | .NET has no encrypted disk fallback | | `o.loadState()` | filestore.go:12203 | PORTED | `ConsumerFileStore` constructor | .NET loads state in constructor from disk | | `decodeConsumerState(buf)` | filestore.go:12216 | PORTED | `ConsumerStateCodec.Decode()` | Full Go wire format compatibility including v1 and v2 | | `o.Stop()` | filestore.go:12328 | PORTED | `ConsumerFileStore.Stop()` | | | `o.waitOnFlusher()` | filestore.go:12368 | PARTIAL | `ConsumerFileStore.Stop()` (Task.Wait) | .NET uses Task.Wait with timeout; Go polls with sleep | | `o.Delete()` | filestore.go:12383 | PORTED | `ConsumerFileStore.Delete()` | | | `o.StreamDelete()` | filestore.go:12387 | PORTED | `ConsumerFileStore.StreamDelete()` | | | `o.delete(streamDeleted)` | filestore.go:12391 | PARTIAL | `ConsumerFileStore.Delete()` | .NET always deletes file; Go skips dir removal if stream was already deleted | | `fs.AddConsumer(o)` | filestore.go:12423 | MISSING | — | Registers a ConsumerStore with the fileStore's consumer list | | `fs.RemoveConsumer(o)` | filestore.go:12430 | MISSING | — | Removes a ConsumerStore from the fileStore's consumer list | | `CompressionInfo.MarshalMetadata()` | filestore.go:12451 | MISSING | — | Encodes compression metadata header (algorithm + original size) | | `CompressionInfo.UnmarshalMetadata(b)` | filestore.go:12459 | MISSING | — | Decodes compression metadata header | | `StoreCompression.Compress(buf)` | filestore.go:12477 | PORTED | `S2Codec.CompressWithTrailingChecksum()` | .NET uses IronSnappy; Go uses S2 stream writer | | `StoreCompression.Decompress(buf)` | filestore.go:12517 | PORTED | `S2Codec.DecompressWithTrailingChecksum()` | .NET uses IronSnappy; Go uses S2 stream reader | | `fs.writeFileWithOptionalSync(name, data, perm)` | filestore.go:12549 | PORTED | `AtomicFileWriter.WriteAtomicallyAsync()` | .NET uses temp-file + rename pattern; Go identical pattern + optional O_SYNC + dios semaphore | | `writeFileWithSync(name, data, perm)` | filestore.go:12553 | PARTIAL | `AtomicFileWriter.WriteAtomicallyAsync()` | .NET always async, no O_SYNC; Go forces sync | | `writeAtomically(name, data, perm, sync)` | filestore.go:12557 | PORTED | `AtomicFileWriter.WriteAtomicallyAsync()` | Core implementation ported; .NET lacks O_SYNC flag and directory fsync | | `validatePathExists(path, dir)` | dirstore.go:40 | MISSING | — | Path validation utility | | `validateDirPath(path)` | dirstore.go:68 | MISSING | — | Directory path validation | | `JWTChanged` callback type | dirstore.go:73 | MISSING | — | Change notification delegate | | `DirJWTStore` struct | dirstore.go:77 | MISSING | — | JWT store with sharding, expiration, LRU | | `newDir(dirPath, create)` | dirstore.go:89 | MISSING | — | Directory creation helper | | `NewImmutableDirJWTStore(...)` | dirstore.go:110 | MISSING | — | Read-only JWT store factory | | `NewDirJWTStore(...)` | dirstore.go:121 | MISSING | — | Writable JWT store factory | | `deleteType` enum | dirstore.go:133 | MISSING | — | NoDelete / RenameDeleted / HardDelete | | `NewExpiringDirJWTStore(...)` | dirstore.go:150 | MISSING | — | JWT store with TTL, LRU eviction, limits | | `store.IsReadOnly()` | dirstore.go:193 | MISSING | — | | | `store.LoadAcc(publicKey)` | dirstore.go:197 | MISSING | — | Load account JWT | | `store.SaveAcc(publicKey, jwt)` | dirstore.go:201 | MISSING | — | Save account JWT | | `store.LoadAct(hash)` | dirstore.go:205 | MISSING | — | Load activation JWT | | `store.SaveAct(hash, jwt)` | dirstore.go:209 | MISSING | — | Save activation JWT | | `store.Close()` | dirstore.go:213 | MISSING | — | Close expiration tracker | | `store.Pack(maxJWTs)` | dirstore.go:223 | MISSING | — | Pack JWTs for replication | | `store.PackWalk(maxJWTs, cb)` | dirstore.go:267 | MISSING | — | Streaming pack with callback | | `store.Merge(pack)` | dirstore.go:318 | MISSING | — | Merge packed JWTs into store | | `store.Reload()` | dirstore.go:339 | MISSING | — | Reload JWTs from disk | | `store.pathForKey(publicKey)` | dirstore.go:377 | MISSING | — | Compute file path with optional sharding | | `store.load(publicKey)` | dirstore.go:395 | MISSING | — | Load JWT from disk | | `store.write(path, publicKey, jwt)` | dirstore.go:412 | MISSING | — | Write JWT with hash tracking, LRU eviction | | `store.delete(publicKey)` | dirstore.go:449 | MISSING | — | Delete JWT (rename or hard delete) | | `store.save(publicKey, jwt)` | dirstore.go:478 | MISSING | — | Save JWT with change callback | | `store.saveIfNewer(publicKey, jwt)` | dirstore.go:506 | MISSING | — | Save only if JWT is newer | | `xorAssign(lVal, rVal)` | dirstore.go:549 | MISSING | — | XOR hash helper | | `store.Hash()` | dirstore.go:556 | MISSING | — | Return XOR hash of all indexed JWTs | | `jwtItem` struct | dirstore.go:567 | MISSING | — | Priority queue item | | `expirationTracker` struct | dirstore.go:575 | MISSING | — | Heap + LRU + hash tracker | | `expirationTracker` heap methods | dirstore.go:587–619 | MISSING | — | Len, Less, Swap, Push, Pop | | `pq.updateTrack(publicKey)` | dirstore.go:621 | MISSING | — | Update expiration + LRU position | | `pq.unTrack(publicKey)` | dirstore.go:635 | MISSING | — | Remove from tracker | | `pq.track(publicKey, hash, jwt)` | dirstore.go:643 | MISSING | — | Add/update tracking entry | | `pq.close()` | dirstore.go:672 | MISSING | — | Stop expiration goroutine | | `store.startExpiring(...)` | dirstore.go:680 | MISSING | — | Start background expiration ticker | | Status | Count | | PORTED | 22 | | PARTIAL | 23 | | MISSING | 24 | | NOT_APPLICABLE | 4 | | **Total** | **73** | | Status | Count | | PORTED | 0 | | PARTIAL | 0 | | MISSING | 33 | | NOT_APPLICABLE | 0 | | **Total** | **33** | | Status | Count | | PORTED | 22 | | PARTIAL | 23 | | MISSING | 57 | | NOT_APPLICABLE | 4 | | **Total** | **106** | | `jetStreamCluster` struct | :44-84 | PARTIAL | `JetStreamMetaGroup` in `Cluster/JetStreamMetaGroup.cs` | .NET has meta group tracking (streams map, inflight, peer management) but missing: `streamResults`/`consumerResults` subscriptions, `stepdown`/`peerRemove`/`peerStreamMove`/`peerStreamCancelMove` subscriptions, `qch`/`stopped` channels, `lastMetaSnapTime`/`lastMetaSnapDuration` monitoring fields | | `inflightStreamInfo` struct | :87-91 | PORTED | `InflightInfo` record in `JetStreamMetaGroup.cs:1016` | Mapped to a record with `OpsCount`, `Deleted`, `Assignment` | | `inflightConsumerInfo` struct | :94-98 | PORTED | `InflightInfo` record in `JetStreamMetaGroup.cs:1016` | Shared record type for both stream and consumer inflight | | `peerRemoveInfo` struct | :101-106 | MISSING | — | Peer-remove reply tracking for responding after quorum | | `Placement` struct | :109-113 | PORTED | `PlacementPolicy` in `Cluster/PlacementEngine.cs:229-243` | .NET adds `ExcludeTags` and `UniqueTag` beyond Go's Cluster+Tags | | `entryOp` type + constants | :116-150 | PARTIAL | `MetaEntryType` enum in `JetStreamMetaGroup.cs:967-990` | .NET has 7 values (StreamCreate, StreamDelete, ConsumerCreate, ConsumerDelete, PeerAdd, PeerRemove, Unknown). Missing granular stream ops: `streamMsgOp`, `purgeStreamOp`, `deleteMsgOp`, `updateDeliveredOp`, `updateAcksOp`, `updateSkipOp`, `compressedStreamMsgOp`, `deleteRangeOp`, `batchMsgOp`, `batchCommitMsgOp`, `resetSeqOp`, `addPendingRequest`, `removePendingRequest` | | `raftGroup` struct | :154-163 | PORTED | `RaftGroup` in `Cluster/ClusterAssignmentTypes.cs:9-105` | Full port with `Name`, `Peers`, `Storage`, `Cluster`, `Preferred`, plus .NET-specific `DesiredReplicas`, `QuorumSize`, `IsMember`, `SetPreferred`, `RemovePeer`, `AddPeer` helpers | | `streamAssignment` struct | :166-184 | PORTED | `StreamAssignment` in `ClusterAssignmentTypes.cs:111-139` | Core fields ported. Missing: `Client`, `Restore`, `err`, `unsupported`, `resetting` fields | | `unsupportedStreamAssignment` struct | :186-247 | MISSING | — | Entire unsupported stream assignment subsystem (info sub, handler) | | `consumerAssignment` struct | :250-266 | PORTED | `ConsumerAssignment` in `ClusterAssignmentTypes.cs:145-164` | Core fields ported. Missing: `Client`, `State`, `err`, `unsupported` fields | | `unsupportedConsumerAssignment` struct | :268-330 | MISSING | — | Entire unsupported consumer assignment subsystem | | `writeableConsumerAssignment` struct | :332-340 | MISSING | — | Serialization DTO for consumer assignments in snapshots | | `streamPurge` struct | :343-350 | MISSING | — | Replicated purge operation struct | | `streamMsgDelete` struct | :353-360 | MISSING | — | Replicated message delete operation struct | | Constants (`defaultStoreDirName`, etc.) | :362-367 | PARTIAL | — | No explicit constants in .NET; `defaultMetaGroupName` logic is embedded | | `trackedJetStreamServers()` | :370-385 | MISSING | — | Mixed-mode node counting | | `getJetStreamCluster()` | :387-399 | MISSING | — | Server-level accessor for JS cluster state | | `JetStreamIsClustered()` | :401-403 | MISSING | — | Atomic boolean check | | `JetStreamIsLeader()` | :405-407 | PARTIAL | `JetStreamMetaGroup.IsLeader()` | .NET has equivalent on meta group, not on Server | | `JetStreamIsCurrent()` | :409-428 | MISSING | — | Meta RAFT currency check | | `JetStreamSnapshotMeta()` | :430-451 | MISSING | — | Force meta snapshot | | `JetStreamStepdownStream()` | :453-477 | PARTIAL | `ClusterControlApiHandlers.HandleStreamLeaderStepdown()` | .NET has API handler stub but missing actual RAFT node interaction | | `JetStreamStepdownConsumer()` | :479-508 | PARTIAL | `ClusterControlApiHandlers.HandleConsumerLeaderStepdown()` | .NET has API handler stub but missing actual RAFT node interaction | | `JetStreamSnapshotStream()` | :510-539 | MISSING | — | Force stream-level snapshot | | `JetStreamClusterPeers()` | :541-568 | MISSING | — | Returns online JS peer names | | `cc.isLeader()` | :571-577 | PORTED | `JetStreamMetaGroup.IsLeader()` | Equivalent logic | | `cc.isStreamCurrent()` | :582-618 | MISSING | — | Checks if stream is up-to-date via RAFT node + catchup state | | `isStreamHealthy()` | :622-679 | MISSING | — | Comprehensive stream health check (R1 check, node skew, monitor running, catching up, healthy) | | `isConsumerHealthy()` | :683-746 | MISSING | — | Comprehensive consumer health check | | `subjectsOverlap()` | :751-766 | MISSING | — | Cross-cluster subject overlap detection for stream assignments | | `getJetStreamFromAccount()` | :768-784 | MISSING | — | Account-level JS accessor | | `JetStreamIsStreamLeader()` (Server) | :786-794 | MISSING | — | Server-level stream leader check | | `JetStreamIsStreamLeader()` (Account) | :796-804 | MISSING | — | Account-level stream leader check | | `JetStreamIsStreamCurrent()` | :806-814 | MISSING | — | Server-level stream currency check | | `JetStreamIsConsumerLeader()` (Account) | :816-824 | MISSING | — | Account-level consumer leader check | | `JetStreamIsConsumerLeader()` (Server) | :826-834 | MISSING | — | Server-level consumer leader check | | `enableJetStreamClustering()` | :836-862 | MISSING | — | Startup: validates cluster name, routes, calls setupMetaGroup | | `isClustered()` | :866-869 | MISSING | — | Lock-safe clustered check | | `isClusteredNoLock()` | :875-877 | MISSING | — | Lock-free atomic clustered check | | `setupMetaGroup()` | :879-993 | MISSING | — | Creates meta RAFT group: WAL, filestore, bootstrap, observer mode, starts monitorCluster | | `getMetaGroup()` | :995-1002 | MISSING | — | Returns meta RaftNode | | `server()` | :1004-1007 | NOT_APPLICABLE | — | Trivial getter | | `isLeaderless()` | :1010-1026 | MISSING | — | Checks if meta group has no leader beyond lost-quorum interval | | `isGroupLeaderless()` | :1029-1071 | MISSING | — | Checks if a specific raft group is leaderless | | `JetStreamIsStreamAssigned()` | :1073-1086 | MISSING | — | Server-level stream assignment check | | `streamAssigned()` (jsAccount) | :1089-1101 | MISSING | — | Account-level stream assignment check | | `cc.isStreamAssigned()` | :1104-1121 | MISSING | — | Core stream assignment membership check | | `cc.isStreamLeader()` | :1124-1154 | MISSING | — | Checks if this node is leader for a stream's raft group | | `cc.isConsumerLeader()` | :1157-1188 | MISSING | — | Checks if this node is leader for a consumer's raft group | | `trackInflightStreamProposal()` | :1193-1209 | PORTED | `JetStreamMetaGroup.TrackInflightStreamProposal()` | Full equivalent with ops counting | | `removeInflightStreamProposal()` | :1214-1229 | PORTED | `JetStreamMetaGroup.RemoveInflightStreamProposal()` | Full equivalent | | `trackInflightConsumerProposal()` | :1234-1255 | PORTED | `JetStreamMetaGroup.TrackInflightConsumerProposal()` | Full equivalent | | `removeInflightConsumerProposal()` | :1260-1280 | PORTED | `JetStreamMetaGroup.RemoveInflightConsumerProposal()` | Full equivalent | | `clusterQuitC()` | :1283-1290 | MISSING | — | Returns cluster quit channel | | `clusterStoppedC()` | :1293-1300 | MISSING | — | Returns cluster stopped channel | | `setMetaRecovering()` | :1303-1310 | MISSING | — | Sets meta recovery state | | `clearMetaRecovering()` | :1313-1317 | MISSING | — | Clears meta recovery state | | `isMetaRecovering()` | :1320-1324 | MISSING | — | Checks meta recovery state | | `recoveryUpdates` struct | :1327-1333 | MISSING | — | Recovery-time diff tracking for streams/consumers | | `ru.removeStream()` | :1335-1342 | MISSING | — | Recovery diff: track stream removal | | `ru.addStream()` | :1344-1347 | MISSING | — | Recovery diff: track stream addition | | `ru.updateStream()` | :1349-1352 | MISSING | — | Recovery diff: track stream update | | `ru.removeConsumer()` | :1354-1364 | MISSING | — | Recovery diff: track consumer removal | | `ru.addOrUpdateConsumer()` | :1366-1373 | MISSING | — | Recovery diff: track consumer add/update | | `checkForOrphans()` | :1378-1428 | MISSING | — | Post-recovery orphan stream/consumer cleanup | | `getOrphans()` | :1433-1453 | MISSING | — | Returns orphaned streams and consumers not in cluster assignments | | `monitorCluster()` | :1455-1825 | PARTIAL | `JetStreamClusterMonitor.StartAsync()` in `Cluster/JetStreamClusterMonitor.cs` | .NET has basic channel-reading entry dispatch loop. Missing: compact/snapshot timer, leader check timer, health check timer, recovery state machine, snapshot scheduling (sync/async), leader change processing, cluster size checks, cold-boot mixed-mode adjustment | | `checkClusterSize()` | :1828-1869 | MISSING | — | Adjusts cluster size for mixed-mode deployments | | `writeableStreamAssignment` struct | :1872-1879 | MISSING | — | Serialization DTO for snapshot encoding | | `clusterStreamConfig()` | :1881-1888 | MISSING | — | Returns stream config from cluster assignment | | `metaSnapshot()` | :1890-1895 | PARTIAL | `MetaSnapshotCodec.Encode()` | .NET has encoding but not the full `js.metaSnapshot()` wrapper | | `applyMetaSnapshot()` | :1897-2028 | PARTIAL | `JetStreamClusterMonitor.ApplyMetaSnapshot()` + `JetStreamMetaGroup.ReplaceAllAssignments()` | .NET has basic snapshot apply via `ReplaceAllAssignments()`. Missing: the full diff algorithm (saAdd/saDel/saChk), consumer delta processing, recovery-aware branching, orphan cleanup post-snapshot | | `decodeMetaSnapshot()` | :2031-2071 | PORTED | `MetaSnapshotCodec.Decode()` in `Cluster/MetaSnapshotCodec.cs` | S2-compressed JSON decode with version header | | `encodeMetaSnapshot()` | :2075-2143 | PORTED | `MetaSnapshotCodec.Encode()` in `Cluster/MetaSnapshotCodec.cs` | S2-compressed JSON encode with version header, metrics tracking not ported | | `collectStreamAndConsumerChanges()` | :2146-2237 | MISSING | — | Async snapshot pipeline: replay RAFT entries into assignment state machine | | `setStreamAssignmentRecovering()` | :2241-2251 | MISSING | — | Marks stream assignment as recovering | | `setConsumerAssignmentRecovering()` | :2254-2263 | MISSING | — | Marks consumer assignment as recovering | | `sa.copyGroup()` | :2267-2272 | MISSING | — | Deep-copies stream assignment with group | | `ca.copyGroup()` | :2276-2281 | MISSING | — | Deep-copies consumer assignment with group | | `sa.missingPeers()` | :2284-2286 | PORTED | `RaftGroup.IsUnderReplicated` in `ClusterAssignmentTypes.cs:36` | Property-based equivalent | | `processAddPeer()` | :2290-2339 | PORTED | `JetStreamMetaGroup.ProcessAddPeer()` in `JetStreamMetaGroup.cs:790-819` | .NET adds new peer to under-replicated streams. Missing: cluster affinity check, consumer re-assignment proposals | | `processRemovePeer()` | :2342-2393 | PARTIAL | `JetStreamMetaGroup.ProcessRemovePeer()` in `JetStreamMetaGroup.cs:827-841` | .NET removes from known peers and calls RemovePeerFromStream. Missing: self-removal advisory, DisableJetStream, consumer ephemeral cleanup | | `removePeerFromStream()` | :2396-2400 | PORTED | `JetStreamMetaGroup.RemovePeerFromStream()` in `JetStreamMetaGroup.cs:849-862` | Equivalent logic | | `removePeerFromStreamLocked()` | :2403-2439 | PARTIAL | `JetStreamMetaGroup.RemovePeerFromStream()` | .NET has basic peer removal + remap. Missing: RAFT proposals for stream + consumer reassignment | | `hasPeerEntries()` | :2442-2449 | MISSING | — | Checks if entries contain peer add/remove | | `sa.recoveryKey()` | :2453-2458 | MISSING | — | Recovery key generation for stream assignments | | `ca.streamRecoveryKey()` | :2460-2465 | MISSING | — | Recovery key for consumer's parent stream | | `ca.recoveryKey()` | :2467-2472 | MISSING | — | Recovery key generation for consumer assignments | | `applyMetaEntries()` | :2474-2608 | PARTIAL | `JetStreamClusterMonitor.ApplyMetaEntry()` in `JetStreamClusterMonitor.cs:108-146` | .NET dispatches on JSON "Op" field. Go dispatches on binary `entryOp` byte. Missing: catchup entry handling, snapshot entry handling, peer add/remove entry handling, recovery-aware branching, peer-remove reply delivery | | `rg.isMember()` | :2611-2621 | PORTED | `RaftGroup.IsMember()` in `ClusterAssignmentTypes.cs:48` | Equivalent | | `rg.setPreferred()` | :2623-2656 | PARTIAL | `RaftGroup.SetPreferred()` in `ClusterAssignmentTypes.cs:55-61` | .NET requires explicit peer ID. Go version auto-selects from online peers | | `createRaftGroup()` | :2659-2789 | MISSING | — | Full RAFT group creation: WAL setup, filestore/memstore, peer bootstrap, campaign, HA asset limits check | | `mset.raftGroup()` | :2792-2802 | NOT_APPLICABLE | — | Trivial accessor | | `mset.raftNode()` | :2804-2811 | NOT_APPLICABLE | — | Trivial accessor | | `mset.removeNode()` | :2813-2820 | MISSING | — | Deletes RAFT node from stream | | `genPeerInfo()` | :2824-2837 | MISSING | — | Helper for peer set splitting during migration | | `waitOnConsumerAssignments()` | :2842-2892 | MISSING | — | Waits for consumer assignments before proceeding (interest retention) | | `monitorStream()` | :2895-3506 | PARTIAL | `StreamReplicaGroup` in `Cluster/StreamReplicaGroup.cs` | .NET has a simplified stream replica group model with election, propose, apply, snapshot. Missing: the full monitor loop (apply queue, leader change, compact timer, migration monitoring, direct access monitoring, interest state check, restore handling, scale-down logic, catchup error handling, `resetClusteredState` integration) | | `isMigrating()` | :3509-3531 | MISSING | — | Detects stream migration (group peers != config replicas) | | `resetClusteredState()` | :3534-3637 | MISSING | — | Full clustered state reset: stepdown, delete/stop node, re-create stream + consumers | | `isControlHdr()` | :3639-3641 | NOT_APPLICABLE | — | Trivial header check | | `applyStreamEntries()` | :3643-4000+ | PARTIAL | `StreamReplicaGroup.ApplyCommittedEntriesAsync()` in `StreamReplicaGroup.cs:228-269` | .NET dispatches on string-based command prefixes (smsg:, centry:, +peer:, -peer:). Go dispatches on binary `entryOp`. Missing: full `batchMsgOp`/`batchCommitMsgOp` batch apply logic, `deleteMsgOp` with erase/remove, `purgeStreamOp` with request replay, `EntrySnapshot` with `StreamReplicatedState`, `EntryRemovePeer` processing, recovering-aware error handling, `resetClusteredState` on error | | `encodeAddStreamAssignment()` | :8703+ (referenced) | PORTED | `AssignmentCodec.EncodeStreamAssignment()` in `Cluster/AssignmentCodec.cs:52-53` | JSON serialization | | `decodeStreamAssignment()` | :8733+ (referenced) | PORTED | `AssignmentCodec.DecodeStreamAssignment()` in `Cluster/AssignmentCodec.cs:61-74` | JSON deserialization | | `encodeAddConsumerAssignment()` | :9175+ (referenced) | PORTED | `AssignmentCodec.EncodeConsumerAssignment()` in `Cluster/AssignmentCodec.cs:87-88` | JSON serialization | | `decodeConsumerAssignment()` | :9195+ (referenced) | PORTED | `AssignmentCodec.DecodeConsumerAssignment()` in `Cluster/AssignmentCodec.cs:96-109` | JSON deserialization | | `encodeAddConsumerAssignmentCompressed()` | :9226+ (referenced) | PORTED | `AssignmentCodec.CompressIfLarge()` in `Cluster/AssignmentCodec.cs:131-141` | Snappy compression with sentinel byte | | `decodeConsumerAssignmentCompressed()` | :9238+ (referenced) | PORTED | `AssignmentCodec.DecompressIfNeeded()` in `Cluster/AssignmentCodec.cs:151-157` | Snappy decompression | | `selectPeerGroup()` | :7212+ (referenced) | PORTED | `PlacementEngine.SelectPeerGroup()` in `Cluster/PlacementEngine.cs:34-97` | Full pipeline: cluster affinity, tag filter, HA limit split, weighted score sort, unique-tag constraint | | `remapStreamAssignment()` | :7077+ (referenced) | PORTED | `JetStreamMetaGroup.RemapStreamAssignment()` in `JetStreamMetaGroup.cs:872-904` | Replacement peer selection with fallback shrink | | `jsClusteredStreamRequest()` | :7620+ (referenced) | PORTED | `ClusteredRequestProcessor` in `Api/ClusteredRequestProcessor.cs` | Channel-per-request pattern using TaskCompletionSource; register, wait, deliver, cancel-all | | Status | Count | | PORTED | 25 | | PARTIAL | 14 | | MISSING | 48 | | NOT_APPLICABLE | 4 | | DEFERRED | 0 | | **Total** | **91** | | `stream.skipBatchIfRecovering` | 4034-4066 | MISSING | -- | Batch recovery skip logic for stream RAFT apply; requires real RAFT + stream integration | | `jetStream.applyStreamMsgOp` | 4068-4242 | PARTIAL | `StreamReplicaGroup.ApplyStreamMsgOp` | .NET has simplified Store/Remove/Purge enum but lacks S2 decompression, flow-control reply, lastSeq mismatch retry, inflight/counter tracking, preAck clearing | | `Server.replicas` | 4246-4259 | MISSING | -- | Converts RAFT node peers to external PeerInfo with lag/active time | | `jetStream.processStreamLeaderChange` | 4262-4365 | PARTIAL | `JetStreamMetaGroup.ProcessLeaderChange` | .NET has leader change event + inflight clearing but no dedupe-ID clearing, inflight map clearing, advisory sending, stream setLeader, or API response to client | | `stream.shouldSendLostQuorum` | 4371-4379 | MISSING | -- | Throttled quorum-loss advisory gate | | `Server.sendStreamLostQuorumAdvisory` | 4381-4414 | MISSING | -- | Stream lost-quorum advisory publication | | `Server.sendStreamLeaderElectAdvisory` | 4416-4444 | MISSING | -- | Stream leader-elected advisory publication | | `jetStream.streamAssignment` (lookup) | 4448-4458 | PORTED | `JetStreamMetaGroup.GetStreamAssignment` | Direct lookup by account+stream | | `jetStream.streamAssignmentOrInflight` | 4462-4481 | PARTIAL | `JetStreamMetaGroup.IsStreamInflight` + `GetStreamAssignment` | Separate methods exist but no single combined lookup that returns the SA from inflight | | `jetStream.streamAssignmentsOrInflightSeq` | 4485-4507 | MISSING | -- | Iterator over all stream assignments (applied + inflight) for an account | | `jetStream.streamAssignmentsOrInflightSeqAllAccounts` | 4511-4538 | MISSING | -- | Iterator over all stream assignments across all accounts | | `jetStream.processStreamAssignment` | 4540-4647 | PARTIAL | `JetStreamMetaGroup.ProcessStreamAssignment` | .NET validates and adds assignment but lacks account lookup, stream creation dispatch, raft group node transfer, unsupported stream detection, syncSubject bug check | | `jetStream.processUpdateStreamAssignment` | 4650-4763 | PARTIAL | `JetStreamMetaGroup.ProcessUpdateStreamAssignment` | .NET updates config but lacks member check, raft node transfer, stream removal on non-member, unsupported handling | | `Server.removeStream` | 4767-4794 | MISSING | -- | Removes stream from non-member server (stepdown, node delete, monitor quit, stop) | | `jetStream.processClusterUpdateStream` | 4798-4940 | MISSING | -- | Full cluster stream update with scaling up/down, raft group creation, monitor start/stop, advisory response | | `jetStream.processClusterCreateStream` | 4944-5213 | MISSING | -- | Full cluster stream create with raft group, monitor goroutine, restore handling, single-replica path | | `jetStream.processStreamRemoval` | 5216-5264 | PARTIAL | `JetStreamMetaGroup.ProcessStreamRemoval` / `JetStreamClusterMonitor.ProcessStreamRemoval` | .NET removes assignment from meta state; lacks account lookup, created-time check, file cleanup | | `jetStream.processClusterDeleteStream` | 5266-5360 | MISSING | -- | Full stream deletion: raft node delete, monitor shutdown, file cleanup, response sending | | `jetStream.processConsumerAssignment` | 5362-5541 | PARTIAL | `JetStreamMetaGroup.ProcessConsumerAssignment` | .NET validates and adds to meta state; lacks member check, raft group transfer, state capture, unsupported handling, non-member removal with stepdown | | `jetStream.processConsumerRemoval` | 5543-5590 | PARTIAL | `JetStreamMetaGroup.ProcessConsumerRemoval` | .NET removes from meta state; lacks account lookup, created-time check, disk cleanup dispatch | | `consumerAssignmentResult` (struct) | 5592-5597 | MISSING | -- | Result struct for consumer assignment error forwarding | | `jetStream.processClusterCreateConsumer` | 5600-5854 | MISSING | -- | Full consumer create: raft group, add/update consumer, scale-down handling, monitor start, snapshot send, config update response | | `jetStream.processClusterDeleteConsumer` | 5856-5922 | MISSING | -- | Full consumer deletion: stop consumer, node delete, file cleanup, response | | `jetStream.consumerAssignment` (lookup) | 5926-5931 | PORTED | `JetStreamMetaGroup.GetConsumerAssignment` | Lookup by account/stream/consumer | | `jetStream.consumerAssignmentOrInflight` | 5935-5955 | PARTIAL | `JetStreamMetaGroup.IsConsumerInflight` + `GetConsumerAssignment` | Separate methods but no combined lookup returning the CA from inflight | | `jetStream.consumerAssignmentsOrInflightSeq` | 5959-5986 | MISSING | -- | Iterator over consumer assignments (applied + inflight) for account/stream | | `jsAccount.consumerAssigned` | 5989-5999 | MISSING | -- | Checks if this server has a consumer assigned | | `jetStreamCluster.isConsumerAssigned` | 6003-6025 | MISSING | -- | Checks consumer membership in cluster | | `consumer.streamAndNode` | 6028-6035 | NOT_APPLICABLE | -- | Go-specific accessor for consumer's stream + raft node | | `consumer.replica` | 6039-6049 | NOT_APPLICABLE | -- | Go-specific replica count getter | | `consumer.raftGroup` | 6051-6061 | NOT_APPLICABLE | -- | Go-specific raft group accessor | | `consumer.clearRaftNode` | 6063-6070 | NOT_APPLICABLE | -- | Go-specific raft node clear | | `consumer.raftNode` | 6072-6079 | NOT_APPLICABLE | -- | Go-specific raft node accessor | | `jetStream.monitorConsumer` | 6081-6349 | MISSING | -- | Consumer RAFT monitor loop: apply entries, leader change, snapshot, migration monitoring, compaction | | `jetStream.applyConsumerEntries` | 6351-6531 | PARTIAL | `StreamReplicaGroup.ApplyConsumerEntry` | .NET has simplified Ack/Nak/Deliver/Term/Progress enum tracking but lacks full op decoding (updateDeliveredOp, updateAcksOp, updateSkipOp, resetSeqOp, addPendingRequest, removePendingRequest), snapshot application, peer removal | | `consumer.processReplicatedAck` | 6535-6585 | MISSING | -- | Full replicated ack processing with AckAll gap handling, retention policy | | `decodeAckUpdate` | 6590-6600 | MISSING | -- | Binary uvarint decoder for ack updates | | `decodeDeliveredUpdate` | 6602-6620 | MISSING | -- | Binary uvarint/varint decoder for delivered updates | | `jetStream.processConsumerLeaderChange` | 6622-6698 | MISSING | -- | Consumer leader change: advisory, setLeader, API response, pause advisory | | `consumer.shouldSendLostQuorum` | 6701-6709 | MISSING | -- | Throttled consumer quorum-loss advisory gate | | `Server.sendConsumerLostQuorumAdvisory` | 6711-6745 | MISSING | -- | Consumer lost-quorum advisory publication | | `Server.sendConsumerLeaderElectAdvisory` | 6747-6777 | MISSING | -- | Consumer leader-elected advisory publication | | `streamAssignmentResult` (struct) | 6779-6785 | MISSING | -- | Result struct for stream assignment error forwarding | | `isInsufficientResourcesErr` | 6788-6790 | MISSING | -- | Error classification helper | | `jetStream.processStreamAssignmentResults` | 6794-6870 | MISSING | -- | Stream assignment error result processing with retry across clusters | | `jetStream.processConsumerAssignmentResults` | 6872-6906 | MISSING | -- | Consumer assignment error result processing | | `streamAssignmentSubj` / `consumerAssignmentSubj` (consts) | 6908-6911 | MISSING | -- | System subject constants for assignment results | | `jetStream.startUpdatesSub` | 6914-6937 | MISSING | -- | Subscribe to system subjects for assignment results, stepdown, peer remove, stream move | | `jetStream.stopUpdatesSub` | 6940-6970 | MISSING | -- | Unsubscribe from system subjects | | `Server.sendDomainLeaderElectAdvisory` | 6972-6999 | MISSING | -- | Domain leader-elected advisory publication | | `jetStream.processLeaderChange` (meta) | 7001-7074 | PARTIAL | `JetStreamMetaGroup.ProcessLeaderChange` | .NET clears inflight and fires event; lacks server atomic update, startUpdatesSub/stopUpdatesSub, observer stepdown, streamsCheck fix | | `jetStreamCluster.remapStreamAssignment` | 7077-7111 | PORTED | `JetStreamMetaGroup.RemapStreamAssignment` | .NET implements retain/replace/shrink logic | | `selectPeerError` (struct + methods) | 7113-7207 | MISSING | -- | Detailed peer-selection error reporting with tag/storage/offline reasons | | `jetStreamCluster.selectPeerGroup` | 7212-7507 | PARTIAL | `PlacementEngine.SelectPeerGroup` | .NET has tag filtering, cluster affinity, unique-tag, HA limit, weighted scoring; lacks per-peer storage availability check against MaxBytes, peerStreams/peerHA counting from live assignments, quorum check for online peers, detailed error reporting | | `groupNameForStream` | 7509-7511 | MISSING | -- | Stream raft group name generator with hash | | `groupNameForConsumer` | 7513-7515 | MISSING | -- | Consumer raft group name generator with hash | | `groupName` | 7517-7520 | MISSING | -- | Group name helper: prefix-R{n}{storage}-{hash} | | `jetStream.tieredStreamAndReservationCount` | 7524-7543 | MISSING | -- | Account-tier stream count + storage reservation tracking | | `jetStream.createGroupForStream` | 7547-7576 | MISSING | -- | Creates raft group for new stream with multi-cluster fallback | | `Account.selectLimits` | 7578-7596 | MISSING | -- | Selects account tier limits by replica count | | `jetStream.jsClusteredStreamLimitsCheck` | 7599-7618 | MISSING | -- | Pre-proposal stream limits validation (max streams, account limits) | | `Server.jsClusteredStreamRequest` | 7620-7701 | PARTIAL | `ClusteredRequestProcessor` | .NET has request-id correlation + timeout pattern; lacks full stream config validation, subject overlap check, placement + group creation, meta propose | | `sysRequest[T]` | 7710-7755 | MISSING | -- | Generic blocking system-account request utility | | `Server.jsClusteredStreamUpdateRequest` | 7757-8000+ | MISSING | -- | Full clustered stream update: config validation, move request handling, replica scaling, consumer remapping | | `lostQuorumAdvInterval` (const) | 4368 | MISSING | -- | 10-second throttle interval for quorum-loss advisories | | Status | Count | | PORTED | 4 | | PARTIAL | 14 | | MISSING | 38 | | NOT_APPLICABLE | 5 | | **Total** | **61** | | `Cluster/JetStreamMetaGroup.cs` | Meta-group assignment tracking, proposals, inflight, peer mgmt | | `Cluster/StreamReplicaGroup.cs` | Per-stream RAFT group model, propose/apply/stepdown, msg ops | | `Cluster/PlacementEngine.cs` | Topology-aware peer selection (selectPeerGroup) | | `Cluster/ClusterAssignmentTypes.cs` | RaftGroup, StreamAssignment, ConsumerAssignment types | | `Cluster/AssignmentCodec.cs` | JSON encode/decode for assignments, S2 compression | | `Cluster/MetaSnapshotCodec.cs` | Binary meta snapshot encode/decode | | `Cluster/JetStreamClusterMonitor.cs` | Background loop consuming RAFT entries, dispatching | | `Cluster/AssetPlacementPlanner.cs` | Simple replica count planning | | `Api/Handlers/StreamApiHandlers.cs` | Clustered stream create/update/delete handlers | | `Api/Handlers/ConsumerApiHandlers.cs` | Clustered consumer create/delete handlers | | `Api/Handlers/ClusterControlApiHandlers.cs` | Meta stepdown, stream/consumer leader stepdown | | `Api/ClusteredRequestProcessor.cs` | Pending request correlation (propose + wait) | | `Snapshots/StreamSnapshotService.cs` | TAR-based snapshot create/restore | | `jsClusteredStreamUpdateRequest` (tail) | jetstream_cluster.go:7757 (8001–8138) | PARTIAL | `StreamApiHandlers.HandleClusteredUpdateAsync` | Go version handles scale up/down, move requests, peer set reassignment, consumer migration. .NET version only does simple config update via `ProcessUpdateStreamAssignment`. Missing: scale up/down logic, move request handling, consumer peer reassignment. | | `jsClusteredStreamDeleteRequest` | jetstream_cluster.go:8140–8165 | PORTED | `StreamApiHandlers.HandleClusteredDeleteAsync` | Proposes deletion to meta group, validates existence first. | | `jsClusteredStreamPurgeRequest` | jetstream_cluster.go:8168–8212 | PARTIAL | `StreamApiHandlers.HandlePurge` (local) | Go proposes purge through RAFT when clustered (stream group node). .NET has local purge only, no RAFT proposal path for purge. | | `jsClusteredStreamRestoreRequest` | jetstream_cluster.go:8214–8262 | PARTIAL | `StreamApiHandlers.HandleRestore` + `StreamSnapshotService` | Go creates a new RAFT group, sets preferred leader, proposes as stream assignment with Restore state. .NET has local restore path but no RAFT-based clustered restore proposal. | | `allPeersOffline` | jetstream_cluster.go:8265–8278 | MISSING | — | Server-level helper to check if all peers in a RAFT group are offline. No nodeToInfo equivalent in .NET. | | `jsClusteredStreamListRequest` | jetstream_cluster.go:8282–8444 | PARTIAL | `JetStreamApiRouter` routes to `StreamApiHandlers.HandleList` | Go does scatter-gather across cluster (sends internal msgs to each stream peer, collects responses, handles timeouts, offline peers, missing names). .NET has local-only list from StreamManager. | | `jsClusteredConsumerListRequest` | jetstream_cluster.go:8448–8591 | PARTIAL | `JetStreamApiRouter` routes to `ConsumerApiHandlers.HandleList` | Same scatter-gather pattern as stream list. .NET has local-only consumer list. | | `encodeStreamPurge` | jetstream_cluster.go:8593–8598 | MISSING | — | Binary encode of streamPurge for RAFT proposal. | | `decodeStreamPurge` | jetstream_cluster.go:8600–8604 | MISSING | — | Binary decode of streamPurge from RAFT log. | | `jsClusteredConsumerDeleteRequest` | jetstream_cluster.go:8606–8643 | PORTED | `ConsumerApiHandlers.HandleClusteredDeleteAsync` | Proposes consumer deletion via meta RAFT group. | | `encodeMsgDelete` | jetstream_cluster.go:8645–8650 | MISSING | — | Binary encode of streamMsgDelete for RAFT proposal. | | `decodeMsgDelete` | jetstream_cluster.go:8652–8656 | MISSING | — | Binary decode of streamMsgDelete from RAFT log. | | `jsClusteredMsgDeleteRequest` | jetstream_cluster.go:8658–8701 | MISSING | — | Clustered message delete by sequence — proposes through stream RAFT node. No .NET equivalent. | | `encodeAddStreamAssignment` | jetstream_cluster.go:8703–8711 | PORTED | `AssignmentCodec.EncodeStreamAssignment` | JSON serialization of stream assignment. | | `encodeUpdateStreamAssignment` | jetstream_cluster.go:8713–8721 | PARTIAL | `AssignmentCodec.EncodeStreamAssignment` | Go uses a separate opcode byte (updateStreamOp). .NET uses same encoder without op-code prefix. | | `encodeDeleteStreamAssignment` | jetstream_cluster.go:8723–8731 | PARTIAL | `AssignmentCodec.EncodeStreamAssignment` | Go uses removeStreamOp opcode byte. .NET lacks op-code prefix differentiation. | | `decodeStreamAssignment` | jetstream_cluster.go:8733–8742 | PORTED | `AssignmentCodec.DecodeStreamAssignment` | JSON deserialization of stream assignment. | | `decodeStreamAssignmentConfig` | jetstream_cluster.go:8744–8764 | PARTIAL | `AssignmentCodec.DecodeStreamAssignment` | Go does DisallowUnknownFields for forward-compat (marks as unsupported). .NET does basic decode without unsupported field detection. | | `encodeDeleteRange` | jetstream_cluster.go:8766–8771 | MISSING | — | Binary encode of DeleteRange for catchup gap markers. | | `decodeDeleteRange` | jetstream_cluster.go:8773–8780 | MISSING | — | Binary decode of DeleteRange. | | `createGroupForConsumer` | jetstream_cluster.go:8783–8821 | PARTIAL | `PlacementEngine.SelectPeerGroup` | Go selects peers from stream's group, checks active/offline, enforces quorum, picks storage type. .NET PlacementEngine does general peer selection but not the specific consumer-from-stream-peers logic. | | `jsClusteredConsumerRequest` | jetstream_cluster.go:8824–9173 | PARTIAL | `ConsumerApiHandlers.HandleClusteredCreateAsync` | Go has full validation: account limits, maxConsumers check, WQ policy checks, ephemeral naming, HA asset limits, scale up/down, consumer state transfer. .NET does basic leader + stream-exists validation only. | | `encodeAddConsumerAssignment` | jetstream_cluster.go:9175–9183 | PORTED | `AssignmentCodec.EncodeConsumerAssignment` | JSON serialization of consumer assignment. | | `encodeDeleteConsumerAssignment` | jetstream_cluster.go:9185–9193 | PARTIAL | `AssignmentCodec.EncodeConsumerAssignment` | Go uses removeConsumerOp opcode. .NET lacks op-code differentiation. | | `decodeConsumerAssignment` | jetstream_cluster.go:9195–9204 | PORTED | `AssignmentCodec.DecodeConsumerAssignment` | JSON deserialization of consumer assignment. | | `decodeConsumerAssignmentConfig` | jetstream_cluster.go:9206–9224 | PARTIAL | `AssignmentCodec.DecodeConsumerAssignment` | Go does unsupported-field detection + API level check. .NET does basic decode. | | `encodeAddConsumerAssignmentCompressed` | jetstream_cluster.go:9226–9236 | PORTED | `AssignmentCodec.CompressIfLarge` | S2/Snappy compression for large consumer assignments. | | `decodeConsumerAssignmentCompressed` | jetstream_cluster.go:9238–9250 | PORTED | `AssignmentCodec.DecompressIfNeeded` | Snappy decompression for compressed consumer assignments. | | `decodeStreamMsg` | jetstream_cluster.go:9254–9309 | MISSING | — | Binary wire decode of replicated stream messages (seq, ts, subject, reply, hdr, msg, flags). Critical for RAFT replication. | | `decodeBatchMsg` | jetstream_cluster.go:9311–9332 | MISSING | — | Binary decode of batch message wrapper (batchId, batchSeq, inner op). | | `encodeStreamMsg` | jetstream_cluster.go:9339–9341 | MISSING | — | Binary wire encode of replicated stream messages. | | `encodeStreamMsgAllowCompress` | jetstream_cluster.go:9343–9345 | MISSING | — | Wrapper calling encode with compression allowed. | | `encodeStreamMsgAllowCompressAndBatch` | jetstream_cluster.go:9352–9421 | MISSING | — | Full binary encoder with S2 compression (threshold 8KB), batch support, source/mirror flags. Core of clustered message replication. | | `compressThreshold` (const) | jetstream_cluster.go:9349 | MISSING | — | 8192-byte threshold for message compression. | | `msgFlagFromSourceOrMirror` (const) | jetstream_cluster.go:9336 | MISSING | — | Flag constant for source/mirror tracking. | | `supportsBinarySnapshot` | jetstream_cluster.go:9424–9428 | MISSING | — | Checks if all peers support binary snapshot format. | | `supportsBinarySnapshotLocked` | jetstream_cluster.go:9432–9450 | MISSING | — | Lock-held version checking peer nodeInfo for binary snapshot support. | | `streamSnapshot` (struct) | jetstream_cluster.go:9454–9461 | MISSING | — | Legacy JSON snapshot type (v1 format with deleted[]uint64). | | `stateSnapshot` | jetstream_cluster.go:9464–9468 | MISSING | — | Creates binary/legacy snapshot for RAFT. .NET has `StreamSnapshotService` but it's TAR-based, not the RAFT binary snapshot format. | | `stateSnapshotLocked` | jetstream_cluster.go:9472–9501 | MISSING | — | Lock-held snapshot with binary vs legacy fallback and EncodedStreamState. | | `processClusteredInboundMsg` | jetstream_cluster.go:9507–9690 | MISSING | — | Proposes inbound published message to stream RAFT group. Core of clustered publish path: leader check, sealed check, limits, dedup, lag warning, message trace. | | `streamLagWarnThreshold` (const) | jetstream_cluster.go:9504 | MISSING | — | 10,000 threshold for lag warning. | | `getAndDeleteMsgTrace` | jetstream_cluster.go:9692–9703 | MISSING | — | Retrieves and deletes a message trace by sequence. | | `streamSyncRequest` (struct) | jetstream_cluster.go:9707–9713 | MISSING | — | Request structure for stream catchup (Peer, FirstSeq, LastSeq, DeleteRangesOk, MinApplied). | | `calculateSyncRequest` | jetstream_cluster.go:9717–9728 | MISSING | — | Calculates catchup request based on local state vs snapshot. | | `processSnapshotDeletes` | jetstream_cluster.go:9732–9756 | MISSING | — | Processes deletes from snapshot (Compact, SyncDeleted). | | `setCatchupPeer` | jetstream_cluster.go:9758–9768 | MISSING | — | Sets catchup peer with lag tracking. | | `updateCatchupPeer` | jetstream_cluster.go:9771–9780 | MISSING | — | Decrements catchup lag by one. | | `decrementCatchupPeer` | jetstream_cluster.go:9782–9796 | MISSING | — | Decrements catchup lag by N. | | `clearCatchupPeer` | jetstream_cluster.go:9798–9804 | MISSING | — | Clears a single catchup peer. | | `clearAllCatchupPeers` | jetstream_cluster.go:9807–9811 | MISSING | — | Clears all catchup peers. | | `lagForCatchupPeer` | jetstream_cluster.go:9813–9820 | MISSING | — | Returns lag for a specific catchup peer. | | `hasCatchupPeers` | jetstream_cluster.go:9822–9826 | MISSING | — | Returns true if any catchup peers exist. | | `setCatchingUp` | jetstream_cluster.go:9828–9830 | MISSING | — | Atomic bool: set catching up. | | `clearCatchingUp` | jetstream_cluster.go:9832–9834 | MISSING | — | Atomic bool: clear catching up. | | `isCatchingUp` | jetstream_cluster.go:9836–9838 | MISSING | — | Atomic bool: is catching up. | | `isCurrent` | jetstream_cluster.go:9842–9847 | MISSING | — | Check if non-leader stream is current (RAFT current + not catching up). | | `maxConcurrentSyncRequests` (const) | jetstream_cluster.go:9850 | MISSING | — | 32 max concurrent sync requests. | | Error sentinel vars (catchup errors) | jetstream_cluster.go:9852–9860 | MISSING | — | errCatchupCorruptSnapshot, errCatchupStalled, errCatchupStreamStopped, etc. | | `processSnapshot` | jetstream_cluster.go:9863–10000+ | MISSING | — | Full stream snapshot processing: delete sync, calculate sync request, pause RAFT apply, semaphore-limited catchup with retries (max 3). ~140 lines. | | `processCatchupMsg` | jetstream_cluster.go:10160–10251 | MISSING | — | Processes out-of-band catchup messages: deleteRange handling, S2 decompression, preAck clearing, store writes, dedup tracking. | | `flushAllPending` | jetstream_cluster.go:10254–10256 | MISSING | — | Flush pending writes after snapshot install. | | `handleClusterSyncRequest` | jetstream_cluster.go:10258–10265 | MISSING | — | Handler for inbound cluster sync requests; starts runCatchup goroutine. | | `offlineClusterInfo` | jetstream_cluster.go:10268–10280 | MISSING | — | Returns ClusterInfo for offline RAFT group (all replicas marked offline). | | `clusterInfo` | jetstream_cluster.go:10283–10355 | MISSING | — | Returns full ClusterInfo for RAFT group: leader, replicas, lag, current status, sorted by name. Central to /jsz and stream info responses. | | `checkClusterInfo` | jetstream_cluster.go:10357–10365 | MISSING | — | Adjusts ClusterInfo replicas for catchup lag. | | `streamAlternates` | jetstream_cluster.go:10370–10420 | MISSING | — | Returns ranked list of stream mirrors/alternates for client connection. | | `handleClusterStreamInfoRequest` | jetstream_cluster.go:10423–10425 | MISSING | — | Inbound handler for cluster stream info (dispatches to processClusterStreamInfoRequest). | | `processClusterStreamInfoRequest` | jetstream_cluster.go:10427–10460 | MISSING | — | Processes cluster stream info request: gathers state, config, cluster info, sources, mirror. | | `defaultMaxTotalCatchupOutBytes` (const) | jetstream_cluster.go:10465 | MISSING | — | 64 MB max total server-wide catchup outbound bytes. | | `gcbTotal` | jetstream_cluster.go:10468–10472 | MISSING | — | Returns total outstanding catchup bytes. | | `gcbBelowMax` | jetstream_cluster.go:10476–10480 | MISSING | — | Returns true if catchup bytes below max. | | `gcbAdd` | jetstream_cluster.go:10485–10493 | MISSING | — | Adds to server-wide and local catchup byte counters. | | `gcbSubLocked` | jetstream_cluster.go:10499–10509 | MISSING | — | Subtracts from catchup byte counters (locked). | | `gcbSub` | jetstream_cluster.go:10512–10516 | MISSING | — | Locking wrapper for gcbSubLocked. | | `gcbSubLast` | jetstream_cluster.go:10522–10527 | MISSING | — | Final subtract + zero out local counter on catchup exit. | | `cbKickChan` | jetstream_cluster.go:10530–10534 | MISSING | — | Returns kick channel for catchup bandwidth management. | | `runCatchup` | jetstream_cluster.go:10536–10846 | MISSING | — | Leader-side catchup sender: flow control, batch sending, ack tracking, delete ranges, gap markers, activity timeout, global bandwidth cap. ~310 lines. | | `syncSubjForStream` | jetstream_cluster.go:10850–10852 | MISSING | — | Returns sync subject for stream catchup. | | `syncReplySubject` | jetstream_cluster.go:10854–10856 | MISSING | — | Returns reply subject for sync. | | `infoReplySubject` | jetstream_cluster.go:10858–10860 | MISSING | — | Returns reply subject for info. | | `syncAckSubject` | jetstream_cluster.go:10862–10864 | MISSING | — | Returns ack subject for sync flow control. | | `syncSubject` | jetstream_cluster.go:10866–10880 | MISSING | — | Generates random sync subject with base encoding. | | `clusterStreamInfoT` (const) | jetstream_cluster.go:10883 | MISSING | — | Subject template for cluster stream info requests. | | `clusterConsumerInfoT` (const) | jetstream_cluster.go:10884 | MISSING | — | Subject template for cluster consumer info requests. | | `jsaUpdatesSubT` / `jsaUpdatesPubT` (consts) | jetstream_cluster.go:10885–10886 | MISSING | — | Subject templates for JSC account-related update subscriptions. | | `jscAllSubj` (const) | jetstream_cluster.go:10848 | MISSING | — | `$JSC.>` wildcard subscription for all cluster subjects. | | Status | Count | | PORTED | 8 | | PARTIAL | 13 | | MISSING | 56 | | NOT_APPLICABLE | 0 | | DEFERRED | 0 | | **Total** | **77** | ## Change Log | Date | Change | By | |------|--------|----| | 2026-02-25 | JS-2a: store.go full + stream.go lines 1-4000. 172 symbols. PORTED:55 PARTIAL:30 MISSING:75 NA:12 | opus | | 2026-02-25 | JS-2b: stream.go lines 4001-end. 116 symbols. PORTED:11 PARTIAL:24 MISSING:73 NA:8 | opus | | 2026-02-25 | JS-3a: consumer.go lines 1-3500. 115 symbols. PORTED:23 PARTIAL:21 MISSING:70 NA:1 | opus | | 2026-02-25 | JS-3b: consumer.go lines 3501-end. 105 symbols. PORTED:31 PARTIAL:28 MISSING:37 NA:9 | opus | | 2026-02-25 | JS-4a: memstore + filestore 1-4000. 140 symbols. PORTED:37 PARTIAL:27 MISSING:71 NA:5 | opus | | 2026-02-25 | JS-4b: filestore 4001-7000. 80 symbols. PORTED:14 PARTIAL:27 MISSING:37 NA:2 | opus | | 2026-02-25 | JS-4c: filestore 7001-10000. 82 symbols. PORTED:26 PARTIAL:19 MISSING:32 NA:5 | opus | | 2026-02-25 | JS-4d: filestore 10001-end + dirstore. 106 symbols. PORTED:22 PARTIAL:23 MISSING:57 NA:4 | opus | | 2026-02-25 | JS-5a: jetstream_cluster.go lines 1-4000. 91 symbols. PORTED:25 PARTIAL:14 MISSING:48 NA:4 | opus | | 2026-02-25 | JS-5b: jetstream_cluster.go lines 4001-8000. 61 symbols. PORTED:4 PARTIAL:14 MISSING:38 NA:5 | opus | | 2026-02-25 | JS-5c: jetstream_cluster.go lines 8001-end. 77 symbols. PORTED:8 PARTIAL:13 MISSING:56 | opus | | 2026-02-25 | File created with LLM analysis instructions | auto | | 2026-02-25 | JS-1 Core sub-pass: analyzed jetstream.go, jetstream_api.go, jetstream_events.go, jetstream_errors.go, jetstream_versioning.go, jetstream_batching.go. 150+ symbols inventoried. | opus | | 2026-02-25 | JS core config parity batch: added JetStream default constants (`JetStreamStoreDir`, max store/mem defaults) and server accessors (`JetStreamEnabled`, `JetStreamConfig`, `StoreDir`) with focused tests | codex | | 2026-02-26 | JS config/model parity batch: extended `JetStreamOptions` (sync/compress/strict/unique-tag and account-limit fields), added parser support for extended jetstream config keys, and added core parity models (`JetStreamStats`, `JetStreamApiStats`, `JetStreamAccountLimits`, `JetStreamTier`) with focused tests | codex |