Phosphor.Core.Sdk 0.2.2

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

phosphor-adapter-sdk

The contract library for the Phosphor real-time operational visualization platform. Defines the interfaces, message envelopes, and plugin discovery contract that all Phosphor adapters implement.

Published to NuGet as Phosphor.Core.Sdk.

What It Defines

This package is interfaces only — no transport dependencies, no runtime behavior. It defines the contracts that phosphor-core and all adapters agree on.

Bus Connection (IBusConnection)

The transport abstraction. Adapters publish, subscribe, and issue request/reply queries through this interface without knowing whether the underlying transport is an in-process bus or a NATS cluster.

public interface IBusConnection : IAsyncDisposable
{
    Task PublishAsync(string subject, PhosphorMessage message, CancellationToken ct = default);
    IDisposable Subscribe(string pattern, Func<PhosphorMessage, Task> handler);
    Task<PhosphorMessage?> RequestAsync(string subject, PhosphorMessage request, TimeSpan timeout, CancellationToken ct = default);
}

phosphor-core provides the implementations (InternalBusConnection, NatsBusConnection). Adapters never reference a transport directly.

Adapter Lifecycle (IPhosphorAdapter)

Every adapter implements this. Core discovers, initializes, and manages adapters through this interface.

public interface IPhosphorAdapter : IAsyncDisposable
{
    string AdapterId { get; }
    string DisplayName { get; }
    AdapterCapabilities Capabilities { get; }

    // busConnection is null in standalone mode — adapter creates its own connection.
    // busConnection is provided when hosted by phosphor-core.
    Task InitializeAsync(PhosphorAdapterConfig config, IBusConnection? busConnection = null, CancellationToken ct = default);

    Task StartAsync(CancellationToken ct = default);
    Task StopAsync(CancellationToken ct = default);
    Task PublishAsync(PhosphorCommand command, CancellationToken ct = default);
}
Dual-mode initialization

The same adapter binary works in two contexts without code changes:

  • Core-hosted: phosphor-core injects its IBusConnection via InitializeAsync. The adapter publishes through whatever transport core configured.
  • Standalone: busConnection is null. The adapter null-checks it in InitializeAsync and creates its own connection using settings from config.
public async Task InitializeAsync(PhosphorAdapterConfig config, IBusConnection? busConnection = null, CancellationToken ct = default)
{
    _bus = busConnection ?? await CreateStandaloneConnectionAsync(config, ct);
}

Adapter Capabilities

[Flags]
public enum AdapterCapabilities
{
    None            = 0,
    CanSubscribe    = 1 << 0,   // Produces inbound telemetry
    CanPublish      = 1 << 1,   // Accepts outbound commands (write-back)
    CanDiscoverTopology = 1 << 2, // Emits structural/topology events
    CanReplay       = 1 << 3,   // Historical replay (JetStream, Kafka offsets)
    CanPersist      = 1 << 4,   // Stores and retrieves historical data
    CanQuery        = 1 << 5,   // Responds to request/reply queries
    CanStream       = 1 << 6,   // Continuous streaming output
    ReadOnly        = CanSubscribe,
    ReadWrite       = CanSubscribe | CanPublish,
}

Core uses capabilities for routing — e.g., history query requests route to the adapter advertising CanPersist | CanQuery.

Extended Interfaces

  • IStructuralAdapter — for adapters that emit topology events (hardware discovery, Prometheus targets, K8s resources)
  • IHealthReporter — adapter self-monitoring (throughput, errors, connection state)

Message Envelope

public sealed class PhosphorMessage
{
    public required string Subject { get; init; }
    public required byte[] Payload { get; init; }
    public long Timestamp { get; init; }             // Unix epoch ms, source time
    public Dictionary<string, string>? Headers { get; init; }
    public string? ReplyTo { get; init; }            // For request/reply patterns
}

Payload is raw bytes — by convention JSON, but any format is valid. The consumer owns deserialization.

Query Contract

Standardized request/reply messages for historical data access. Any adapter can issue these via IBusConnection.RequestAsync. Whichever adapter advertises CanPersist | CanQuery handles them.

