using System.Security.Cryptography; using System.Security.Authentication; using System.Text; using System.Text.Json; using NATS.Server.Auth; using NATS.Server.Configuration; using NATS.Server.Protocol; using NATS.Server.Tls; namespace NATS.Server; /// /// Represents the complete runtime configuration for a NATS server instance, /// including client connectivity, auth, clustering, transport, and observability settings. /// public sealed class NatsOptions { private static bool _allowUnknownTopLevelFields; private string _configDigest = string.Empty; /// /// Gets or sets the listener host for client connections. /// public string Host { get; set; } = NatsProtocol.DefaultHost; /// /// Gets or sets the TCP port for client connections. /// public int Port { get; set; } = NatsProtocol.DefaultPort; /// /// Gets or sets a logical server name advertised to clients and peers. /// public string? ServerName { get; set; } /// /// Gets or sets the maximum accepted payload size in bytes for client publishes. /// public int MaxPayload { get; set; } = 1024 * 1024; /// /// Gets or sets the maximum protocol control line length in bytes. /// public int MaxControlLine { get; set; } = 4096; /// /// Gets or sets the maximum number of concurrent client connections. /// public int MaxConnections { get; set; } = NatsProtocol.DefaultMaxConnections; /// /// Gets or sets the maximum buffered outbound data per client before the connection is considered slow. /// public long MaxPending { get; set; } = 64 * 1024 * 1024; // 64MB, matching Go MAX_PENDING_SIZE /// /// Gets or sets the write deadline used for flushing outbound protocol frames. /// public TimeSpan WriteDeadline { get; set; } = NatsProtocol.DefaultFlushDeadline; /// /// Gets or sets the server heartbeat interval used to detect stale clients. /// public TimeSpan PingInterval { get; set; } = NatsProtocol.DefaultPingInterval; /// /// Gets or sets the maximum number of unanswered pings before disconnect. /// public int MaxPingsOut { get; set; } = NatsProtocol.DefaultPingMaxOut; /// /// Gets or sets a value indicating whether to disable the short-first-ping grace behavior. /// public bool DisableShortFirstPing { get; set; } /// /// Gets or sets the maximum subscriptions allowed per connection. A value of 0 means unlimited. /// public int MaxSubs { get; set; } /// /// Gets or sets the maximum number of subject tokens allowed in a subscription. A value of 0 means unlimited. /// public int MaxSubTokens { get; set; } /// /// Gets or sets user-defined server tags exposed via monitoring endpoints. /// public Dictionary? Tags { get; set; } /// /// Gets or sets account definitions used to isolate tenants and permissions. /// public Dictionary? Accounts { get; set; } /// /// Gets or sets the global username for single-user authentication mode. /// public string? Username { get; set; } /// /// Gets or sets the global password for single-user authentication mode. /// public string? Password { get; set; } /// /// Gets or sets the global token for token-based authentication mode. /// public string? Authorization { get; set; } /// /// Gets or sets the configured list of explicit users for multi-user auth. /// public IReadOnlyList? Users { get; set; } /// /// Gets or sets the configured list of NKey users for signature-based auth. /// public IReadOnlyList? NKeys { get; set; } /// /// Gets or sets the fallback account user identity when no credentials are provided. /// public string? NoAuthUser { get; set; } /// /// Gets or sets external authorization callout settings. /// public Auth.ExternalAuthOptions? ExternalAuth { get; set; } /// /// Gets or sets proxy-based authentication settings. /// public Auth.ProxyAuthOptions? ProxyAuth { get; set; } /// /// Gets or sets the timeout for completing client authentication. /// public TimeSpan AuthTimeout { get; set; } = NatsProtocol.AuthTimeout; /// /// Gets or sets the HTTP monitoring port. A value of 0 disables monitoring. /// public int MonitorPort { get; set; } /// /// Gets or sets the bind host for monitoring endpoints. /// public string MonitorHost { get; set; } = "0.0.0.0"; /// /// Gets or sets an optional URL base path prefix for monitoring endpoints. /// public string? MonitorBasePath { get; set; } /// /// Gets or sets the HTTPS monitoring port. A value of 0 disables HTTPS monitoring. /// public int MonitorHttpsPort { get; set; } /// /// Gets or sets the duration of lame duck mode before final shutdown. /// public TimeSpan LameDuckDuration { get; set; } = NatsProtocol.DefaultLameDuckDuration; /// /// Gets or sets the grace period before starting client eviction during lame duck mode. /// public TimeSpan LameDuckGracePeriod { get; set; } = NatsProtocol.DefaultLameDuckGracePeriod; /// /// Gets or sets the optional PID file path written at startup. /// public string? PidFile { get; set; } /// /// Gets or sets the directory where dynamic listener port files are written. /// public string? PortsFileDir { get; set; } /// /// Gets or sets the primary configuration file path. /// public string? ConfigFile { get; set; } /// /// Gets or sets the output file path for server logs. /// public string? LogFile { get; set; } /// /// Gets or sets the maximum log file size before rotation. /// public long LogSizeLimit { get; set; } /// /// Gets or sets the number of rotated log files to retain. /// public int LogMaxFiles { get; set; } /// /// Gets or sets a value indicating whether debug-level logging is enabled. /// public bool Debug { get; set; } /// /// Gets or sets a value indicating whether protocol trace logging is enabled. /// public bool Trace { get; set; } /// /// Gets or sets a value indicating whether timestamps are included in log entries. /// public bool Logtime { get; set; } = true; /// /// Gets or sets a value indicating whether log timestamps use UTC instead of local time. /// public bool LogtimeUTC { get; set; } /// /// Gets or sets a value indicating whether logs are emitted to syslog. /// public bool Syslog { get; set; } /// /// Gets or sets the remote syslog endpoint, when remote syslog forwarding is enabled. /// public string? RemoteSyslog { get; set; } /// /// Gets or sets the profiling HTTP port. A value of 0 disables profiling endpoints. /// public int ProfPort { get; set; } /// /// Gets or sets the client advertise address provided to peers and clients. /// public string? ClientAdvertise { get; set; } /// /// Gets or sets a value indicating whether verbose protocol tracing is enabled. /// public bool TraceVerbose { get; set; } /// /// Gets or sets the maximum payload length included in trace log messages. /// public int MaxTracedMsgLen { get; set; } /// /// Gets or sets a value indicating whether sublist result caching is disabled. /// public bool DisableSublistCache { get; set; } /// /// Gets or sets how many connection errors are logged before log suppression starts. /// public int ConnectErrorReports { get; set; } = NatsProtocol.DefaultConnectErrorReports; /// /// Gets or sets how many reconnect errors are logged before log suppression starts. /// public int ReconnectErrorReports { get; set; } = NatsProtocol.DefaultReconnectErrorReports; /// /// Gets or sets a value indicating whether protocol headers are disabled. /// public bool NoHeaderSupport { get; set; } /// /// Gets or sets the number of closed-client records retained for monitoring. /// public int MaxClosedClients { get; set; } = NatsProtocol.DefaultMaxClosedClients; /// /// Gets or sets a value indicating whether system account setup is disabled. /// public bool NoSystemAccount { get; set; } /// /// Gets or sets the configured system account name used for server-level subjects. /// public string? SystemAccount { get; set; } /// /// Gets the set of fields explicitly provided on the command line to preserve override precedence. /// public HashSet InCmdLine { get; } = []; /// /// Gets or sets the server TLS certificate file path. /// public string? TlsCert { get; set; } /// /// Gets or sets the server TLS private key file path. /// public string? TlsKey { get; set; } /// /// Gets or sets the trusted CA certificate file used to validate peers. /// public string? TlsCaCert { get; set; } /// /// Gets or sets a value indicating whether client certificates are required and verified. /// public bool TlsVerify { get; set; } /// /// Gets or sets a value indicating whether certificate subject mapping to users is enabled. /// public bool TlsMap { get; set; } /// /// Gets or sets the timeout for TLS handshake and verification. /// public TimeSpan TlsTimeout { get; set; } = NatsProtocol.TlsTimeout; /// /// Gets or sets a value indicating whether clients must start with TLS handshake bytes. /// public bool TlsHandshakeFirst { get; set; } /// /// Gets or sets the fallback delay before trying plaintext parsing when handshake-first is enabled. /// public TimeSpan TlsHandshakeFirstFallback { get; set; } = NatsProtocol.DefaultTlsHandshakeFirstFallbackDelay; /// /// Gets or sets a value indicating whether non-TLS client connections are allowed. /// public bool AllowNonTls { get; set; } /// /// Gets or sets an optional TLS handshake rate limit. /// public long TlsRateLimit { get; set; } /// /// Gets or sets the pinned server certificate fingerprints accepted for peers. /// public HashSet? TlsPinnedCerts { get; set; } /// /// Gets or sets the minimum TLS protocol version allowed for client connections. /// public SslProtocols TlsMinVersion { get; set; } = SslProtocols.Tls12; /// /// Gets or sets OCSP stapling behavior for presented server certificates. /// public OcspConfig? OcspConfig { get; set; } /// /// Gets or sets a value indicating whether OCSP status on peer certificates is enforced. /// public bool OcspPeerVerify { get; set; } /// /// Gets or sets the trusted operator/public keys used to validate account JWTs. /// public string[]? TrustedKeys { get; set; } /// /// Gets or sets the account resolver used to fetch and cache account JWT metadata. /// public Auth.Jwt.IAccountResolver? AccountResolver { get; set; } /// /// Gets or sets per-subsystem log level overrides. /// public Dictionary? LogOverrides { get; set; } /// /// Gets or sets configured subject mapping transforms. /// public Dictionary? SubjectMappings { get; set; } /// /// Gets or sets MQTT bridge options. /// public MqttOptions? Mqtt { get; set; } /// /// Gets or sets cluster route listener and dial settings. /// public ClusterOptions? Cluster { get; set; } /// /// Gets or sets gateway federation settings. /// public GatewayOptions? Gateway { get; set; } /// /// Gets or sets leaf node listener and remote settings. /// public LeafNodeOptions? LeafNode { get; set; } /// /// Gets or sets JetStream persistence and API options. /// public JetStreamOptions? JetStream { get; set; } /// /// Gets a value indicating whether TLS server certificate and key paths are both configured. /// public bool HasTls => TlsCert != null && TlsKey != null; /// /// Gets or sets websocket listener and authentication options. /// public WebSocketOptions WebSocket { get; set; } = new(); /// /// Enables or disables tolerance for unknown top-level configuration fields. /// /// If , unknown fields are accepted without scan failure. public static void NoErrOnUnknownFields(bool noError) { _allowUnknownTopLevelFields = noError; } /// /// Gets a value indicating whether unknown top-level config keys are currently accepted. /// internal static bool AllowUnknownTopLevelFields => _allowUnknownTopLevelFields; /// /// Parses a comma-delimited route URL string into absolute route URIs. /// /// The route list from CLI or config. /// A list of valid absolute route endpoints. public static List RoutesFromStr(string routesStr) { if (string.IsNullOrWhiteSpace(routesStr)) return []; var routes = new List(); foreach (var route in routesStr.Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries)) { if (Uri.TryCreate(route, UriKind.Absolute, out var uri)) routes.Add(uri); } return routes; } /// /// Creates a deep copy of the options object for reload and runtime mutation workflows. /// /// A cloned options instance preserving effective values and command-line precedence flags. public NatsOptions Clone() { try { var json = JsonSerializer.Serialize(this); var clone = JsonSerializer.Deserialize(json) ?? new NatsOptions(); clone.InCmdLine.Clear(); foreach (var flag in InCmdLine) clone.InCmdLine.Add(flag); if (TlsPinnedCerts != null) clone.TlsPinnedCerts = [.. TlsPinnedCerts]; return clone; } catch { var clone = new NatsOptions(); CopyFrom(clone, this); clone.InCmdLine.Clear(); foreach (var flag in InCmdLine) clone.InCmdLine.Add(flag); if (Tags != null) clone.Tags = new Dictionary(Tags); if (SubjectMappings != null) clone.SubjectMappings = new Dictionary(SubjectMappings); if (TlsPinnedCerts != null) clone.TlsPinnedCerts = [.. TlsPinnedCerts]; return clone; } } /// /// Applies configuration parsed from raw config text to this options instance. /// /// Configuration file contents in NATS config format. public void ProcessConfigString(string data) { var parsed = ConfigProcessor.ProcessConfig(data); CopyFrom(this, parsed); _configDigest = ComputeDigest(data); } /// /// Returns the SHA-256 digest of the last processed config text. /// /// A lowercase hex digest for reload/change detection. public string ConfigDigest() => _configDigest; private static void CopyFrom(NatsOptions destination, NatsOptions source) { foreach (var prop in typeof(NatsOptions).GetProperties()) { if (!prop.CanRead || !prop.CanWrite) continue; prop.SetValue(destination, prop.GetValue(source)); } } private static string ComputeDigest(string text) { var hash = SHA256.HashData(Encoding.UTF8.GetBytes(text)); return Convert.ToHexString(hash).ToLowerInvariant(); } } /// /// Defines operational limits for JetStream API request handling. /// public sealed class JSLimitOpts { /// /// Gets or sets the maximum number of messages requested in a single batch pull. /// public int MaxRequestBatch { get; set; } /// /// Gets or sets the maximum number of pending acknowledgements per consumer. /// public int MaxAckPending { get; set; } /// /// Gets or sets the maximum number of high-availability JetStream assets. /// public int MaxHAAssets { get; set; } /// /// Gets or sets the duplicate window used for message de-duplication. /// public TimeSpan Duplicates { get; set; } /// /// Gets or sets the per-stream cap for in-flight batched pull requests. /// public int MaxBatchInflightPerStream { get; set; } /// /// Gets or sets the global cap for in-flight batched pull requests. /// public int MaxBatchInflightTotal { get; set; } /// /// Gets or sets the maximum payload size permitted for batch responses. /// public int MaxBatchSize { get; set; } /// /// Gets or sets the maximum wait time for a pull batch request. /// public TimeSpan MaxBatchTimeout { get; set; } } /// /// Configures operator-issued external auth callout claims and delegation. /// public sealed class AuthCallout { /// /// Gets or sets the issuer public key for validating callout-issued JWTs. /// public string Issuer { get; set; } = string.Empty; /// /// Gets or sets the account used for auth callout request subjects. /// public string Account { get; set; } = string.Empty; /// /// Gets or sets the users permitted to receive callout requests. /// public List AuthUsers { get; set; } = []; /// /// Gets or sets the XKey used for encrypting auth callout payloads. /// public string XKey { get; set; } = string.Empty; /// /// Gets or sets the accounts that can be authenticated via this callout. /// public List AllowedAccounts { get; set; } = []; } /// /// Contains trusted proxy connection settings for delegated client identity. /// public sealed class ProxiesConfig { /// /// Gets or sets the list of trusted proxy identities. /// public List Trusted { get; set; } = []; } /// /// Describes a trusted proxy identity key. /// public sealed class ProxyConfig { /// /// Gets or sets the trusted proxy public key. /// public string Key { get; set; } = string.Empty; } /// /// Captures dynamically assigned listener ports for process integration and discovery. /// public sealed class Ports { /// /// Gets or sets exposed client listener endpoints. /// public List Nats { get; set; } = []; /// /// Gets or sets exposed monitoring listener endpoints. /// public List Monitoring { get; set; } = []; /// /// Gets or sets exposed route listener endpoints. /// public List Cluster { get; set; } = []; /// /// Gets or sets exposed profiler listener endpoints. /// public List Profile { get; set; } = []; /// /// Gets or sets exposed websocket listener endpoints. /// public List WebSocket { get; set; } = []; /// /// Gets or sets exposed leaf node listener endpoints. /// public List LeafNodes { get; set; } = []; } /// /// Enumerates supported wire-compression mode names shared with config parsing. /// public static class CompressionModes { /// /// Disables transport compression. /// public const string Off = "off"; /// /// Accepts compression when the peer requests it. /// public const string Accept = "accept"; /// /// Uses fast S2 compression. /// public const string S2Fast = "s2_fast"; /// /// Uses balanced S2 compression. /// public const string S2Better = "s2_better"; /// /// Uses highest-ratio S2 compression. /// public const string S2Best = "s2_best"; /// /// Uses S2 framing without payload compression. /// public const string S2Uncompressed = "s2_uncompressed"; /// /// Chooses an S2 mode automatically from latency thresholds. /// public const string S2Auto = "s2_auto"; } /// /// Configures connection compression policy. /// public sealed class CompressionOpts { /// /// Gets or sets the selected compression mode. /// public string Mode { get; set; } = CompressionModes.Off; /// /// Gets or sets RTT thresholds used by automatic compression mode selection. /// public List RTTThresholds { get; set; } = [10, 50, 100, 250]; } /// /// Represents websocket listener and auth configuration for browser and websocket clients. /// public sealed class WebSocketOptions { /// /// Gets or sets the websocket bind host. /// public string Host { get; set; } = "0.0.0.0"; /// /// Gets or sets the websocket bind port. /// public int Port { get; set; } = -1; /// /// Gets or sets the advertised websocket URL authority. /// public string? Advertise { get; set; } /// /// Gets or sets the fallback no-auth user for websocket clients. /// public string? NoAuthUser { get; set; } /// /// Gets or sets the cookie name used to read JWT credentials. /// public string? JwtCookie { get; set; } /// /// Gets or sets the cookie name used to read username credentials. /// public string? UsernameCookie { get; set; } /// /// Gets or sets the cookie name used to read password credentials. /// public string? PasswordCookie { get; set; } /// /// Gets or sets the cookie name used to read token credentials. /// public string? TokenCookie { get; set; } /// /// Gets or sets a static websocket username override. /// public string? Username { get; set; } /// /// Gets or sets a static websocket password override. /// public string? Password { get; set; } /// /// Gets or sets a static websocket token override. /// public string? Token { get; set; } /// /// Gets or sets the websocket authentication timeout. /// public TimeSpan AuthTimeout { get; set; } = TimeSpan.FromSeconds(2); /// /// Gets or sets a value indicating whether websocket TLS is disabled. /// public bool NoTls { get; set; } /// /// Gets or sets the websocket TLS certificate path. /// public string? TlsCert { get; set; } /// /// Gets or sets the websocket TLS key path. /// public string? TlsKey { get; set; } /// /// Gets or sets a value indicating whether same-origin checks are enforced. /// public bool SameOrigin { get; set; } /// /// Gets or sets explicit allowed origins for cross-origin websocket upgrades. /// public List? AllowedOrigins { get; set; } /// /// Gets or sets a value indicating whether per-message websocket compression is enabled. /// public bool Compression { get; set; } /// /// Gets or sets the websocket handshake timeout. /// public TimeSpan HandshakeTimeout { get; set; } = TimeSpan.FromSeconds(2); /// /// Gets or sets an optional server ping interval for websocket connections. /// public TimeSpan? PingInterval { get; set; } /// /// Gets or sets additional HTTP headers included on websocket upgrade responses. /// public Dictionary? Headers { get; set; } /// /// Gets a value indicating whether websocket auth settings override top-level auth configuration. /// public bool AuthOverride { get; internal set; } }