# Operations Overview

This document covers running the server, graceful shutdown, connecting clients, and the test suite.

---

## Running the Server

The host application is `src/NATS.Server.Host`. Run it with `dotnet run`:

```bash
# Default (port 4222, bind 0.0.0.0)
dotnet run --project src/NATS.Server.Host

# Custom port
dotnet run --project src/NATS.Server.Host -- -p 14222

# Custom bind address and name
dotnet run --project src/NATS.Server.Host -- -a 127.0.0.1 -n my-server
```

Arguments after `--` are passed to the program. The three supported flags are `-p`/`--port`, `-a`/`--addr`, and `-n`/`--name`. See [Configuration Overview](../Configuration/Overview.md) for the full CLI reference.

On startup, the server logs the address it is listening on:

```
[14:32:01 INF] Listening on 0.0.0.0:4222
```

### Full host setup

`Program.cs` initializes Serilog, parses CLI arguments, starts the server, and handles graceful shutdown. The startup sequence does two passes over `args`: the first scans for `-c` to load a config file as the base `NatsOptions`, and the second applies remaining CLI flags on top (CLI flags always win over the config file):

```csharp
// First pass: scan args for -c flag to get config file path
string? configFile = null;
for (int i = 0; i < args.Length; i++)
{
    if (args[i] == "-c" && i + 1 < args.Length)
    {
        configFile = args[++i];
        break;
    }
}

var options = configFile != null
    ? ConfigProcessor.ProcessConfigFile(configFile)
    : new NatsOptions();

// Second pass: apply CLI args on top of config-loaded options
for (int i = 0; i < args.Length; i++)
{
    switch (args[i])
    {
        case "-p" or "--port" when i + 1 < args.Length:
            options.Port = int.Parse(args[++i]);
            options.InCmdLine.Add("Port");
            break;
        case "-a" or "--addr" when i + 1 < args.Length:
            options.Host = args[++i];
            options.InCmdLine.Add("Host");
            break;
        case "-n" or "--name" when i + 1 < args.Length:
            options.ServerName = args[++i];
            options.InCmdLine.Add("ServerName");
            break;
        // ... additional flags: -m, --tls*, -D/-V/-DV, -l, -c, --pid, etc.
    }
}

using var loggerFactory = new Serilog.Extensions.Logging.SerilogLoggerFactory(Log.Logger);
using var server = new NatsServer(options, loggerFactory);

server.HandleSignals();

Console.CancelKeyPress += (_, e) =>
{
    e.Cancel = true;
    _ = Task.Run(async () => await server.ShutdownAsync());
};

_ = server.StartAsync(CancellationToken.None);
await server.WaitForReadyAsync();
server.WaitForShutdown();
```

`InCmdLine` tracks which options were supplied on the command line so that a subsequent config-file reload does not overwrite them.

---

## Graceful Shutdown

Pressing Ctrl+C triggers `Console.CancelKeyPress`. The handler sets `e.Cancel = true` — this prevents the process from terminating immediately — and dispatches `server.ShutdownAsync()` on a background task. `WaitForShutdown()` blocks the main thread until shutdown completes. The `finally` block runs `Log.CloseAndFlush()` to ensure all buffered log output is written before the process exits.

`server.HandleSignals()` registers additional OS signal handlers (SIGHUP for config reload, SIGUSR1 for log file reopen on Unix) before the main loop starts.

---

## Testing

Run the full test suite with:

```bash
dotnet test
```

The test project is at `tests/NATS.Server.Tests/`. It uses xUnit with Shouldly for assertions.

### Test summary

The test project contains 99 test files across seven areas:

- **Auth/TLS** (23 files) — authenticators (token, username/password, NKey, JWT), client permissions, OCSP, TLS connection wrapping, TLS rate limiting, account isolation, permission integration
- **JetStream/RAFT** (23 files) — stream API, consumer API, publish, pull/push delivery, ack redelivery, retention policies, mirroring/sourcing, config validation, FileStore, MemStore, store contract, RAFT election, replication, and snapshot catchup
- **Monitoring/Config** (15 files) — HTTP monitor endpoints, `/jsz`, config file parsing (lexer + parser), config reload, `NatsOptions`, server stats, subsz, account stats, account resolver, logging, Go parity runner
- **Client lifecycle** (12 files) — `NatsClient` flags, closed-reason tracking, trace mode, write loop, no-responders, verbose mode, RTT, response tracker, internal client, event system, import/export, response routing
- **Protocol/Parser** (7 files) — `NatsParser` commands, subject validation and wildcard matching, `SubList` trie, NATS header parser, subject transforms
- **Clustering** (4 files) — route handshake, route subscription propagation, gateway/leaf bootstrap, cluster JetStream config processor
- **WebSocket** (9 files in `WebSocket/`) — frame read/write, compression, upgrade handshake, origin checking, connection handling, integration, options, constants
- **Integration** (6 files) — end-to-end tests using `NATS.Client.Core`, system events, system request-reply, auth integration, NKey integration, permission integration

