Improve docs coverage and refresh profiling parser artifacts

Add domain-specific XML documentation across src server components to satisfy CommentChecker, and update dotTrace parsing outputs used for diagnostics.
This commit is contained in:
Joseph Doherty
2026-03-14 04:06:04 -04:00
parent 46ead5ea9f
commit 5de4962bd3
46 changed files with 761 additions and 10488 deletions

View File

@@ -9,12 +9,26 @@ public sealed class ExternalAuthCalloutAuthenticator : IAuthenticator
private readonly IExternalAuthClient _client;
private readonly TimeSpan _timeout;
/// <summary>
/// Creates an authenticator that delegates user validation to the external auth callout subject.
/// This mirrors the NATS external authorization flow used for centralized policy decisions.
/// </summary>
/// <param name="client">Client used to publish authorization requests and receive decisions.</param>
/// <param name="timeout">Maximum time to wait for an authorization decision.</param>
public ExternalAuthCalloutAuthenticator(IExternalAuthClient client, TimeSpan timeout)
{
_client = client;
_timeout = timeout;
}
/// <summary>
/// Authenticates a client by calling the external authorization service and mapping the decision
/// into a local identity/account context for the accepted connection.
/// </summary>
/// <param name="context">Connection authentication inputs received from the CONNECT payload.</param>
/// <returns>
/// An <see cref="AuthResult"/> when the callout allows the connection; otherwise <see langword="null"/>.
/// </returns>
public AuthResult? Authenticate(ClientAuthContext context)
{
using var cts = new CancellationTokenSource(_timeout);

View File

@@ -99,9 +99,17 @@ public sealed class AccountLimits
public sealed class AccountJetStreamLimits
{
/// <summary>
/// Maximum number of streams the account can create in JetStream.
/// This limit protects cluster resources in multi-tenant deployments.
/// </summary>
[JsonPropertyName("max_streams")]
public int MaxStreams { get; set; }
/// <summary>
/// Optional JetStream service tier label assigned to the account (for example, dev or prod).
/// Tier is used for policy and placement decisions in operator-managed environments.
/// </summary>
[JsonPropertyName("tier")]
public string? Tier { get; set; }
}

View File

@@ -15,6 +15,14 @@ internal static class JwtConnectionTypes
Standard, Websocket, Leafnode, LeafnodeWs, Mqtt, MqttWs, InProcess,
];
/// <summary>
/// Converts raw JWT allowed connection type values into normalized server constants
/// and tracks whether any unknown connection types were supplied by policy.
/// </summary>
/// <param name="values">Allowed connection type values from user JWT claims.</param>
/// <returns>
/// A set of valid normalized types and a flag indicating whether unknown values were present.
/// </returns>
public static (HashSet<string> Valid, bool HasUnknown) Convert(IEnumerable<string>? values)
{
var valid = new HashSet<string>(StringComparer.Ordinal);

View File

@@ -223,11 +223,11 @@ public sealed class JwtToken
/// </summary>
public sealed class JwtHeader
{
[System.Text.Json.Serialization.JsonPropertyName("alg")]
/// <summary>JWT signing algorithm identifier (typically <c>ed25519-nkey</c> for NATS).</summary>
[System.Text.Json.Serialization.JsonPropertyName("alg")]
public string? Algorithm { get; set; }
[System.Text.Json.Serialization.JsonPropertyName("typ")]
/// <summary>JWT type marker (typically <c>JWT</c>).</summary>
[System.Text.Json.Serialization.JsonPropertyName("typ")]
public string? Type { get; set; }
}

View File

@@ -12,12 +12,26 @@ public sealed class JwtAuthenticator : IAuthenticator
private readonly string[] _trustedKeys;
private readonly IAccountResolver _resolver;
/// <summary>
/// Creates a JWT authenticator that trusts the provided operator keys and resolves account JWTs
/// from the configured resolver source.
/// </summary>
/// <param name="trustedKeys">Trusted operator/signing keys allowed to issue account JWTs.</param>
/// <param name="resolver">Resolver used to fetch account claims by account public key.</param>
public JwtAuthenticator(string[] trustedKeys, IAccountResolver resolver)
{
_trustedKeys = trustedKeys;
_resolver = resolver;
}
/// <summary>
/// Authenticates a client using NATS JWT flow: decode user claims, resolve account claims,
/// verify trust/signatures/revocations, and derive connection permissions and limits.
/// </summary>
/// <param name="context">CONNECT options and nonce/signature context for the client.</param>
/// <returns>
/// Populated authentication result when JWT policy allows the connection; otherwise <see langword="null"/>.
/// </returns>
public AuthResult? Authenticate(ClientAuthContext context)
{
var jwt = context.Opts.JWT;

View File

@@ -18,6 +18,12 @@ public sealed class NKeyAuthenticator(IEnumerable<NKeyUser> nkeyUsers) : IAuthen
u => u,
StringComparer.Ordinal);
/// <summary>
/// Authenticates a client by verifying its nonce signature with the presented NKey public key
/// and returning the mapped account and permission context for that key.
/// </summary>
/// <param name="context">CONNECT payload plus server nonce used for signature verification.</param>
/// <returns><see cref="AuthResult"/> for a valid NKey user; otherwise <see langword="null"/>.</returns>
public AuthResult? Authenticate(ClientAuthContext context)
{
var clientNkey = context.Opts.Nkey;

View File

@@ -2,6 +2,12 @@ namespace NATS.Server.Auth;
public sealed class ProxyAuthenticator(ProxyAuthOptions options) : IAuthenticator
{
/// <summary>
/// Authenticates a client from a trusted proxy identity prefix and maps it to the configured account.
/// This supports edge proxies that perform upstream auth and pass a canonical user principal.
/// </summary>
/// <param name="context">Client credentials and connection metadata from CONNECT.</param>
/// <returns><see cref="AuthResult"/> when proxy-auth rules match; otherwise <see langword="null"/>.</returns>
public AuthResult? Authenticate(ClientAuthContext context)
{
if (!options.Enabled)

View File

@@ -14,12 +14,22 @@ public sealed class SimpleUserPasswordAuthenticator : IAuthenticator
private readonly byte[] _expectedUsername;
private readonly string _serverPassword;
/// <summary>
/// Creates an authenticator for a single configured user credential pair from server options.
/// </summary>
/// <param name="username">Expected username for incoming client connections.</param>
/// <param name="password">Expected password (plain or bcrypt hash) for that username.</param>
public SimpleUserPasswordAuthenticator(string username, string password)
{
_expectedUsername = Encoding.UTF8.GetBytes(username);
_serverPassword = password;
}
/// <summary>
/// Authenticates the configured single user using constant-time comparisons and optional bcrypt verification.
/// </summary>
/// <param name="context">Client-provided username/password from CONNECT.</param>
/// <returns><see cref="AuthResult"/> on successful validation; otherwise <see langword="null"/>.</returns>
public AuthResult? Authenticate(ClientAuthContext context)
{
var clientUsername = context.Opts.Username;

View File

@@ -7,11 +7,20 @@ public sealed class TokenAuthenticator : IAuthenticator
{
private readonly byte[] _expectedToken;
/// <summary>
/// Creates a token authenticator for deployments that use shared bearer tokens.
/// </summary>
/// <param name="token">Server-configured token value clients must present in CONNECT.</param>
public TokenAuthenticator(string token)
{
_expectedToken = Encoding.UTF8.GetBytes(token);
}
/// <summary>
/// Authenticates the client using constant-time token comparison to avoid timing leakage.
/// </summary>
/// <param name="context">Client connection options containing the presented token.</param>
/// <returns><see cref="AuthResult"/> for a matching token; otherwise <see langword="null"/>.</returns>
public AuthResult? Authenticate(ClientAuthContext context)
{
var clientToken = context.Opts.Token;

View File

@@ -13,6 +13,10 @@ public sealed class UserPasswordAuthenticator : IAuthenticator
{
private readonly Dictionary<string, User> _users;
/// <summary>
/// Creates an authenticator for a configured user set and builds a fast lookup by username.
/// </summary>
/// <param name="users">Configured users with account mappings and permission scopes.</param>
public UserPasswordAuthenticator(IEnumerable<User> users)
{
_users = new Dictionary<string, User>(StringComparer.Ordinal);
@@ -20,6 +24,11 @@ public sealed class UserPasswordAuthenticator : IAuthenticator
_users[user.Username] = user;
}
/// <summary>
/// Authenticates a username/password client and returns account, permissions, and connection expiry metadata.
/// </summary>
/// <param name="context">Client CONNECT credentials.</param>
/// <returns><see cref="AuthResult"/> when credentials match; otherwise <see langword="null"/>.</returns>
public AuthResult? Authenticate(ClientAuthContext context)
{
var username = context.Opts.Username;

View File

@@ -28,6 +28,11 @@ public enum ClientClosedReason
public static class ClientClosedReasonExtensions
{
/// <summary>
/// Converts an internal close reason enum into the human-readable text exposed by monitoring endpoints.
/// </summary>
/// <param name="reason">Internal close classification captured at disconnect time.</param>
/// <returns>Display string used in `/connz` and related operational diagnostics.</returns>
public static string ToReasonString(this ClientClosedReason reason) => reason switch
{
ClientClosedReason.None => "",

View File

@@ -17,6 +17,12 @@ public enum ClientKind
public static class ClientKindExtensions
{
/// <summary>
/// Indicates whether a client kind represents internal server infrastructure traffic
/// rather than an external end-user connection.
/// </summary>
/// <param name="kind">Connection kind being evaluated.</param>
/// <returns><see langword="true"/> for internal kinds such as system and JetStream.</returns>
public static bool IsInternal(this ClientKind kind) =>
kind is ClientKind.System or ClientKind.JetStream or ClientKind.Account;
}

View File

@@ -63,6 +63,12 @@ public sealed class NatsConfLexer
_ilstart = 0;
}
/// <summary>
/// Tokenizes a NATS configuration document into lexical tokens consumed by the config parser.
/// The lexer preserves Go-compatible token rules for production config parity.
/// </summary>
/// <param name="input">Raw configuration text in NATS conf syntax.</param>
/// <returns>Ordered token stream including error tokens when malformed input is encountered.</returns>
public static IReadOnlyList<Token> Tokenize(string input)
{
ArgumentNullException.ThrowIfNull(input);

View File

@@ -11,11 +11,19 @@ public sealed class AdaptiveReadBuffer
private int _target = 4096;
private int _consecutiveShortReads;
/// <summary>
/// Current target buffer size used for the next socket read operation.
/// </summary>
public int CurrentSize => Math.Clamp(_target, 512, 64 * 1024);
/// <summary>Number of consecutive short reads since last full read or grow.</summary>
public int ConsecutiveShortReads => _consecutiveShortReads;
/// <summary>
/// Updates adaptive sizing state using the number of bytes returned by the latest read.
/// Full reads bias toward growth for throughput, repeated short reads bias toward shrink to save memory.
/// </summary>
/// <param name="bytesRead">Byte count returned by the transport read.</param>
public void RecordRead(int bytesRead)
{
if (bytesRead <= 0)

View File

@@ -2,6 +2,13 @@ namespace NATS.Server.Imports;
public sealed class ServiceLatency
{
/// <summary>
/// Percentage of service requests to sample for latency telemetry export.
/// </summary>
public int SamplingPercentage { get; init; } = 100;
/// <summary>
/// Subject where sampled service latency observations are published.
/// </summary>
public string Subject { get; init; } = string.Empty;
}

View File

@@ -2,5 +2,8 @@ namespace NATS.Server.Imports;
public sealed class StreamExport
{
/// <summary>
/// Authorization rules that govern which remote accounts may import this stream export.
/// </summary>
public ExportAuth Auth { get; init; } = new();
}

View File

@@ -2,6 +2,12 @@ namespace NATS.Server.JetStream.Api.Handlers;
public static class AccountApiHandlers
{
/// <summary>
/// Builds JetStream account usage info for `$JS.API.INFO`, including current stream and consumer counts.
/// </summary>
/// <param name="streams">Stream manager for the account context.</param>
/// <param name="consumers">Consumer manager for the account context.</param>
/// <returns>API response payload containing JetStream account statistics.</returns>
public static JetStreamApiResponse HandleInfo(StreamManager streams, ConsumerManager consumers)
{
return new JetStreamApiResponse

View File

@@ -7,6 +7,13 @@ public static class DirectApiHandlers
{
private const string Prefix = JetStreamApiSubjects.DirectGet;
/// <summary>
/// Handles direct message fetch requests by stream and sequence for `$JS.API.DIRECT.GET.*`.
/// </summary>
/// <param name="subject">API subject containing the target stream token.</param>
/// <param name="payload">JSON payload with the requested sequence number.</param>
/// <param name="streamManager">Stream manager used to read the stored message.</param>
/// <returns>Direct message response or a not-found/validation error response.</returns>
public static JetStreamApiResponse HandleGet(string subject, ReadOnlySpan<byte> payload, StreamManager streamManager)
{
var streamName = ExtractTrailingToken(subject, Prefix);

View File

@@ -2,7 +2,14 @@ namespace NATS.Server.JetStream.Api;
public sealed class JetStreamApiError
{
/// <summary>
/// Numeric JetStream API error code returned to clients for programmatic handling.
/// </summary>
public int Code { get; init; }
/// <summary>
/// Human-readable description of the API error condition.
/// </summary>
public string Description { get; init; } = string.Empty;
/// <summary>

View File

@@ -4,11 +4,20 @@ public sealed class AssetPlacementPlanner
{
private readonly int _nodes;
/// <summary>
/// Creates a planner that allocates stream/consumer replicas across the available cluster node count.
/// </summary>
/// <param name="nodes">Number of eligible JetStream nodes in the placement domain.</param>
public AssetPlacementPlanner(int nodes)
{
_nodes = Math.Max(nodes, 1);
}
/// <summary>
/// Produces a deterministic list of node slots for replica placement capped by available nodes.
/// </summary>
/// <param name="replicas">Requested replica count from stream or consumer configuration.</param>
/// <returns>Ordered node slot identifiers selected for placement.</returns>
public IReadOnlyList<int> PlanReplicas(int replicas)
{
var count = Math.Min(Math.Max(replicas, 1), _nodes);

View File

@@ -25,6 +25,7 @@ internal static class MetaSnapshotCodec
/// Encodes <paramref name="assignments"/> into the versioned, S2-compressed binary format.
/// Go reference: jetstream_cluster.go:2075 encodeMetaSnapshot.
/// </summary>
/// <param name="assignments">Current stream placement assignments to persist into the meta snapshot.</param>
public static byte[] Encode(Dictionary<string, StreamAssignment> assignments)
{
var json = JsonSerializer.SerializeToUtf8Bytes(assignments, SerializerOptions);
@@ -40,6 +41,7 @@ internal static class MetaSnapshotCodec
/// Decodes a versioned, S2-compressed binary snapshot into a stream assignment map.
/// Go reference: jetstream_cluster.go:2100 decodeMetaSnapshot.
/// </summary>
/// <param name="data">Versioned binary snapshot payload received from replicated state.</param>
/// <exception cref="InvalidOperationException">
/// Thrown when <paramref name="data"/> is too short or contains an unrecognised version.
/// </exception>

View File

@@ -12,6 +12,12 @@ public sealed class DeliveryInterestTracker
private int _subscriberCount;
private DateTime? _lastUnsubscribeUtc;
/// <summary>
/// Creates a tracker for consumer delivery-subject interest with an optional inactivity timeout override.
/// </summary>
/// <param name="inactiveTimeout">
/// Duration with zero subscribers before the consumer is considered inactive for cleanup.
/// </param>
public DeliveryInterestTracker(TimeSpan? inactiveTimeout = null)
{
_inactiveTimeout = inactiveTimeout ?? TimeSpan.FromSeconds(30);

View File

@@ -2877,6 +2877,7 @@ public sealed class FileStore : IStreamStore, IAsyncDisposable, IDisposable
/// background flush loop's deferred sync queue).
/// Used by <see cref="FileStore.RotateBlock"/> to avoid synchronous fsync on the hot path.
/// </summary>
/// <param name="blockId">Identifier of the message block whose pending cache entry should be removed.</param>
public void EvictBlockNoSync(int blockId)
{
_entries.TryRemove(blockId, out _);

View File

@@ -10,6 +10,11 @@ namespace NATS.Server.Monitoring;
/// </summary>
public sealed class ConnzHandler(NatsServer server)
{
/// <summary>
/// Handles `/connz` monitor requests, applying filters/sorting/pagination over open and closed connections.
/// </summary>
/// <param name="ctx">HTTP request context containing query parameters for connection diagnostics.</param>
/// <returns>Connection report payload compatible with NATS monitoring clients.</returns>
public Connz HandleConnz(HttpContext ctx)
{
var opts = ParseQueryParams(ctx);

View File

@@ -9,6 +9,11 @@ namespace NATS.Server.Monitoring;
/// </summary>
public sealed class SubszHandler(NatsServer server)
{
/// <summary>
/// Handles `/subsz` monitor requests, returning subscription totals and optional per-subscription detail.
/// </summary>
/// <param name="ctx">HTTP request context containing account/subject/paging filter parameters.</param>
/// <returns>Subscription diagnostics payload for operational introspection.</returns>
public Subsz HandleSubsz(HttpContext ctx)
{
var opts = ParseQueryParams(ctx);

View File

@@ -18,6 +18,10 @@ public sealed class MqttStreamInitializer
private volatile bool _initialized;
private readonly Lock _initLock = new();
/// <summary>
/// Creates an initializer that provisions account-scoped MQTT persistence streams on demand.
/// </summary>
/// <param name="streamManager">JetStream manager used to create and query internal MQTT streams.</param>
public MqttStreamInitializer(StreamManager streamManager)
{
_streamManager = streamManager;

View File

@@ -2,6 +2,13 @@ namespace NATS.Server.Protocol;
public sealed class ClientCommandMatrix
{
/// <summary>
/// Validates whether a protocol operation is permitted for a given connection kind
/// to enforce route/gateway/leaf command boundaries.
/// </summary>
/// <param name="kind">Kind of connection issuing the command.</param>
/// <param name="op">Protocol operation verb (for example RS+, A-, LMSG).</param>
/// <returns><see langword="true"/> when the command is allowed for that connection kind.</returns>
public bool IsAllowed(global::NATS.Server.ClientKind kind, string? op)
{
if (string.IsNullOrWhiteSpace(op))

View File

@@ -27,6 +27,7 @@ public readonly record struct RaftMembershipChange(RaftMembershipChangeType Type
/// Parses a log entry command string back into a membership change.
/// Returns null if the command is not a membership change.
/// </summary>
/// <param name="command">Serialized membership command from a replicated RAFT log entry.</param>
public static RaftMembershipChange? TryParse(string command)
{
var colonIndex = command.IndexOf(':');

View File

@@ -31,6 +31,7 @@ public sealed class RaftSnapshotCheckpoint
/// <summary>
/// Adds a chunk of snapshot data for streaming assembly.
/// </summary>
/// <param name="chunk">One snapshot payload fragment received during InstallSnapshot transfer.</param>
public void AddChunk(byte[] chunk) => _chunks.Add(chunk);
/// <summary>

View File

@@ -6,6 +6,11 @@ namespace NATS.Server.Raft;
/// </summary>
public static class RaftStateExtensions
{
/// <summary>
/// Returns the canonical state label used in RAFT diagnostics and status output.
/// </summary>
/// <param name="state">Current RAFT node state.</param>
/// <returns>Stable, human-readable state name.</returns>
public static string String(this RaftState state) =>
state switch
{

View File

@@ -66,7 +66,10 @@ public sealed class SnapshotChunkEnumerator : IEnumerable<byte[]>
/// </summary>
public int ChunkCount => _data.Length == 0 ? 1 : ((_data.Length + _chunkSize - 1) / _chunkSize);
/// <inheritdoc />
/// <summary>
/// Enumerates snapshot chunks in transmission order for InstallSnapshot streaming.
/// </summary>
/// <returns>Sequence of byte arrays, each no larger than the configured chunk size.</returns>
public IEnumerator<byte[]> GetEnumerator()
{
if (_data.Length == 0)
@@ -86,5 +89,6 @@ public sealed class SnapshotChunkEnumerator : IEnumerable<byte[]>
}
}
/// <inheritdoc />
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator();
}

View File

@@ -8,6 +8,14 @@ public sealed record RemoteSubscription(
int QueueWeight = 1,
bool IsRemoval = false)
{
/// <summary>
/// Creates a tombstone record that propagates remote subscription removal across route links.
/// </summary>
/// <param name="subject">Subject being removed from remote interest tracking.</param>
/// <param name="queue">Optional queue group associated with the removed subscription.</param>
/// <param name="routeId">Route identifier where the removal originated.</param>
/// <param name="account">Account namespace for the subscription removal.</param>
/// <returns>A <see cref="RemoteSubscription"/> marked as a removal operation.</returns>
public static RemoteSubscription Removal(string subject, string? queue, string routeId, string account = "$G")
=> new(subject, queue, routeId, account, QueueWeight: 1, IsRemoval: true);
}

View File

@@ -2,6 +2,11 @@ namespace NATS.Server.Subscriptions;
internal readonly record struct RoutedSubKey(string RouteId, string Account, string Subject, string? Queue)
{
/// <summary>
/// Projects a remote subscription announcement into the canonical routed-subscription key shape.
/// </summary>
/// <param name="sub">Remote subscription metadata received from a route peer.</param>
/// <returns>Deduplication key used by routed subscription indexes.</returns>
public static RoutedSubKey FromRemoteSubscription(RemoteSubscription sub)
=> new(sub.RouteId, sub.Account, sub.Subject, sub.Queue);
}

View File

@@ -12,6 +12,18 @@ public static class TlsConnectionWrapper
{
private const byte TlsRecordMarker = 0x16;
/// <summary>
/// Negotiates client transport security according to server TLS mode (required, mixed, or TLS-first),
/// including INFO preface behavior and optional pinned certificate validation.
/// </summary>
/// <param name="socket">Accepted client socket.</param>
/// <param name="networkStream">Base transport stream over the accepted socket.</param>
/// <param name="options">Server TLS and protocol options controlling negotiation mode.</param>
/// <param name="sslOptions">TLS server authentication options, or <see langword="null"/> when TLS is disabled.</param>
/// <param name="serverInfo">Server INFO payload template to send before/after TLS handshake.</param>
/// <param name="logger">Logger used for TLS negotiation diagnostics.</param>
/// <param name="ct">Cancellation token for connection startup and handshake operations.</param>
/// <returns>Negotiated stream and a flag indicating whether INFO was already written.</returns>
public static async Task<(Stream stream, bool infoAlreadySent)> NegotiateAsync(
Socket socket,
Stream networkStream,

View File

@@ -15,6 +15,12 @@ public static class WebSocketOptionsValidator
"Sec-WebSocket-Protocol",
};
/// <summary>
/// Validates websocket listener settings against NATS auth, TLS, and header constraints
/// to fail fast on incompatible runtime configuration.
/// </summary>
/// <param name="options">Server options containing websocket and global auth/TLS configuration.</param>
/// <returns>Validation result with error details when the websocket configuration is invalid.</returns>
public static WebSocketOptionsValidationResult Validate(NatsOptions options)
{
ArgumentNullException.ThrowIfNull(options);

View File

@@ -61,6 +61,11 @@ public static class WsConstants
// Decompression trailer appended before decompressing (RFC 7692 Section 7.2.2)
public static readonly byte[] DecompressTrailer = [0x00, 0x00, 0xff, 0xff];
/// <summary>
/// Determines whether an opcode is a WebSocket control frame (close, ping, or pong).
/// </summary>
/// <param name="opcode">RFC 6455 opcode from the incoming frame header.</param>
/// <returns><see langword="true"/> when the frame must follow control-frame size/fragmentation rules.</returns>
public static bool IsControlFrame(int opcode) => opcode >= CloseMessage;
}