# Remaining Lower-Priority Gaps — Design Document **Goal:** Resolve all remaining lower-priority gaps from differences.md with full Go parity, bringing the .NET NATS server to feature completeness for single-server (non-clustered) deployments. **Approach:** Full Go parity — implement all listed gaps including JWT authentication, subject mapping, OCSP, and quick wins. --- ## 1. JWT Authentication The largest component. NATS uses standard JWT encoding (base64url header.payload.signature) with Ed25519 signing via the `nats-io/jwt/v2` Go library. ### New Files - **`Auth/Jwt/NatsJwt.cs`** — JWT decode/encode utilities. Base64url parsing, header extraction, Ed25519 signature verification via NATS.NKeys. Detects JWT by `"eyJ"` prefix. - **`Auth/Jwt/UserClaims.cs`** — User claim record: `Subject` (nkey), `Issuer` (account nkey), `IssuerAccount` (scoped signer), `Name`, `Tags`, `BearerToken`, `Permissions` (pub/sub allow/deny), `ResponsePermission`, `Src` (allowed CIDRs), `Times` (time-based access), `AllowedConnectionTypes`, `IssuedAt`, `Expires`. - **`Auth/Jwt/AccountClaims.cs`** — Account claim record: `Subject`, `Issuer` (operator nkey), `SigningKeys`, `Limits`, `Exports`, `Imports`. - **`Auth/Jwt/PermissionTemplates.cs`** — Template expansion for all 6 mustache-style templates: - `{{name()}}` → user's Name - `{{subject()}}` → user's Subject (nkey) - `{{tag(name)}}` → user tags matching `name:` prefix - `{{account-name()}}` → account display name - `{{account-subject()}}` → account nkey - `{{account-tag(name)}}` → account tags matching prefix - Cartesian product applied for multi-value tags. - **`Auth/Jwt/AccountResolver.cs`** — `IAccountResolver` interface (`FetchAsync`, `StoreAsync`, `IsReadOnly`) + `MemAccountResolver` in-memory implementation. - **`Auth/JwtAuthenticator.cs`** — Implements `IAuthenticator`. Flow: decode user JWT → resolve account → verify Ed25519 signature against nonce → expand permission templates → build `NkeyUser` → validate source IP + time restrictions → check user revocation. ### Modified Files - **`Auth/Account.cs`** — Add `Nkey`, `Issuer`, `SigningKeys` (Dictionary), `RevokedUsers` (ConcurrentDictionary). - **`NatsOptions.cs`** — Add `TrustedKeys` (string[]), `AccountResolver` (IAccountResolver). - **`NatsClient.cs`** — Pass JWT + signature from CONNECT opts to authenticator. ### Design Decisions - No external JWT NuGet — NATS JWTs are simple enough to parse inline (base64url + System.Text.Json + Ed25519 via NATS.NKeys). - `MemAccountResolver` only — URL/directory resolvers are deployment infrastructure. - User revocation: `ConcurrentDictionary` on Account (nkey → issuedAt; JWTs issued before revocation time are rejected). - Source IP validation via `System.Net.IPNetwork.Contains()` (.NET 8+). --- ## 2. Subject Mapping / Transforms Port Go's `subject_transform.go`. Configurable source pattern → destination template with function tokens. ### New File - **`Subscriptions/SubjectTransform.cs`** — A transform has a source pattern (with wildcards) and a destination template with function tokens. On match, captured wildcard values are substituted into the destination. ### Transform Functions | Function | Description | |----------|-------------| | `{{wildcard(n)}}` / `$n` | Replace with nth captured wildcard token (1-based) | | `{{partition(num,tokens...)}}` | FNV-1a hash of captured tokens mod `num` | | `{{split(token,delim)}}` | Split captured token by delimiter into subject tokens | | `{{splitFromLeft(token,pos)}}` | Split token into two at position from left | | `{{splitFromRight(token,pos)}}` | Split token into two at position from right | | `{{sliceFromLeft(token,size)}}` | Slice token into fixed-size chunks from left | | `{{sliceFromRight(token,size)}}` | Slice token into fixed-size chunks from right | | `{{left(token,len)}}` | Take first `len` chars | | `{{right(token,len)}}` | Take last `len` chars | ### Integration - `NatsOptions.SubjectMappings` — `Dictionary` of source→destination rules. - Transforms compiled at config time into token operation lists (no runtime regex). - Applied in `NatsServer.DeliverMessage` before subject matching. - Account-level mappings for import/export rewriting. --- ## 3. OCSP Support Two dimensions: peer verification (client cert revocation checking) and stapling (server cert status). ### New File - **`Tls/OcspConfig.cs`** — `OcspMode` enum (`Auto`, `Always`, `Must`, `Never`) + `OcspConfig` record with `Mode` and `OverrideUrls`. ### Peer Verification - Modify `TlsConnectionWrapper` to set `X509RevocationMode.Online` when `OcspPeerVerify` is true. - Checks CRL/OCSP during TLS handshake for client certificates. ### OCSP Stapling - Build `SslStreamCertificateContext.Create(cert, chain, offline: false)` at startup — .NET fetches OCSP response automatically. - Pass to `SslServerAuthenticationOptions.ServerCertificateContext`. - `Must` mode: verify OCSP response obtained; fail startup if not. ### Modified Files - `NatsOptions.cs` — `OcspConfig` and `OcspPeerVerify` properties. - `TlsConnectionWrapper.cs` — Peer verification in cert validation callback. - `NatsServer.cs` — `SslStreamCertificateContext` with OCSP at startup. - `VarzHandler.cs` — Populate `TlsOcspPeerVerify` field. --- ## 4. Quick Wins ### A. Windows Service Integration - Add `Microsoft.Extensions.Hosting.WindowsServices` NuGet. - Detect `--service` flag in `Program.cs`, call `UseWindowsService()`. - .NET generic host handles service lifecycle automatically. ### B. Per-Subsystem Log Control - `NatsOptions.LogOverrides` — `Dictionary` mapping namespace→level. - CLI: `--log_level_override "NATS.Server.Protocol=Trace"` (repeatable). - Serilog: `MinimumLevel.Override(namespace, level)` per entry. ### C. Per-Client Trace Mode - `TraceMode` flag in `ClientFlagHolder`. - When set, parser receives logger regardless of global `options.Trace`. - Connz response includes `trace` boolean per connection. ### D. Per-Account Stats - `long _inMsgs, _outMsgs, _inBytes, _outBytes` on `Account` with `Interlocked`. - `IncrementInbound(long bytes)` / `IncrementOutbound(long bytes)`. - Called from `DeliverMessage` (outbound) and message processing (inbound). - `/accountz` endpoint returns per-account stats. ### E. TLS Certificate Expiry in /varz - In `VarzHandler`, read server TLS cert `NotAfter`. - Populate existing `TlsCertNotAfter` field. ### F. differences.md Update - Mark all resolved features as Y with notes. - Update summary section. - Correct any stale entries. --- ## Task Dependencies ``` Independent (parallelizable): - JWT Authentication (#23) - Subject Mapping (#24) - OCSP Support (#25) - Windows Service (#26) - Per-Subsystem Logging (#27) - Per-Client Trace (#28) - Per-Account Stats (#29) - TLS Cert Expiry (#30) Dependent: - Update differences.md (#31) — blocked by all above ``` Most tasks can run in parallel since they touch different files. JWT and Subject Mapping are the two largest. The quick wins (26-30) are all independent of each other and of the larger tasks.