LunaLink 1.0.50

dotnet add package LunaLink --version 1.0.50
                    
NuGet\Install-Package LunaLink -Version 1.0.50
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="LunaLink" Version="1.0.50" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="LunaLink" Version="1.0.50" />
                    
Directory.Packages.props
<PackageReference Include="LunaLink" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add LunaLink --version 1.0.50
                    
#r "nuget: LunaLink, 1.0.50"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package LunaLink@1.0.50
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=LunaLink&version=1.0.50
                    
Install as a Cake Addin
#tool nuget:?package=LunaLink&version=1.0.50
                    
Install as a Cake Tool

Reliable node-to-master communication for .NET industrial systems.

NuGet NuGet Downloads .NET 10 License

LunaLink is a compact binary protocol and .NET library for moving real-time tag data between distributed edge nodes and a central master. It is designed for SCADA, industrial IoT, telemetry, and unreliable or bandwidth-constrained networks.

It powers communication inside Lunasoft SCADA and can also be embedded in any .NET application.

  • Reliable delivery - persistent SQLite outbox, ordered replay, acknowledgements, duplicate detection, and sequence-gap recovery.
  • Efficient transport - MessagePack serialization with automatic LZ4 compression.
  • Industrial data model - typed values, timestamps, quality codes, tag inventory, snapshots, history, and remote writes.
  • Resilient connections - automatic reconnect, heartbeat telemetry, health tracking, and backlog collapse through a full-state baseline.
  • Secure by design - authentication token, TLS 1.3, optional mutual TLS, and local RSA-signed license validation.
  • Host-friendly - server and client run as standard BackgroundService components with dependency injection and structured logging.

Install

dotnet add package LunaLink

LunaLink currently targets .NET 10.

Official examples

The LunaLink Examples repository contains complete Windows Forms Master and Node applications with lifecycle controls, live telemetry, structured logs, configuration, TLS guidance, and troubleshooting.

git clone https://github.com/lunasoft-llc/lunalink-examples.git

Quick start

1. Run a master

Implement the callback that connects LunaLink to your historian, HMI, or application services:

using LunaLink;

public sealed class MasterCallback : ILunaLinkServerCallback
{
    public Task OnNodeHelloAsync(
        string nodeId,
        string nodeName,
        string remoteEndpoint,
        CancellationToken ct)
    {
        Console.WriteLine($"Connected: {nodeName} ({nodeId}) from {remoteEndpoint}");
        return Task.CompletedTask;
    }

    public Task ProcessDataPointAsync(
        Guid tagId,
        string? tagName,
        object? value,
        LunaLinkQuality quality,
        DateTimeOffset timestamp,
        LunaLinkDataType dataType)
    {
        Console.WriteLine($"{tagName ?? tagId.ToString()} = {value} [{quality}]");
        return Task.CompletedTask;
    }

    public Task<List<LunaLinkTagSnapshot>> GetSnapshotAsync(
        IEnumerable<Guid>? tagIds,
        CancellationToken ct) =>
        Task.FromResult(new List<LunaLinkTagSnapshot>());
}

Register the master in your host:

builder.Services.Configure<LunaLinkOptions>(
    builder.Configuration.GetSection(LunaLinkOptions.Section));
builder.Services.AddSingleton<LunaLinkNodeTracker>();
builder.Services.AddSingleton<ILunaLinkServerCallback, MasterCallback>();
builder.Services.AddHostedService<LunaLinkServer>();
{
  "LunaLink": {
    "Port": 7788,
    "AuthToken": "replace-with-a-secret"
  }
}

2. Connect an edge node

Implement the node callback. The first four methods are required; inventory and history methods have safe empty defaults and can be added when needed.

using LunaLink;

public sealed class NodeCallback : ILunaLinkClientCallback
{
    public Task<List<LunaLinkTagSnapshot>> GetSnapshotAsync(
        IEnumerable<Guid>? tagIds,
        CancellationToken ct) =>
        Task.FromResult(new List<LunaLinkTagSnapshot>());

    public Task ProcessDataPointAsync(
        Guid tagId,
        string? tagName,
        object? value,
        LunaLinkQuality quality,
        DateTimeOffset timestamp,
        LunaLinkDataType dataType) => Task.CompletedTask;

    public Task<bool> WriteTagAsync(
        Guid tagId,
        string? tagName,
        object? value,
        CancellationToken ct) => Task.FromResult(false);

    public Task<int> GetConnectedDeviceCountAsync(CancellationToken ct) =>
        Task.FromResult(0);
}

Register one shared client instance as both the hosted service and the application-facing ILunaLinkClient:

builder.Services.Configure<LunaLinkOptions>(
    builder.Configuration.GetSection(LunaLinkOptions.Section));
builder.Services.AddSingleton<ILunaLinkClientCallback, NodeCallback>();
builder.Services.AddSingleton<LunaLinkOutbox>();
builder.Services.AddSingleton<LunaLinkClient>();
builder.Services.AddSingleton<ILunaLinkClient>(sp =>
    sp.GetRequiredService<LunaLinkClient>());
