Tharga.Communication 0.2.0

dotnet add package Tharga.Communication --version 0.2.0
                    
NuGet\Install-Package Tharga.Communication -Version 0.2.0
                    
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="Tharga.Communication" Version="0.2.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Tharga.Communication" Version="0.2.0" />
                    
Directory.Packages.props
<PackageReference Include="Tharga.Communication" />
                    
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 Tharga.Communication --version 0.2.0
                    
#r "nuget: Tharga.Communication, 0.2.0"
                    
#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 Tharga.Communication@0.2.0
                    
#: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=Tharga.Communication&version=0.2.0
                    
Install as a Cake Addin
#tool nuget:?package=Tharga.Communication&version=0.2.0
                    
Install as a Cake Tool

Tharga Communication

GitHub repo

A SignalR-based communication framework for .NET with built-in message handler patterns for request-response and fire-and-forget messaging.

Features

  • Fire-and-forget and request-response messaging patterns
  • Automatic message handler discovery via dependency injection
  • Client connection tracking with metadata
  • Automatic reconnection with configurable delays
  • Extensible client state storage
  • Pluggable API key validation with custom validators
  • Subscription-based messaging with type and data-level granularity

Getting started

Server

builder.AddThargaCommunicationServer(options =>
{
    options.RegisterClientStateService<MyClientStateService>();
    options.RegisterClientRepository<MemoryClientRepository<ClientConnectionInfo>, ClientConnectionInfo>();
});

app.UseThargaCommunicationServer();

Client

Add to appsettings.json:

{
  "Tharga": {
    "Communication": {
      "ServerAddress": "https://localhost:5001"
    }
  }
}
builder.AddThargaCommunicationClient();

API key validation

By default, the server accepts all connections. To require API keys, set ApiKeys on the server options:

builder.AddThargaCommunicationServer(options =>
{
    options.ApiKeys = ["my-secret-key", "rotation-key"];
    options.RegisterClientStateService<MyClientStateService>();
    options.RegisterClientRepository<MemoryClientRepository<ClientConnectionInfo>, ClientConnectionInfo>();
});

Clients send the key via CommunicationOptions.ApiKey (or appsettings.json):

builder.AddThargaCommunicationClient(o => o.ApiKey = "my-secret-key");
Custom validators

For more advanced scenarios (per-key lookup in a database, per-IP allowlists, integration with Tharga.Platform's API key management, etc.), register an IApiKeyValidator:

public class MyApiKeyValidator : IApiKeyValidator
{
    private readonly IApiKeyAdministrationService _keys;

    public MyApiKeyValidator(IApiKeyAdministrationService keys) => _keys = keys;

    public async Task<ApiKeyValidationResult> ValidateAsync(string apiKey, CancellationToken ct = default)
    {
        var key = await _keys.GetByValueAsync(apiKey, ct);
        return key is null
            ? new() { IsValid = false }
            : new() { IsValid = true, KeyId = key.Id.ToString(), KeyName = key.Name };
    }
}

builder.AddThargaCommunicationServer(options =>
{
    options.RegisterApiKeyValidator<MyApiKeyValidator>();
    // …
});

The validator decides everything — including whether to accept empty keys (return IsValid = true, KeyId = null to allow anonymous), or how to identify the matched key. KeyId and KeyName flow through to IClientConnectionInfo, where consumers can use them for admin UIs and audit logs.

Need HTTP context? Inject IHttpContextAccessor into your validator if you need access to the request (IP allowlists, custom headers, etc.). The framework intentionally does not pass HttpContext to keep IApiKeyValidator narrow and testable.

Client identity

The client sends four self-reported identity headers during connection: Instance (Guid generated per process run), Machine, Type (assembly name), and Version. Two of those — Machine and Type — can be overridden via options:

builder.AddThargaCommunicationClient(o =>
{
    o.ServerAddress = "https://localhost:5001";
    o.ClientType = "monitor-agent";        // override; defaults to entry assembly name
    o.ClientMachine = "us-east-prod-1";    // override; defaults to Environment.MachineName
});

Useful when one assembly hosts multiple roles, or when containerized hosts have meaningless hash-based hostnames.

Message handlers

// Fire-and-forget
public class MyHandler : PostMessageHandlerBase<MyMessage>
{
    public override Task Handle(MyMessage message) => Task.CompletedTask;
}

// Request-response
public class PingHandler : SendMessageHandlerBase<PingRequest, PingResponse>
{
    public override Task<PingResponse> Handle(PingRequest message) =>
        Task.FromResult(new PingResponse("Pong"));
}

Subscription messaging

Subscriptions allow the server to signal clients whether anyone is consuming a particular message type, so clients can skip sending data when no dashboard or consumer is active.

Server side (consumer/dashboard)
// Type-based: subscribe to all messages of a type
await using var sub = await serverCommunication.SubscribeAsync<CollectionDto>();

// Data-based: subscribe to a specific entity
await using var sub = await serverCommunication.SubscribeAsync<FarmDetailsDto>(farmId.ToString());

// Monitor active subscriptions
IReadOnlyDictionary<string, int> active = serverCommunication.GetSubscriptions();

In Blazor, tie the subscription to the page lifecycle:

@implements IAsyncDisposable
@inject IServerCommunication ServerCommunication

@code {
    private IAsyncDisposable? _subscription;

    protected override async Task OnInitializedAsync()
    {
        _subscription = await ServerCommunication.SubscribeAsync<FarmDetailsDto>(FarmId.ToString());
    }

    public async ValueTask DisposeAsync()
    {
        if (_subscription != null) await _subscription.DisposeAsync();
    }
}
Client side (agent/producer)
// Check before sending
if (clientCommunication.HasSubscribers<FarmDetailsDto>(farmId.ToString()))
    await clientCommunication.PostAsync(farmDetails);

// Or use the convenience method (no-ops when no subscribers)
await clientCommunication.PostIfSubscribedAsync(farmDetails, farmId.ToString());

// React to subscription changes
clientCommunication.SubscriptionChanged += (sender, e) =>
{
    Console.WriteLine($"{e.Topic}:{e.Key} → {(e.HasSubscribers ? "active" : "inactive")}");
};
Matching rules
  • Type-based (SubscribeAsync<T>() without key): wildcard — HasSubscribers<T>("anyKey") returns true.
  • Data-based (SubscribeAsync<T>("1") with key): specific — only HasSubscribers<T>("1") returns true.

For full documentation and examples, see the GitHub repository.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  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 (3)

Showing the top 3 NuGet packages that depend on Tharga.Communication:

Package Downloads
Tharga.MongoDB.Monitor.Client

Forwards MongoDB monitoring data to a central server via Tharga.Communication.

Tharga.MongoDB.Monitor.Server

Receives MongoDB monitoring data from remote agents via Tharga.Communication and aggregates it into the local IDatabaseMonitor.

Tharga.Communication.Mcp

Exposes Tharga.Communication runtime data (connected clients, active subscriptions, registered handlers) via MCP (Model Context Protocol). Plugs into Tharga.Mcp.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.2.0 20 5/16/2026
0.1.6 46 5/14/2026
0.1.5 311 5/2/2026
0.1.4 260 4/28/2026
0.1.3 182 4/16/2026
0.1.2 217 4/3/2026
0.1.1 114 4/2/2026
0.1.0 102 4/1/2026
0.0.3 100 3/25/2026
0.0.2 143 1/31/2026
0.0.1 123 1/19/2026