RootHerald.Native (P/Invoke wrapper)
P/Invoke binding to the native C++ SDK for desktop .NET applications — WPF, WinUI, MAUI, Avalonia. Like the C++ SDK it is a keyless evidence collector: it holds no RootHerald key and never renders a verdict. It collects an evidence blob over a backend-issued nonce; your backend relays that blob to the verify API, which appraises it.
This SDK is implemented but not yet published to its package registry. Until it ships, mint attestation tokens on the device and verify them server-side with @rootherald/node (or any available server SDK). The API shown below is the planned surface and may change before release.
Install
dotnet add package RootHerald.Native --version 1.0.0-preview.1The NuGet bundles RootHerald.dll under runtimes/win-x64/native/; the .NET host's native-loader picks it up automatically. Linux and macOS RIDs ship in Wave 3.
Sample
The client is keyless — the constructor takes no key and no endpoint. It collects an evidence blob over a nonce your backend issued; your backend relays that blob to POST /api/v1/attestations/verify (using its rh_sk_ secret via a server SDK such as @rootherald/node) and enforces the returned verdict. The verdict never travels through the client.
using RootHerald;
public partial class LauncherWindow : Window
{
private readonly RootHeraldClient _herald;
public LauncherWindow()
{
InitializeComponent();
// Keyless: no api_key, no endpoint — nothing to connect to.
_herald = new RootHeraldClient();
_herald.SetApplicationId("com.acme.launcher");
}
private async void OnPlay_Click(object sender, RoutedEventArgs e)
{
try
{
// 1. Ask YOUR backend for a fresh challenge nonce (it called
// POST /api/v1/attestations/challenge with its rh_sk_ secret).
string nonce = await MyBackend.GetChallengeNonceAsync();
// 2. Collect a quote-over-nonce evidence blob locally. Keyless, no network.
string evidenceJson = await _herald.CollectEvidenceAsync(nonce);
// 3. Relay the blob to your backend, which POSTs it to /attestations/verify
// and returns whether the device was trusted.
var launch = await MyBackend.VerifyAndAuthorizeLaunchAsync(evidenceJson);
if (!launch.Allowed)
{
MessageBox.Show($"Device not trusted: {launch.Reason}");
return;
}
LaunchGame(launch.DeviceId);
}
catch (RootHeraldException ex)
{
MessageBox.Show($"Evidence collection failed (status={ex.NativeStatus}): {ex.Message}");
}
}
protected override void OnClosed(EventArgs e)
{
_herald.Dispose();
base.OnClosed(e);
}
}Where the evidence goes
The client opens no socket to RootHerald and carries no key, so there is no endpoint to configure. Every RootHerald call happens on your server: your backend issues the nonce (POST /api/v1/attestations/challenge), relays the collected blob to POST /api/v1/attestations/verify, and enforces the verdict — all authenticated with its rh_sk_ secret. Use a server SDK such as @rootherald/node for that leg; the secret must never reach the desktop client.
How the binding works
The package ships a managed wrapper plus the native runtime for the target RID (win-x64 in Wave 2; linux-x64, osx-arm64, osx-x64 in Wave 3).
The native ABI is synchronous; the wrapper marshals each call through Task.Run so CollectEvidenceAsyncdoesn't block the calling thread. Disposal is wired through IDisposable so a finalizer never has to clean up native handles.
RootHeraldClient.AbiVersion and RootHeraldClient.LibraryVersionreport the loaded native binary's versions. Validate at startup if you bundle the .dll yourself rather than letting NuGet's native-loader place it.