Skip to content

Server BlueprintΒΆ

Learning Signals

  • Level: Intermediate
  • Time: 15–20 minutes
  • Prerequisites: Quickstart

This page provides the recommended architectural blueprint for a production-grade Nalix server. It moves beyond the single-file quickstart to a shape that scales as features, security policies, and diagnostic needs grow.


πŸ—οΈ Startup ArchitectureΒΆ

A robust server follows a deterministic sequence:

flowchart LR
    subgraph Setup ["Phase 1: Setup"]
        direction TB
        Config["Load Configuration"]
        Reg["Register Services"]
    end

    subgraph Pipeline ["Phase 2: Runtime Pipeline"]
        direction TB
        Disp["Activate Dispatch"]
        Trans["Start Transport"]
    end

    subgraph Ext ["Phase 3: Extensions"]
        direction TB
        Boot["Hosted Services"]
    end

    Config --> Reg
    Reg --> Disp
    Disp --> Trans
    Trans --> Boot

Why this blueprint?

Treating the server startup as a sequence of discrete layers ensures that when the socket starts accepting traffic, every security policy and reporting hook is already "warm" and ready.


Consistency is key for maintainability. We recommend the following layout for a Nalix server project:

πŸ“‚ Server/
β”œβ”€β”€ πŸ“‚ Bootstrap/           # Service & Dispatch wiring
β”œβ”€β”€ πŸ“‚ Protocols/           # Transport protocol definitions
β”œβ”€β”€ πŸ“‚ Handlers/            # Application logic (Controllers)
β”œβ”€β”€ πŸ“‚ Middleware/          # Security & Policy filters
β”œβ”€β”€ πŸ“‚ Metadata/            # Custom convention providers
└── πŸ“‚ Hosting/             # Entry point & Lifecycle management

πŸš€ The Blueprint StepsΒΆ

1. Configuration & ValidationΒΆ

Load and validate focused network options before starting the runtime. Fail-fast is better than a runtime error in a worker loop.

var socket = ConfigurationManager.Instance.Get<NetworkSocketOptions>();
socket.Validate();

var dispatchOptions = ConfigurationManager.Instance.Get<DispatchOptions>();
dispatchOptions.Validate();

var connectionLimits = ConfigurationManager.Instance.Get<ConnectionLimitOptions>();
connectionLimits.Validate();

If you use the hosting builder, src/Nalix.Hosting/NetworkApplicationBuilder.cs already does this step for every Configure<TOptions>(...) registration by invoking public Validate() when the option type exposes it.

2. Registry InitializationΒΆ

The InstanceManager serves as the runtime core for shared infrastructure when you wire the stack manually. The hosting builder performs the same registration work for you.

using Microsoft.Extensions.Logging;
using Nalix.Logging;

ILogger logger = NLogix.Host.Instance;

InstanceManager.Instance.Register<ILogger>(logger);
InstanceManager.Instance.Register<IPacketRegistry>(packetRegistry);

3. Dispatch & Middleware SetupΒΆ

Define your application pipeline in a centralized location.

PacketDispatchChannel dispatch = new(options =>
{
    options.WithLogging(logger)
           .WithErrorHandling((ex, opcode) => logger.Error($"dispatch 0x{opcode:X4}", ex))
           .WithMiddleware(new AuthMiddleware())
           .WithMiddleware(new AuditMiddleware())
           .WithHandler(() => new AccountHandlers())
           .WithHandler(() => new MatchHandlers());
});

Centralized Wiring

Keep all WithMiddleware and WithHandler calls in a single bootstrap class. Spreading these across the codebase makes startup order nearly impossible to debug.

4. Protocol ImplementationΒΆ

Keep your protocol thin. It should strictly act as the bridge between raw frames and clean messages.

public sealed class ServerProtocol : Protocol
{
    private readonly PacketDispatchChannel _dispatch;

    public ServerProtocol(PacketDispatchChannel dispatch)
    {
        _dispatch = dispatch;
        this.IsAccepting = true;
    }

    public override void ProcessMessage(object? sender, IConnectEventArgs args)
        => _dispatch.HandlePacket(args.Lease, args.Connection);
}

That shape matches src/Nalix.Hosting/DefaultProtocol.cs, which is the built-in implementation used when you do not need custom protocol hooks.


⚑ Lifecycle Management¢

Managing the Activation and Shutdown order is critical for preventing connection "dangling."

phase Action Detail
Startup dispatch.Activate() Warm up the dispatch pipeline before listeners begin accepting traffic.
Startup listener.Activate() Open the socket and begin accepting.
Shutdown listener.Deactivate() + Dispose() Stop accepting and release transport resources first.
Shutdown protocol.Dispose() Dispose protocols after listeners stop.
Shutdown dispatch.Deactivate() Stop the dispatch pipeline after listeners and hosted services.

This order comes directly from src/Nalix.Hosting/NetworkApplication.cs, where ActivateAsync() prepares callbacks, activates dispatch, starts listeners, then activates hosted services; DeactivateAsync() reverses that order and finally waits for ITaskManager.WaitGroupAsync("net/*") and "time/*".


πŸ“Š Diagnostics SurfaceΒΆ

A production-ready blueprint always includes a way to query the internal health.

  • listener.GenerateReport() - Listener-side transport state and counters.
  • protocol.GenerateReport() - Protocol-side counters and post-process diagnostics.
  • dispatch.GenerateReport() - Dispatch runtime state.

Pro-Tip

Even if you don't have an Admin API, ensure your logs occasionally output these reports during periods of high traffic.