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; }
}