# 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<T>` 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<string, bool>)` — 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.