### Test categories

**SubjectMatchTests** — `[Theory]` cases verifying `SubjectMatch.IsValidSubject`, `SubjectMatch.IsValidPublishSubject`, and `SubjectMatch.MatchLiteral`. Covers empty strings, leading/trailing dots, embedded spaces, `>` in non-terminal position, and all wildcard combinations.

**SubListTests** — `[Fact]` tests exercising the `SubList` trie directly: literal insert and match, empty result, `*` wildcard at various token levels, `>` wildcard, root `>`, multiple overlapping subscriptions, remove, queue group grouping, `Count` tracking, and cache invalidation after a wildcard insert.

**ParserTests** — `async [Fact]` tests that write protocol bytes into a `Pipe` and assert on the resulting `ParsedCommand` list. Covers `PING`, `PONG`, `CONNECT`, `SUB` (with and without queue group), `UNSUB` (with and without `max-messages`), `PUB` (with payload, with reply-to, zero payload), `HPUB` (with header), `INFO`, multiple commands in a single buffer, and case-insensitive parsing.

**ClientTests** — `async [Fact]` tests using a real loopback socket pair. Verifies that `NatsClient` sends an `INFO` frame immediately on connection, and that it responds `PONG` to a `PING` after `CONNECT`.

**ServerTests** — `async [Fact]` tests that start `NatsServer` on a random port. Verifies `INFO` on connect, basic pub/sub delivery (`MSG` format), and wildcard subscription matching.

**IntegrationTests** — `async [Fact]` tests using the official `NATS.Client.Core` NuGet package. Verifies end-to-end protocol compatibility with a real NATS client library: basic pub/sub, `*` wildcard delivery, `>` wildcard delivery, fan-out to two subscribers, and `PingAsync`.

Integration tests use `NullLoggerFactory.Instance` for the server so test output is not cluttered with server logs.

---

## Connecting with Standard NATS Clients

The server speaks the standard NATS wire protocol. Any NATS client library can connect to it.

```bash
# Using the nats CLI tool
nats sub "test.>" --server nats://127.0.0.1:4222
nats pub test.hello "world" --server nats://127.0.0.1:4222
```

From .NET, using `NATS.Client.Core`:

```csharp
var opts = new NatsOpts { Url = "nats://127.0.0.1:4222" };
await using var conn = new NatsConnection(opts);
await conn.ConnectAsync();
await conn.PublishAsync("test.hello", "world");
```

---

## Go Reference Server

The Go reference implementation can be built from `golang/nats-server/` for comparison testing:

```bash
# Build
cd golang/nats-server && go build

# Run on default port
./nats-server

# Run on custom port
./nats-server -p 14222
```

The Go server is useful for verifying that the .NET port produces identical protocol output and behaves the same way under edge-case client interactions.

---

## Known Gaps vs Go Reference

The following areas have partial or stub implementations compared to the Go reference server:

- **MQTT listener** — config is parsed and the option is recognized, but no MQTT transport is implemented
- **Route message routing** — the route TCP connection and handshake are established, but `RMSG` forwarding is not implemented; messages are not relayed to peer nodes
- **Gateways** — the listener stub accepts connections, but no inter-cluster bridging or interest-only filtering is implemented
- **Leaf nodes** — the listener stub accepts connections, but no hub-and-spoke topology or subject sharing is implemented
- **JetStream API surface** — only `STREAM.CREATE`, `STREAM.INFO`, `CONSUMER.CREATE`, and `CONSUMER.INFO` API subjects are handled; all others return a not-found error response
- **FileStore durability** — the file store maintains a full in-memory index, performs per-write I/O without batching, and rewrites the full block on trim; it is not production-safe under load
- **RAFT network transport** — the RAFT implementation uses in-process message passing only; there is no network transport, so consensus does not survive process restarts or span multiple server instances

---

## Related Documentation

- [Configuration Overview](../Configuration/Overview.md)
- [Server Overview](../Server/Overview.md)
- [Protocol Overview](../Protocol/Overview.md)

<!-- Last verified against codebase: 2026-02-23 -->
