OutWit.Common.Platform
1.2.0
dotnet add package OutWit.Common.Platform --version 1.2.0
NuGet\Install-Package OutWit.Common.Platform -Version 1.2.0
<PackageReference Include="OutWit.Common.Platform" Version="1.2.0" />
<PackageVersion Include="OutWit.Common.Platform" Version="1.2.0" />
<PackageReference Include="OutWit.Common.Platform" />
paket add OutWit.Common.Platform --version 1.2.0
#r "nuget: OutWit.Common.Platform, 1.2.0"
#:package OutWit.Common.Platform@1.2.0
#addin nuget:?package=OutWit.Common.Platform&version=1.2.0
#tool nuget:?package=OutWit.Common.Platform&version=1.2.0
OutWit.Common.Platform
Cross-platform helpers for collecting machine system information (OS / CPU / GPU / memory / storage / health), a stable hashed machine identity, and semantic standard directories. Internally organised as a small Strategy split per OS so that new platforms can be added by writing one class — Windows, Linux, macOS and Android are bundled out of the box.
Why
A distributed-compute platform needs every participating node to advertise its capabilities (what OS, how much RAM, which GPU, how much temp space) and its current health (CPU load, idle/active state) in a uniform shape that the scheduler can match against activity requirements. This package is the substrate that produces those shapes — without itself making any scheduling decisions.
Install
dotnet add package OutWit.Common.Platform
Public surface
PlatformKind // enum: Windows / Linux / MacOS / Android / Unknown
PlatformDetector // static GetCurrentPlatform()
ISystemProfileProvider // → SystemProfile { Os, Cpu, Memory, Gpus[], TempStorage }
ISystemHealthProvider // → SystemHealthSnapshot { CpuLoadPercent, AvailableRamMb, GpuLoadPercent, IsUserActive, TimestampUtc }
IMachineIdentityProvider // → stable hashed machine identity (SHA-256 hex)
IMachineFactorsProvider // → MachineFactor[] { Key, Value } — the same identity, kept apart
IStandardDirectoryProvider // → User data / Shared data / Cache / Logs / Config / Temp directories
SystemProfileProvider : ISystemProfileProvider
SystemHealthProvider : ISystemHealthProvider, IDisposable
MachineIdentityProvider : IMachineIdentityProvider
MachineFactorsProvider : IMachineFactorsProvider
StandardDirectoryProvider : IStandardDirectoryProvider
The default constructor on every provider auto-selects the per-OS probe for the current host:
var profile = await new SystemProfileProvider().CollectAsync();
var health = await new SystemHealthProvider().CollectAsync();
var id = await new MachineIdentityProvider(
new StandardDirectoryProvider("Acme", "MyApp")
).GetMachineIdentityAsync();
var factors = await new MachineFactorsProvider().CollectAsync();
One identity, or several factors
IMachineIdentityProvider collapses the machine into a single hash.
IMachineFactorsProvider returns the same observations kept apart:
machine-id = b84fe522-d160-424a-932e-642f9cfa8b3b ← OS identity; survives hardware change
primary-mac = 50:EB:F6:26:50:F1 ← physical adapter; survives OS reinstall
machine-name = DESKTOP-OFFICE ← weak, but free and independent
Reach for the factors when partial change has to be tolerated. Hardware drifts one component at a time, and a single combined hash turns a replaced network card into a support ticket; with factors, a caller can require n of m to still match. Reach for the single identity when a machine just needs a name.
Values are returned raw, never hashed — hashing, storage and matching policy belong to the caller, and a diagnostics screen wants to show a human the real value. Factors that cannot be read are omitted, never returned blank, so two hosts that both failed to read something never look alike because of it.
primary-mac picks the physical adapter and ignores the noise a real machine
carries — hypervisor, container, VPN and Bluetooth adapters, tunnels, and the
per-adapter filter-driver instances Windows enumerates. Wired is preferred over
wireless, because some hosts randomise wireless addresses per network. Adapter
operational status is deliberately ignored: unplugging a cable must not
change the machine's identity.
Architecture
The library follows the Strategy pattern: the public providers
(SystemProfileProvider, SystemHealthProvider, MachineIdentityProvider)
are thin orchestrators that delegate all OS-specific reading to an internal
IPlatformProbe instance. One probe per OS, each in its own file.
SystemProfileProvider ─┐
SystemHealthProvider ─┼─→ IPlatformProbe ─→ PlatformProbeWindows
MachineIdentityProvider ─┘ ─→ PlatformProbeLinux
─→ PlatformProbeMacOS
─→ PlatformProbeAndroid (: PlatformProbeLinux)
─→ PlatformProbeNull (Unknown fallback)
Benefits:
- Per-OS code lives in one file. "How does macOS detect storage type?"
is answered by opening
PlatformProbeMacOS.cs, not by greping forOperatingSystem.IsMacOS()across three providers. - Adding a new platform = one new
PlatformProbe*class implementingIPlatformProbe, plus a switch arm inPlatformProbeFactory. No changes to provider orchestrators. - Testable in isolation. Tests inject a fake probe through the providers'
internaltest-only constructors, exercise per-OS rules on any host, and verify disposal ownership. - The wire shape (
SystemProfile,SystemHealthSnapshot) is stable. Probes can change without breaking serialization.
Continuous integration
The Platform suite is the only one in the OutWit.Common monorepo whose
behaviour varies per host OS, so its CI also varies: in addition to the
default windows-latest test run shared with every other package, the
test suite is also executed on ubuntu-latest and macos-latest
runners on every change under Platform/**. The Linux and macOS code
paths in the bundled probes are therefore validated end-to-end against
real OS facilities (/proc, sysctl, vm_stat, diskutil, …) on
every commit, not just unit-tested via fakes. See
.github/workflows/ci.yml
(run-platform-multi-os job).
Coverage matrix
What each bundled probe currently reports. Empty / Unknown is a valid
answer — providers never throw on missing data.
| Capability | Windows | Linux | macOS | Android | Unknown |
|---|---|---|---|---|---|
| CPU model name | Registry: ProcessorNameString |
/proc/cpuinfo model name |
sysctl machdep.cpu.brand_string |
/proc/cpuinfo Hardware → build.prop ro.board.platform |
empty |
| GPU list | WMI Win32_VideoController + driver registry |
lspci -mm (no VRAM) |
system_profiler SPDisplaysDataType |
build.prop GLES/Vulkan hint (best-effort) |
empty |
| Storage type | WMI MSFT_PhysicalDisk |
/sys/block/{dev}/queue/rotational + nvme prefix |
diskutil info text |
SSD (mobile = solid-state by definition) |
Unknown |
| CPU load % | PerformanceCounter("Processor", "% Processor Time") |
/proc/stat delta |
top -l 1 -s 0 "idle" % |
/proc/stat (inherited from Linux) |
0 |
| Available RAM MB | PerformanceCounter("Memory", "Available MBytes") |
/proc/meminfo MemAvailable |
vm_stat (free + inactive) |
/proc/meminfo (inherited from Linux) |
0 |
| User active | GetLastInputInfo P/Invoke (5-min idle threshold) |
true (no headless API) |
true (no headless API) |
true (needs Android SDK PowerManager) |
true |
| Machine identity | Registry MachineGuid |
/etc/machine-id / /var/lib/dbus/machine-id |
sysctl kern.uuid |
build.prop ro.serialno → Linux machine-id fallback |
null (per-app GUID file) |
| Machine factors | machine-id + primary-mac + machine-name |
same | same, but machine-id = ioreg IOPlatformUUID |
same | primary-mac + machine-name |
primary-mac and machine-name come from the BCL and are therefore identical on
every platform — one copy of the rules instead of one per probe.
The machine-id factor is the identity of the hardware a licence binds to.
On Windows and Linux it is the machine identity above (written once per
installation and kept for it). On macOS it is the firmware's platform UUID
(System Information's "Hardware UUID"), not kern.uuid: that names the kernel
binary macOS ships for the chip family, so it is shared by every Mac of that
family on the same build and changes with every update. The machine identity
itself keeps kern.uuid on macOS, so the identities already handed out stay
as they are.
The Android probe stays pure .NET — it works on net10.0 without an
android TFM. Bridging real Android APIs (Build, PowerManager,
Settings.Secure.ANDROID_ID, EGL/Vulkan for the GPU) is an app-level
concern; a consuming Android client can subclass PlatformProbeAndroid and
override the relevant methods.
Note:
PlatformProbeAndroidisinternal, like all the other probes — if you want app-level overrides, file an issue and we can promote the shape topublicalong with a clean DI registration entry point.
Adding a new platform
Today the supported set is Windows / Linux / macOS / Android / Unknown. Adding (say) iOS or BSD looks like this:
- Add the value to
PlatformKind(e.g.Ios). - Add the corresponding branch to
OsPlatform+PlatformDetector. - Write
Internal/PlatformProbeIos.cs : IPlatformProbe. Return safe defaults for what the OS doesn't expose; implement the rest. - Add a
PlatformKind.Ios => new PlatformProbeIos()arm toPlatformProbeFactory.ForPlatform. - Write
Internal/PlatformProbeIosTests.csexercising the new methods.
The public API (ISystemProfileProvider, SystemProfile, …) does not
change.
License
Licensed under the Apache License, Version 2.0. See LICENSE.
Attribution (optional)
If you use OutWit.Common.Platform in a product, a mention is appreciated (but not required): "Powered by OutWit.Common.Platform (https://ratner.io/)".
Trademark / Project name
"OutWit" and the OutWit logo are used to identify the official project by Dmitry Ratner. You may refer to the project name in a factual way (e.g., "built with OutWit.Common.Platform") or to indicate compatibility. You may not use the name as the name of a fork or derived product in a way that implies it is the official project, nor use the OutWit logo to promote forks or derived products without permission.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 is compatible. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. 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 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. |
-
net10.0
- OutWit.Common (>= 1.4.0)
- System.Diagnostics.PerformanceCounter (>= 10.0.10)
- System.Management (>= 10.0.10)
-
net6.0
- OutWit.Common (>= 1.4.0)
- System.Diagnostics.PerformanceCounter (>= 8.0.1)
- System.Management (>= 8.0.0)
-
net7.0
- OutWit.Common (>= 1.4.0)
- System.Diagnostics.PerformanceCounter (>= 8.0.1)
- System.Management (>= 8.0.0)
-
net8.0
- OutWit.Common (>= 1.4.0)
- System.Diagnostics.PerformanceCounter (>= 9.0.18)
- System.Management (>= 9.0.18)
-
net9.0
- OutWit.Common (>= 1.4.0)
- System.Diagnostics.PerformanceCounter (>= 9.0.18)
- System.Management (>= 9.0.18)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on OutWit.Common.Platform:
| Package | Downloads |
|---|---|
|
OutWit.Common.Licensing
Offline software licensing for .NET: a compact signed licence token (JWS-style, ECDSA/RSA), a key ring scoped per product line, tolerant n-of-m machine binding, validation that reports a reason rather than a boolean, and a store that survives clock tampering. No BouncyCastle, no vendor service, no phone-home. |
GitHub repositories
This package is not used by any popular GitHub repositories.