- Fix pull consumer fetch: send original stream subject in HMSG (not inbox) so NATS client distinguishes data messages from control messages - Fix MaxAge expiry: add background timer in StreamManager for periodic pruning - Fix JetStream wire format: Go-compatible anonymous objects with string enums, proper offset-based pagination for stream/consumer list APIs - Add 42 E2E black-box tests (core messaging, auth, TLS, accounts, JetStream) - Add ~1000 parity tests across all subsystems (gaps closure) - Update gap inventory docs to reflect implementation status
291 lines
25 KiB
Markdown
291 lines
25 KiB
Markdown
# Configuration — Gap Analysis
|
||
|
||
> This file tracks what has and hasn't been ported from Go to .NET for the **Configuration** 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 Configuration
|
||
|
||
- .NET LOC **exceeds** Go LOC (4,559 vs 1,871) because .NET implements a full lexer/parser/token pipeline.
|
||
- The Go config format is a custom format (not JSON, not YAML). It supports includes, variables, and block structures.
|
||
- `opts.go` (in Core Server category) also participates in config — it maps CLI flags + config file to the options struct.
|
||
- Hot reload (`reload.go` in Core Server) depends on config diffing — ensure the .NET config model supports this.
|
||
|
||
---
|
||
|
||
## Go Reference Files (Source)
|
||
|
||
- `golang/nats-server/conf/lex.go` — Lexer for NATS config file format
|
||
- `golang/nats-server/conf/parse.go` — Parser for config files (supports includes, variables)
|
||
- `golang/nats-server/conf/token.go` — Token types for config lexer
|
||
|
||
## Go Reference Files (Tests)
|
||
|
||
- `golang/nats-server/conf/lex_test.go`
|
||
- `golang/nats-server/conf/parse_test.go`
|
||
- `golang/nats-server/server/config_check_test.go`
|
||
|
||
## .NET Implementation Files (Source)
|
||
|
||
- `src/NATS.Server/Configuration/NatsConfLexer.cs`
|
||
- `src/NATS.Server/Configuration/NatsConfParser.cs`
|
||
- `src/NATS.Server/Configuration/NatsConfToken.cs`
|
||
- `src/NATS.Server/Configuration/ConfigProcessor.cs`
|
||
- `src/NATS.Server/Configuration/ConfigReloader.cs`
|
||
- All other files in `src/NATS.Server/Configuration/`
|
||
|
||
## .NET Implementation Files (Tests)
|
||
|
||
- `tests/NATS.Server.Tests/Configuration/`
|
||
|
||
---
|
||
|
||
## Gap Inventory
|
||
|
||
<!-- After analysis, fill in this table. Group rows by Go source file. -->
|
||
|
||
### golang/nats-server/conf/lex.go
|
||
|
||
| Go Symbol | Go File:Line | Status | .NET Equivalent | Notes |
|
||
|-----------|:-------------|--------|:----------------|-------|
|
||
| `itemType` (type alias `int`) | `conf/lex.go:37` | PORTED | `src/NATS.Server/Configuration/NatsConfToken.cs:5` | Ported as `TokenType` enum |
|
||
| `itemError` constant | `conf/lex.go:40` | PORTED | `NatsConfToken.cs:7` | `TokenType.Error` |
|
||
| `itemNIL` constant | `conf/lex.go:41` | NOT_APPLICABLE | — | Go-only sentinel used in parser internal state; .NET uses `Token` default struct instead |
|
||
| `itemEOF` constant | `conf/lex.go:42` | PORTED | `NatsConfToken.cs:8` | `TokenType.Eof` |
|
||
| `itemKey` constant | `conf/lex.go:43` | PORTED | `NatsConfToken.cs:9` | `TokenType.Key` |
|
||
| `itemText` constant | `conf/lex.go:44` | PORTED | `NatsConfToken.cs:10` | Added `TokenType.Text` and emit comment body text tokens in lexer comment state. |
|
||
| `itemString` constant | `conf/lex.go:45` | PORTED | `NatsConfToken.cs:10` | `TokenType.String` |
|
||
| `itemBool` constant | `conf/lex.go:46` | PORTED | `NatsConfToken.cs:11` | `TokenType.Bool` |
|
||
| `itemInteger` constant | `conf/lex.go:47` | PORTED | `NatsConfToken.cs:12` | `TokenType.Integer` |
|
||
| `itemFloat` constant | `conf/lex.go:48` | PORTED | `NatsConfToken.cs:13` | `TokenType.Float` |
|
||
| `itemDatetime` constant | `conf/lex.go:49` | PORTED | `NatsConfToken.cs:14` | `TokenType.DateTime` |
|
||
| `itemArrayStart` constant | `conf/lex.go:50` | PORTED | `NatsConfToken.cs:15` | `TokenType.ArrayStart` |
|
||
| `itemArrayEnd` constant | `conf/lex.go:51` | PORTED | `NatsConfToken.cs:16` | `TokenType.ArrayEnd` |
|
||
| `itemMapStart` constant | `conf/lex.go:52` | PORTED | `NatsConfToken.cs:17` | `TokenType.MapStart` |
|
||
| `itemMapEnd` constant | `conf/lex.go:53` | PORTED | `NatsConfToken.cs:18` | `TokenType.MapEnd` |
|
||
| `itemCommentStart` constant | `conf/lex.go:54` | PORTED | `NatsConfToken.cs:22` | .NET now emits `TokenType.Comment` followed by `TokenType.Text` for comment body, matching Go token stream semantics. |
|
||
| `itemVariable` constant | `conf/lex.go:55` | PORTED | `NatsConfToken.cs:19` | `TokenType.Variable` |
|
||
| `itemInclude` constant | `conf/lex.go:56` | PORTED | `NatsConfToken.cs:20` | `TokenType.Include` |
|
||
| `stateFn` type | `conf/lex.go:84` | PORTED | `NatsConfLexer.cs:30` | Ported as `delegate LexState? LexState(NatsConfLexer lx)` — identical functional model |
|
||
| `lexer` struct | `conf/lex.go:86` | PORTED | `NatsConfLexer.cs:6` | All fields: `_input`, `_start`, `_pos`, `_width`, `_line`, `_stack`, `_stringParts`, `_stringStateFn`, `_lstart`, `_ilstart` |
|
||
| `item` struct | `conf/lex.go:113` | PORTED | `NatsConfToken.cs:24` | Ported as `readonly record struct Token(TokenType Type, string Value, int Line, int Position)` |
|
||
| `(lx *lexer) nextItem()` | `conf/lex.go:120` | PORTED | `NatsConfLexer.cs:62` | Go uses a goroutine channel; .NET runs state machine eagerly in `Tokenize()` collecting all tokens into a `List<Token>` — semantically equivalent |
|
||
| `lex(input string)` | `conf/lex.go:131` | PORTED | `NatsConfLexer.cs:48` | Ported as private constructor + `Tokenize()` factory |
|
||
| `(lx *lexer) push()` | `conf/lex.go:143` | PORTED | `NatsConfLexer.cs:75` | `Push()` using `Stack<LexState>` |
|
||
| `(lx *lexer) pop()` | `conf/lex.go:147` | PORTED | `NatsConfLexer.cs:77` | `Pop()` |
|
||
| `(lx *lexer) emit()` | `conf/lex.go:157` | PORTED | `NatsConfLexer.cs:87` | `Emit()` — identical logic for computing position and clearing stringParts |
|
||
| `(lx *lexer) emitString()` | `conf/lex.go:166` | PORTED | `NatsConfLexer.cs:106` | `EmitString()` |
|
||
| `(lx *lexer) addCurrentStringPart()` | `conf/lex.go:181` | PORTED | `NatsConfLexer.cs:125` | `AddCurrentStringPart()` |
|
||
| `(lx *lexer) addStringPart()` | `conf/lex.go:186` | PORTED | `NatsConfLexer.cs:131` | `AddStringPart()` |
|
||
| `(lx *lexer) hasEscapedParts()` | `conf/lex.go:192` | PORTED | `NatsConfLexer.cs:138` | `HasEscapedParts()` |
|
||
| `(lx *lexer) next()` | `conf/lex.go:196` | PORTED | `NatsConfLexer.cs:144` | Updated lexer read path to decode UTF-16 runes (`Rune.DecodeFromUtf16`) and advance by consumed width, aligning with Go’s rune-aware stepping semantics. |
|
||
| `(lx *lexer) ignore()` | `conf/lex.go:215` | PORTED | `NatsConfLexer.cs:160` | `Ignore()` |
|
||
| `(lx *lexer) backup()` | `conf/lex.go:221` | PORTED | `NatsConfLexer.cs:166` | `Backup()` |
|
||
| `(lx *lexer) peek()` | `conf/lex.go:229` | PORTED | `NatsConfLexer.cs:175` | `Peek()` |
|
||
| `(lx *lexer) errorf()` | `conf/lex.go:238` | PORTED | `NatsConfLexer.cs:193` | Added formatted `Errorf(string, params object?[])` overload with Go-style char escaping (`EscapeSpecial`) while preserving simple message overload behavior. |
|
||
| `lexTop` | `conf/lex.go:257` | PORTED | `NatsConfLexer.cs:202` | `LexTop` static method |
|
||
| `lexTopValueEnd` | `conf/lex.go:296` | PORTED | `NatsConfLexer.cs:247` | `LexTopValueEnd` static method |
|
||
| `lexBlockStart` | `conf/lex.go:321` | PORTED | `NatsConfLexer.cs:291` | `LexBlockStart` static method |
|
||
| `lexBlockValueEnd` | `conf/lex.go:363` | PORTED | `NatsConfLexer.cs:338` | `LexBlockValueEnd` static method |
|
||
| `lexBlockEnd` | `conf/lex.go:392` | PORTED | `NatsConfLexer.cs:388` | `LexBlockEnd` static method |
|
||
| `lexKeyStart` | `conf/lex.go:422` | PORTED | `NatsConfLexer.cs:438` | `LexKeyStart` static method |
|
||
| `lexDubQuotedKey` | `conf/lex.go:443` | PORTED | `NatsConfLexer.cs:469` | `LexDubQuotedKey` static method |
|
||
| `lexQuotedKey` | `conf/lex.go:460` | PORTED | `NatsConfLexer.cs:494` | `LexQuotedKey` static method |
|
||
| `(lx *lexer) keyCheckKeyword()` | `conf/lex.go:480` | PORTED | `NatsConfLexer.cs:519` | `KeyCheckKeyword()` — instance method |
|
||
| `lexIncludeStart` | `conf/lex.go:495` | PORTED | `NatsConfLexer.cs:537` | `LexIncludeStart` static method |
|
||
| `lexIncludeQuotedString` | `conf/lex.go:507` | PORTED | `NatsConfLexer.cs:549` | `LexIncludeQuotedString` static method |
|
||
| `lexIncludeDubQuotedString` | `conf/lex.go:525` | PORTED | `NatsConfLexer.cs:569` | `LexIncludeDubQuotedString` static method |
|
||
| `lexIncludeString` | `conf/lex.go:541` | PORTED | `NatsConfLexer.cs:589` | `LexIncludeString` static method |
|
||
| `lexInclude` | `conf/lex.go:559` | PORTED | `NatsConfLexer.cs:611` | `LexInclude` static method |
|
||
| `lexKey` | `conf/lex.go:587` | PORTED | `NatsConfLexer.cs:646` | `LexKey` static method |
|
||
| `lexKeyEnd` | `conf/lex.go:601` | PORTED | `NatsConfLexer.cs:671` | `LexKeyEnd` static method |
|
||
| `lexValue` | `conf/lex.go:621` | PORTED | `NatsConfLexer.cs:695` | `LexValue` static method |
|
||
| `lexArrayValue` | `conf/lex.go:665` | PORTED | `NatsConfLexer.cs:745` | `LexArrayValue` static method |
|
||
| `lexArrayValueEnd` | `conf/lex.go:696` | PORTED | `NatsConfLexer.cs:788` | `LexArrayValueEnd` static method |
|
||
| `lexArrayEnd` | `conf/lex.go:723` | PORTED | `NatsConfLexer.cs:832` | `LexArrayEnd` static method |
|
||
| `lexMapKeyStart` | `conf/lex.go:732` | PORTED | `NatsConfLexer.cs:839` | `LexMapKeyStart` static method |
|
||
| `lexMapQuotedKey` | `conf/lex.go:772` | PORTED | `NatsConfLexer.cs:906` | `LexMapQuotedKey` static method |
|
||
| `lexMapDubQuotedKey` | `conf/lex.go:784` | PORTED | `NatsConfLexer.cs:925` | `LexMapDubQuotedKey` static method |
|
||
| `lexMapKey` | `conf/lex.go:799` | PORTED | `NatsConfLexer.cs:944` | `LexMapKey` static method |
|
||
| `lexMapKeyEnd` | `conf/lex.go:815` | PORTED | `NatsConfLexer.cs:972` | `LexMapKeyEnd` static method |
|
||
| `lexMapValue` | `conf/lex.go:833` | PORTED | `NatsConfLexer.cs:990` | `LexMapValue` static method |
|
||
| `lexMapValueEnd` | `conf/lex.go:850` | PORTED | `NatsConfLexer.cs:1013` | `LexMapValueEnd` static method |
|
||
| `lexMapEnd` | `conf/lex.go:875` | PORTED | `NatsConfLexer.cs:1059` | `LexMapEnd` static method |
|
||
| `(lx *lexer) isBool()` | `conf/lex.go:884` | PORTED | `NatsConfLexer.cs:1066` | `IsBool()` instance method |
|
||
| `(lx *lexer) isVariable()` | `conf/lex.go:892` | PORTED | `NatsConfLexer.cs:1072` | `IsVariable()` instance method |
|
||
| `lexQuotedString` | `conf/lex.go:906` | PORTED | `NatsConfLexer.cs:1088` | `LexQuotedString` static method |
|
||
| `lexDubQuotedString` | `conf/lex.go:928` | PORTED | `NatsConfLexer.cs:1114` | `LexDubQuotedString` static method |
|
||
| `lexString` | `conf/lex.go:951` | PORTED | `NatsConfLexer.cs:1146` | `LexString` static method |
|
||
| `lexBlock` | `conf/lex.go:986` | PORTED | `NatsConfLexer.cs:1193` | `LexBlock` static method |
|
||
| `lexStringEscape` | `conf/lex.go:1021` | PORTED | `NatsConfLexer.cs:1235` | `LexStringEscape` static method |
|
||
| `lexStringBinary` | `conf/lex.go:1043` | PORTED | `NatsConfLexer.cs:1250` | `LexStringBinary` static method — uses `Convert.FromHexString` + `Latin1.GetString` |
|
||
| `lexNumberOrDateOrStringOrIPStart` | `conf/lex.go:1065` | PORTED | `NatsConfLexer.cs:1276` | `LexNumberOrDateOrStringOrIPStart` static method |
|
||
| `lexNumberOrDateOrStringOrIP` | `conf/lex.go:1079` | PORTED | `NatsConfLexer.cs:1292` | `LexNumberOrDateOrStringOrIP` static method |
|
||
| `lexConvenientNumber` | `conf/lex.go:1106` | PORTED | `NatsConfLexer.cs:1333` | `LexConvenientNumber` static method |
|
||
| `lexDateAfterYear` | `conf/lex.go:1124` | PORTED | `NatsConfLexer.cs:1354` | `LexDateAfterYear` static method |
|
||
| `lexNegNumberStart` | `conf/lex.go:1152` | PORTED | `NatsConfLexer.cs:1385` | `LexNegNumberStart` static method |
|
||
| `lexNegNumber` | `conf/lex.go:1165` | PORTED | `NatsConfLexer.cs:1401` | `LexNegNumber` static method |
|
||
| `lexFloatStart` | `conf/lex.go:1182` | PORTED | `NatsConfLexer.cs:1424` | `LexFloatStart` static method |
|
||
| `lexFloat` | `conf/lex.go:1193` | PORTED | `NatsConfLexer.cs:1435` | `LexFloat` static method |
|
||
| `lexIPAddr` | `conf/lex.go:1210` | PORTED | `NatsConfLexer.cs:1454` | `LexIPAddr` static method |
|
||
| `lexCommentStart` | `conf/lex.go:1222` | PORTED | `NatsConfLexer.cs:1467` | Emits `TokenType.Comment` and transitions into comment-body emission state. |
|
||
| `lexComment` | `conf/lex.go:1231` | PORTED | `NatsConfLexer.cs:1474` | Emits `TokenType.Text` for comment body at end-of-line/EOF, matching Go lexer semantics. |
|
||
| `lexSkip` | `conf/lex.go:1242` | PORTED | `NatsConfLexer.cs:1489` | `LexSkip` static method — identical logic |
|
||
| `isNumberSuffix()` | `conf/lex.go:1250` | PORTED | `NatsConfLexer.cs:197` | `IsNumberSuffix()` static method |
|
||
| `isKeySeparator()` | `conf/lex.go:1255` | PORTED | `NatsConfLexer.cs:195` | `IsKeySeparator()` static method |
|
||
| `isWhitespace()` | `conf/lex.go:1261` | PORTED | `NatsConfLexer.cs:191` | `IsWhitespace()` static method |
|
||
| `isNL()` | `conf/lex.go:1265` | PORTED | `NatsConfLexer.cs:193` | `IsNL()` static method |
|
||
| `(itemType) String()` | `conf/lex.go:1269` | PORTED | — | Go's stringer is used for debugging; .NET `TokenType` is an enum (has `.ToString()` built in) — functionally equivalent |
|
||
| `(item) String()` | `conf/lex.go:1309` | PORTED | — | Go debug helper; .NET `Token` is a record struct with auto-generated `ToString()` |
|
||
| `escapeSpecial()` | `conf/lex.go:1313` | PORTED | `NatsConfLexer.cs:1495` | `EscapeSpecial()` static method — .NET version also handles `\r`, `\t`, and EOF |
|
||
|
||
---
|
||
|
||
### golang/nats-server/conf/parse.go
|
||
|
||
| Go Symbol | Go File:Line | Status | .NET Equivalent | Notes |
|
||
|-----------|:-------------|--------|:----------------|-------|
|
||
| `_EMPTY_` constant | `conf/parse.go:40` | NOT_APPLICABLE | — | Go-only string constant alias for `""` |
|
||
| `parser` struct | `conf/parse.go:42` | PORTED | `src/NATS.Server/Configuration/NatsConfParser.cs:115` | Ported as private `ParserState` class inside `NatsConfParser` with context stacks, key stacks, include depth, token stream, and pedantic key-token compatibility stack (`_itemKeys`). |
|
||
| `Parse()` | `conf/parse.go:71` | PORTED | `NatsConfParser.cs:29` | `NatsConfParser.Parse(string)` — identical signature and semantics |
|
||
| `ParseWithChecks()` | `conf/parse.go:80` | PORTED | `src/NATS.Server/Configuration/NatsConfParser.cs:42` | Added compatibility entry point that delegates to `Parse(...)` in .NET. |
|
||
| `ParseFile()` | `conf/parse.go:89` | PORTED | `NatsConfParser.cs:40` | `NatsConfParser.ParseFile(string)` |
|
||
| `ParseFileWithChecks()` | `conf/parse.go:103` | PORTED | `src/NATS.Server/Configuration/NatsConfParser.cs:54` | Added file-based compatibility entry point that delegates to `ParseFile(...)`. |
|
||
| `cleanupUsedEnvVars()` | `conf/parse.go:119` | PORTED | `src/NATS.Server/Configuration/NatsConfParser.cs:96` | Added compatibility hook; no-op in .NET because digesting is based on raw bytes, not token-tree mutation. |
|
||
| `ParseFileWithChecksDigest()` | `conf/parse.go:135` | PORTED | `src/NATS.Server/Configuration/NatsConfParser.cs:88` | Added pedantic parse+digest path that computes SHA-256 from canonical JSON encoding of parsed config tree (sorted object keys), matching Go's token-tree digest intent rather than raw-file bytes. |
|
||
| `token` struct | `conf/parse.go:155` | PORTED | `src/NATS.Server/Configuration/NatsConfToken.cs:32` (`PedanticToken`) | Added pedantic token wrapper with value/line/position/used-variable/source-file metadata accessors. |
|
||
| `(t *token) MarshalJSON()` | `conf/parse.go:162` | PORTED | `src/NATS.Server/Configuration/NatsConfToken.cs:47` | Added `PedanticToken.MarshalJson()` using `System.Text.Json`. |
|
||
| `(t *token) Value()` | `conf/parse.go:166` | PORTED | `src/NATS.Server/Configuration/NatsConfToken.cs:49` | Added `PedanticToken.Value()`. |
|
||
| `(t *token) Line()` | `conf/parse.go:170` | PORTED | `src/NATS.Server/Configuration/NatsConfToken.cs:51` | Added `PedanticToken.Line()`. |
|
||
| `(t *token) IsUsedVariable()` | `conf/parse.go:174` | PORTED | `src/NATS.Server/Configuration/NatsConfToken.cs:53` | Added `PedanticToken.IsUsedVariable()`. |
|
||
| `(t *token) SourceFile()` | `conf/parse.go:178` | PORTED | `src/NATS.Server/Configuration/NatsConfToken.cs:55` | Added `PedanticToken.SourceFile()`. |
|
||
| `(t *token) Position()` | `conf/parse.go:182` | PORTED | `src/NATS.Server/Configuration/NatsConfToken.cs:57` | Added `PedanticToken.Position()`. |
|
||
| `newParser()` | `conf/parse.go:186` | PORTED | `NatsConfParser.cs:105` | `ParserState` constructors |
|
||
| `parse()` | `conf/parse.go:199` | PORTED | `NatsConfParser.cs:118` | `ParserState.Run()` — identical loop structure |
|
||
| `parseEnv()` | `conf/parse.go:207` | PORTED | `NatsConfParser.cs:75` | `ParseEnvValue()` static method — same synthetic `pk=<value>` trick |
|
||
| `(p *parser) parse()` | `conf/parse.go:216` | PORTED | `NatsConfParser.cs:118` | `ParserState.Run()` |
|
||
| `(p *parser) next()` | `conf/parse.go:238` | PORTED | `NatsConfParser.cs:142` | `ParserState.Next()` |
|
||
| `(p *parser) pushContext()` | `conf/parse.go:242` | PORTED | `NatsConfParser.cs:152` | `PushContext()` |
|
||
| `(p *parser) popContext()` | `conf/parse.go:247` | PORTED | `NatsConfParser.cs:158` | `PopContext()` |
|
||
| `(p *parser) pushKey()` | `conf/parse.go:258` | PORTED | `NatsConfParser.cs:171` | `PushKey()` |
|
||
| `(p *parser) popKey()` | `conf/parse.go:262` | PORTED | `NatsConfParser.cs:173` | `PopKey()` |
|
||
| `(p *parser) pushItemKey()` | `conf/parse.go:272` | PORTED | `src/NATS.Server/Configuration/NatsConfParser.cs:214` | Added pedantic key-token stack helper in parser state. |
|
||
| `(p *parser) popItemKey()` | `conf/parse.go:276` | PORTED | `src/NATS.Server/Configuration/NatsConfParser.cs:216` | Added pedantic key-token pop helper; synchronized with map assignments in `SetValue()`. |
|
||
| `(p *parser) processItem()` | `conf/parse.go:286` | PORTED | `NatsConfParser.cs:205` | `ProcessItem()` — handles all token types including variable, include, map, array |
|
||
| `(p *parser) lookupVariable()` | `conf/parse.go:462` | PORTED | `NatsConfParser.cs:344` | `ResolveVariable()` — block scoping, env var lookup, cycle detection, bcrypt prefix all ported |
|
||
| `(p *parser) setValue()` | `conf/parse.go:500` | PORTED | `NatsConfParser.cs:185` | `SetValue()` — array and map context handling |
|
||
| `pkey` constant | `conf/parse.go:452` | PORTED | `NatsConfParser.cs:77` | Used in `ParseEnvValue` synthetic input (`"pk={value}"`) |
|
||
| `bcryptPrefix` constant | `conf/parse.go:455` | PORTED | `src/NATS.Server/Configuration/NatsConfParser.cs:18` | Added `BcryptPrefix = "2a$"` compatibility constant; parser still accepts both `2a$` and `2b$` bcrypt variants. |
|
||
|
||
---
|
||
|
||
### golang/nats-server/conf/token.go (does not exist)
|
||
|
||
The `token.go` file listed in the gap analysis instructions does not exist in the Go source tree. The `item` and `itemType` declarations live in `lex.go`. The `token` struct (pedantic wrapper) lives in `parse.go`. The .NET `NatsConfToken.cs` consolidates both the token type enum (`TokenType`) and the token value record (`Token`).
|
||
|
||
---
|
||
|
||
### .NET-only Configuration Files (no direct Go counterpart in conf/)
|
||
|
||
These .NET files port functionality from `golang/nats-server/server/opts.go` and `golang/nats-server/server/reload.go` which are tracked under the Core Server category, but the implementations live in the Configuration folder:
|
||
|
||
| .NET File | Go Reference | Status | Notes |
|
||
|-----------|:-------------|--------|-------|
|
||
| `src/NATS.Server/Configuration/ConfigProcessor.cs` | `server/opts.go` (processConfigFileLine, ~line 1050) | PORTED | Maps parsed dict to `NatsOptions`. Covers: listen, port, host, logging, limits, monitoring, TLS, auth, cluster, gateway, leafnode, JetStream, MQTT config keys |
|
||
| `src/NATS.Server/Configuration/ConfigReloader.cs` | `server/reload.go` | PORTED | `Diff()`, `Validate()`, `MergeCliOverrides()`, `ApplyDiff()`, `ReloadAsync()`, `ReloadFromOptionsAsync()`, `ApplyClusterConfigChanges()`, `ApplyLoggingChanges()`, `PropagateAuthChanges()`, `ReloadTlsCertificates()`, `ReloadTlsCertificate()`, `ApplyJetStreamConfigChanges()` |
|
||
| `src/NATS.Server/Configuration/IConfigChange.cs` | `server/reload.go:42–74` | PORTED | `IConfigChange` interface + `ConfigChange` implementation |
|
||
| `src/NATS.Server/Configuration/ClusterOptions.cs` | `server/opts.go` (ClusterOpts) | PORTED | Cluster options struct |
|
||
| `src/NATS.Server/Configuration/GatewayOptions.cs` | `server/opts.go` (GatewayOpts, RemoteGatewayOpts) | PORTED | Gateway options + `RemoteGatewayOptions` |
|
||
| `src/NATS.Server/Configuration/LeafNodeOptions.cs` | `server/opts.go` (LeafNodeOpts, RemoteLeafOpts) | PORTED | LeafNode options + `RemoteLeafOptions` |
|
||
| `src/NATS.Server/Configuration/JetStreamOptions.cs` | `server/opts.go` (JetStreamConfig) | PORTED | JetStream options struct |
|
||
| `src/NATS.Server/Configuration/RouteCompression.cs` | `server/opts.go` (Compression enum) | PORTED | `RouteCompression` enum (None, S2) |
|
||
| `src/NATS.Server/Configuration/SignalHandler.cs` | `server/signal_unix.go` | PORTED | SIGHUP handler via `PosixSignalRegistration` |
|
||
|
||
---
|
||
|
||
### conf/fuzz.go
|
||
|
||
| Go Symbol | Go File:Line | Status | .NET Equivalent | Notes |
|
||
|-----------|:-------------|--------|:----------------|-------|
|
||
| `Fuzz()` | `conf/fuzz.go:18` | NOT_APPLICABLE | — | Go fuzz test entry point (`//go:build gofuzz` build tag). .NET has its own fuzzing infrastructure (SharpFuzz / libFuzzer) but no equivalent fuzz target exists. Low priority. |
|
||
|
||
---
|
||
|
||
## 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/Configuration/ -name '*.cs' -type f -exec cat {} + | wc -l
|
||
# Re-count .NET test LOC for this module
|
||
find tests/NATS.Server.Tests/Configuration/ -name '*.cs' -type f -exec cat {} + | wc -l
|
||
```
|
||
4. **Add a changelog entry** below with date and summary of what was ported
|
||
5. **Update the parity DB** if new test mappings were created:
|
||
```bash
|
||
sqlite3 docs/test_parity.db "INSERT INTO test_mappings (go_test_id, dotnet_test_id, confidence, notes) VALUES (?, ?, 'manual', 'ported in YYYY-MM-DD session')"
|
||
```
|
||
|
||
## Change Log
|
||
|
||
| Date | Change | By |
|
||
|------|--------|----|
|
||
| 2026-02-26 | Executed configuration batch 4: upgraded `ParseFileWithChecksDigest` to compute digest from canonicalized parsed config tree (sorted-key JSON) and added targeted digest behavior assertions in `ConfigPedanticParityBatch1Tests`. Reclassified `ParseFileWithChecksDigest` from PARTIAL to PORTED. | codex |
|
||
| 2026-02-25 | File created with LLM analysis instructions | auto |
|
||
| 2026-02-25 | Full gap inventory completed: read all Go source files (lex.go, parse.go) and all .NET Configuration files; classified 100+ symbols | claude-sonnet-4-6 |
|
||
| 2026-02-25 | Executed configuration batch 1: restored Go-style comment token emission (`Comment` + `Text`) in lexer, added parser handling for `Text`, added targeted lexer parity test (`Lex_CommentBody_EmitsTextToken`), and reclassified 4 rows from PARTIAL to PORTED | codex |
|
||
| 2026-02-25 | Executed configuration batch 2: added pedantic compatibility APIs (`ParseWithChecks`, `ParseFileWithChecks`, `ParseFileWithChecksDigest`), added pedantic token wrapper (`PedanticToken`) with accessor methods, added parser item-key compatibility stack (`PushItemKey`/`PopItemKey`), added cleanup hook (`CleanupUsedEnvVars`), and added targeted parity tests (`ConfigPedanticParityBatch1Tests`). | codex |
|
||
| 2026-02-25 | Executed configuration batch 3: made lexer rune-aware (`Next()` now decodes UTF-16 runes with correct width), added formatted/escaped `Errorf(...)` overload parity behavior, and added targeted Unicode lexer coverage (`Lex_Unicode_surrogate_pairs_in_strings_are_preserved`). Reclassified 2 rows from PARTIAL to PORTED. | codex |
|