# Stub Features Implementation Design **Date:** 2026-02-26 **Scope:** Complete the 93 remaining `stub` features in Phase 6 **Approach:** Two parallel sessions (Config + Auth) ## Overview After Phase 6's 23 porting sessions, 93 features remain at `stub` status. They fall into two independent concerns that can be implemented in parallel: | Group | Go File | Stubs | Go LOC | Concern | |-------|---------|-------|--------|---------| | Config | `server/opts.go` | 67 | ~4,876 | Configuration file parsing / binding | | Auth | `server/auth.go` | 19 | ~1,296 | Authentication dispatch | | Auth | `server/auth_callout.go` | 3 | ~456 | External auth callout | | Auth | `server/jwt.go` | 3 | ~137 | Operator JWT validation | | Signals | `server/signal.go` | 1 | ~46 | OS signal handling | --- ## Session A: Configuration Binding (67 stubs, opts.go) ### Decision Map all NATS server configuration to **`appsettings.json`** via `Microsoft.Extensions.Configuration`. The Go `conf` package tokenizer and the 765-line `processConfigFileLine` dispatch loop are **not ported** — JSON deserialization replaces them. ### New Files **`Config/ServerOptionsConfiguration.cs`** ```csharp public static class ServerOptionsConfiguration { public static ServerOptions ProcessConfigFile(string path); public static ServerOptions ProcessConfigString(string json); public static void BindConfiguration(IConfiguration config, ServerOptions target); } ``` - `ProcessConfigFile` uses `new ConfigurationBuilder().AddJsonFile(path).Build()` - `ProcessConfigString` uses `AddJsonStream(new MemoryStream(Encoding.UTF8.GetBytes(json)))` - `BindConfiguration` calls `config.Bind(target)` with custom converters registered **`Config/NatsJsonConverters.cs`** Custom `JsonConverter` for non-trivial types: | Converter | Input | Output | Mirrors | |-----------|-------|--------|---------| | `DurationJsonConverter` | `"2s"`, `"100ms"`, `"1h30m"` | `TimeSpan` | `parseDuration` | | `TlsVersionJsonConverter` | `"1.2"`, `"TLS12"` | `SslProtocols` | `parseTLSVersion` | | `NatsUrlJsonConverter` | `"nats://host:port"` | validated `string` | `parseURL` | | `StorageSizeJsonConverter` | `"1GB"`, `"512mb"` | `long` (bytes) | `getStorageSize` | ### ServerOptions.cs Changes Add `[JsonPropertyName("...")]` attributes for fields whose JSON key names differ from C# names. JSON key names follow NATS server conventions (lowercase, underscore-separated): ```json { "port": 4222, "host": "0.0.0.0", "tls": { "cert_file": "...", "key_file": "...", "ca_file": "..." }, "cluster": { "port": 6222, "name": "my-cluster" }, "gateway": { "port": 7222, "name": "my-gateway" }, "jetstream": { "store_dir": "/data/jetstream", "max_memory": "1GB" }, "leafnodes": { "port": 7422 }, "mqtt": { "port": 1883 }, "websocket": { "port": 8080 }, "accounts": [ { "name": "A", "users": [ { "user": "u1", "password": "p1" } ] } ] } ``` ### DB Outcome All 67 opts.go stubs → `complete`: - Feature IDs 2505–2574, 2580, 2584 (+ `configureSystemAccount` 2509, `setupUsersAndNKeysDuplicateCheckMap` 2515) - `parse*` functions have no C# equivalent — their logic is subsumed by converters and JSON binding --- ## Session B: Auth Implementation (26 stubs) ### New Files **`NatsServer.Auth.cs`** — `partial class NatsServer` with: | Method | Go Equivalent | Notes | |--------|--------------|-------| | `ConfigureAuthorization()` | `configureAuthorization` | Builds `_nkeys`/`_users` dicts from `_opts` | | `BuildNkeysAndUsersFromOptions()` | `buildNkeysAndUsersFromOptions` | Creates typed lookup maps | | `CheckAuthforWarnings()` | `checkAuthforWarnings` | Validates auth config consistency | | `AssignGlobalAccountToOrphanUsers()` | `assignGlobalAccountToOrphanUsers` | — | | `CheckAuthentication(ClientConnection)` | `checkAuthentication` | Entry point | | `IsClientAuthorized(ClientConnection)` | `isClientAuthorized` | Check user credentials | | `ProcessClientOrLeafAuthentication(ClientConnection, ServerOptions)` | `processClientOrLeafAuthentication` | Main 554-line auth dispatch | | `IsRouterAuthorized(ClientConnection)` | `isRouterAuthorized` | Route-specific auth | | `IsGatewayAuthorized(ClientConnection)` | `isGatewayAuthorized` | Gateway-specific auth | | `RegisterLeafWithAccount(ClientConnection, string)` | `registerLeafWithAccount` | — | | `IsLeafNodeAuthorized(ClientConnection)` | `isLeafNodeAuthorized` | Leaf-specific auth | | `ProcessProxiesTrustedKeys()` | `processProxiesTrustedKeys` | Proxy key setup | | `ProxyCheck(ClientConnection, ServerOptions)` | `proxyCheck` | Validate proxy headers | **Auth dispatch flow in `ProcessClientOrLeafAuthentication`:** ``` if callout configured → ProcessClientOrLeafCallout() else if JWT bearer → JwtProcessor.ValidateAndRegisterUser() else if NKey → verify NKey signature (NATS.NKeys NuGet) else if user+password → BCrypt.Net.BCrypt.Verify() (BCrypt.Net-Next NuGet) else if TLS cert map → CheckClientTlsCertSubject() else if no-auth mode → allow (if opts.NoAuth) → set client account, permissions, labels ``` **`Auth/AuthCallout.cs`** — `partial class NatsServer` with: - `ProcessClientOrLeafCallout(ClientConnection, ServerOptions)` — publishes to `$SYS.REQ.USER.AUTH`, waits for signed JWT response, validates it - `FillClientInfo(AuthorizationRequestClaims, ClientConnection)` — populate auth request payload - `FillConnectOpts(AuthorizationRequestClaims, ClientConnection)` — populate connect opts in payload **`Auth/JwtProcessor.cs` additions:** - `ReadOperatorJwt(string path)` — read operator JWT from file, decode `OperatorClaims` - `ReadOperatorJwtInternal(string jwtString)` — decode from string - `ValidateTrustedOperators(ServerOptions opts)` — walk operator → account → user signing key chain **`Auth/AuthHandler.cs` additions:** - `ProcessUserPermissionsTemplate(UserPermissionLimits, UserClaims, Account)` — expand `{{account}}`, `{{tag.*}}` template variables in JWT user permissions - `GetTlsAuthDcs(X509DistinguishedName)` — extract DC= components from TLS cert RDN - `CheckClientTlsCertSubject(ClientConnection, Func)` — TLS cert subject matching - `ValidateProxies(ServerOptions)` — validate proxy configuration - `GetAuthErrClosedState(ClientConnection)` — map auth failure to client closed state enum ### New NuGet Packages | Package | Version | Purpose | |---------|---------|---------| | `BCrypt.Net-Next` | ≥4.0 | bcrypt password hashing and comparison | | `NATS.NKeys` | ≥2.0 | NKey keypair creation, signature verify | ### `NatsServer.Signals.cs` New partial class file: ```csharp // Registers OS signal handlers via PosixSignalRegistration (cross-platform). // SIGHUP → server.Reload() // SIGTERM → server.Shutdown() // SIGINT → server.Shutdown() // Windows fallback: Console.CancelKeyPress → Shutdown() ``` ### DB Outcome All 26 auth/jwt/callout/signal stubs → `complete`: - Feature IDs 354–383, 1973–1976, 2584, 3156 --- ## File Summary | File | Action | |------|--------| | `Config/ServerOptionsConfiguration.cs` | CREATE | | `Config/NatsJsonConverters.cs` | CREATE | | `NatsServer.Auth.cs` | CREATE (partial) | | `NatsServer.Signals.cs` | CREATE (partial) | | `Auth/AuthCallout.cs` | CREATE (partial) | | `Auth/JwtProcessor.cs` | MODIFY (add 3 methods) | | `Auth/AuthHandler.cs` | MODIFY (add 5 methods) | | `ServerOptions.cs` | MODIFY (add JsonPropertyName attrs) | | `ZB.MOM.NatsNet.Server.csproj` | MODIFY (add 2 NuGet packages) | --- ## Testing Unit tests in `ZB.MOM.NatsNet.Server.Tests/`: - `Config/ServerOptionsConfigurationTests.cs` — round-trip JSON bind tests for each major option group - `Auth/AuthHandlerTests.cs` additions — bcrypt comparison, NKey verify, TLS cert subject matching - `Auth/JwtProcessorTests.cs` additions — operator JWT read/validate No new test IDs needed — these are implementations of already-tracked Phase 6 features. After implementation, relevant test IDs in Phase 7 will be marked complete.