builder.Services.AddHostedService(sp =>
    sp.GetRequiredService<LunaLinkClient>());
{
  "LunaLink": {
    "MasterHost": "10.0.0.10",
    "Port": 7788,
    "NodeId": "line-1-edge",
    "NodeName": "Production Line 1",
    "AuthToken": "replace-with-a-secret"
  }
}

Send one or more tag changes from application code:

public sealed class TelemetryPublisher(ILunaLinkClient client)
{
    public Task PublishTemperatureAsync(Guid tagId, double value, CancellationToken ct) =>
        client.SendTagDeltaAsync(
        [
            (
                tagId,
                "reactor.temperature",
                value,
                LunaLinkQuality.Good,
                DateTimeOffset.UtcNow,
                LunaLinkDataType.Float64
            )
        ],
        ct);
}

The client reconnects and replays pending data automatically.

How it works

 Edge node                         Master
    |                                 |
    |------ Hello + authentication -->|
    |<--------- Accepted -------------|
    |------ Ordered tag delta -------->|
    |<------- Application ACK --------|
    |------ Heartbeat + inventory ---->|
    |<--------- Heartbeat ACK --------|
    |<------- Remote tag write -------|
    |--------- Write result ---------->|

Every frame has a fixed header, protocol version, message type, flags, sequence number, payload length, and CRC-16 integrity value. Payloads are MessagePack encoded and larger payloads are compressed with LZ4.

When a node is offline, tag batches are stored in SQLite. Confirmed batches are removed only after the master returns an application-level acknowledgement. Large backlogs can be replaced safely by a current full-state baseline while normal-sized backlogs retain ordered replay.

Configuration

Setting Default Applies to Description
Port 7788 Both TCP port
UseTls false Both Enables TLS transport
AuthToken null Both Shared authentication token
MasterHost null Node Master hostname or IP address
NodeId generated GUID Node Stable unique node identity
NodeName machine name Node Human-readable node name
ReconnectDelaySeconds 5 Node Initial reconnect delay
MaxRetryDelaySeconds 60 Node Maximum reconnect delay
AckTimeoutSeconds 5 Node Delivery acknowledgement timeout
OutboxReplayCollapseThreshold 1024 Node Pending-batch threshold for full-state resync
OutboxMaxBytes 512 MiB Node Persistent outbox size limit
OutboxRetentionDays 7 Node Persistent outbox retention
HistorySyncMode Parallel Node Discard, Full, or Parallel
HistoryBatchSize 2000 Node Samples per history batch
HistoryBatchDelayMilliseconds 100 Node Delay between history batches
LicenseKey null Node Optional LunaLink license key

See LunaLinkOptions.cs for the complete current option set.

TLS and mutual TLS

Master configuration:

{
  "LunaLink": {
    "Port": 7788,
    "UseTls": true,
    "CertificatePath": "certs/master.pfx",
    "CertificatePassword": "use-a-secret-provider",
    "ClientCertificateRequired": true
  }
}

Node configuration:

{
  "LunaLink": {
    "MasterHost": "scada.example.com",
    "Port": 7788,
    "UseTls": true,
    "ValidateServerCertificate": true,
    "ClientCertificatePath": "certs/node.pfx",
    "ClientCertificatePassword": "use-a-secret-provider"
  }
}

Keep credentials outside committed configuration in production. Do not disable server-certificate validation outside controlled development environments.

Data model

Supported data types:

Bool, Int16, UInt16, Int32, UInt32, Float32, Float64, String, Enum, and Json.

Quality states:

Good, Bad, Uncertain, and NotConnected.

The protocol also supports:

  • point-in-time snapshots;
  • device and tag inventory;
  • subscription filtering;
  • master-to-node remote writes with results;
  • node heartbeat and health telemetry;
  • ordered history synchronization;
  • live client reconfiguration.

Reliability guarantees

  • A node sequence is persisted with each outbox batch.
  • The master detects duplicates and missing sequences persistently.
  • A batch remains queued until its application acknowledgement is received.
  • Corrupt outbox databases are quarantined instead of silently reused.
  • Reconnect and replay preserve ordering during normal recovery.
  • Full-state resync resolves large or unavailable sequence gaps.

These guarantees cover LunaLink transport. Your callback implementation remains responsible for application-specific persistence and business validation.

Licensing

Mode Unique tags Persistent offline outbox Cost
Freemium 50 No Free
Licensed Unlimited Yes License required

LunaLink supports local RSA-signed keys and periodic online validation. If the licensing service is temporarily unavailable after a successful validation, a grace period applies.

Visit lunasoft.az for licensing information.

Package dependencies

Support

When reporting a problem, include the LunaLink version, operating system, .NET version, node/master role, and relevant sanitized logs. Never publish authentication tokens, license keys, or private certificates.

License

Copyright (c) 2025-2026 LunaSoft. All rights reserved.

LunaLink is distributed under a proprietary license. See LICENSE.txt.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.50 103 8/10/2026
Loading failed