public sealed class HistoryQueryRequest
{
    public required string SubjectPattern { get; init; }
    public required DateTime StartTime { get; init; }
    public required DateTime EndTime { get; init; }
    public TimeSpan? DownsampleInterval { get; init; }
    public string? AggregateFunction { get; init; }  // avg, min, max, sum, count
    public int? MaxResults { get; init; }
}

Well-known query subjects: phosphor.query.history, phosphor.query.latest, phosphor.query.aggregate.

Plugin Discovery

Adapters ship as DLL assemblies. Core discovers them by scanning the adapters/ folder at startup for types implementing IPhosphorAdapter. The implementing class must have a parameterless constructor — config and bus wiring happen through InitializeAsync.

Declare adapter metadata with the assembly attribute:

[assembly: PhosphorAdapter(
    Name = "hwmon",
    Description = "Hardware sensor monitoring via LibreHardwareMonitor",
    Version = "1.0.0",
    License = "Apache-2.0"
)]

Open source adapters are included in the distribution's adapters/ folder. Proprietary adapters are separate DLLs dropped into the same folder.

Subject Naming Convention

Hierarchical, dot-separated: domain.category.source.metric

Examples: sensors.temperature.floor1.zone3, transfers.node7.progress

Supports NATS-compatible wildcards: * (single token), > (deep wildcard).

Installation

dotnet add package Phosphor.Core.Sdk

Writing an Adapter

using Phosphor.Core.Sdk.Abstractions;
using Phosphor.Core.Sdk.Configuration;
using Phosphor.Core.Sdk.Envelopes;
using Phosphor.Core.Sdk.Health;

[assembly: PhosphorAdapter(Name = "my-adapter", Description = "Example", Version = "1.0.0")]

public class MyAdapter : IPhosphorAdapter, IHealthReporter
{
    public string AdapterId => _config.AdapterId;
    public string DisplayName => _config.DisplayName;
    public AdapterCapabilities Capabilities => AdapterCapabilities.ReadOnly;

    private IBusConnection _bus = null!;
    private MyAdapterConfig _config = null!;

    public MyAdapter() { }

    public async Task InitializeAsync(PhosphorAdapterConfig config, IBusConnection? busConnection = null, CancellationToken ct = default)
    {
        _config = (MyAdapterConfig)config;
        _bus = busConnection ?? await CreateStandaloneConnectionAsync(_config, ct);
    }

    public Task StartAsync(CancellationToken ct = default)
    {
        // Begin reading from external system.
        // On each data point:
        //   var msg = PhosphorSerializer.CreateJsonMessage("sensors.temp.zone1", value);
        //   await _bus.PublishAsync(msg.Subject, msg, ct);
        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken ct = default) => Task.CompletedTask;

    public Task PublishAsync(PhosphorCommand command, CancellationToken ct = default)
        => throw new NotSupportedException("Read-only adapter.");

    public AdapterHealthReport GetHealthReport() => new()
    {
        AdapterId = AdapterId,
        ConnectionState = AdapterConnectionState.Connected,
    };

    public ValueTask DisposeAsync() => ValueTask.CompletedTask;

    private Task<IBusConnection> CreateStandaloneConnectionAsync(MyAdapterConfig cfg, CancellationToken ct)
        => throw new NotImplementedException("Provide a standalone IBusConnection.");
}

public class MyAdapterConfig : PhosphorAdapterConfig
{
    public string ConnectionString { get; set; } = string.Empty;
}

License

Apache 2.0 — see LICENSE.

Releasing a New Version

CI runs on every push to main (build + test). Publishing to NuGet only fires when a version tag is pushed.

  1. Update <Version> in src/Phosphor.Core.Sdk/Phosphor.Core.Sdk.csproj, commit, and push to main.
  2. Tag the commit and push the tag:
git tag v0.2.0
git push origin v0.2.0

The v*.*.* tag triggers the Pack and Push steps in the workflow. The NUGET_API_KEY secret must be set in Settings → Secrets and variables → Actions.

Part of the Phosphor Platform

Phosphor translates operational telemetry into real-time 3D visualizations using Unreal Engine 5. Learn more at github.com/phosphor-unreal.

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 was computed.  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.
  • net8.0

    • No dependencies.

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
0.2.2 239 3/16/2026
0.2.0 139 3/16/2026
0.1.0 136 3/10/2026