RootHeraldUE Plugin
A drop-in UE5 plugin that statically links the Root Herald native SDK into your game module and exposes Blueprint-callable Collect Evidence nodes. Keyless: the plugin holds no RootHerald key and never renders a verdict — it collects an evidence blob over a backend-issued nonce for your backend to appraise. Your game's code-signing certificate covers everything — there's no Root-Herald-signed DLL in your install footprint.
This SDK is planned and not yet available. 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
1. Copy Plugins/RootHeraldUE/from the sample project (which ships with the plugin when it's released) into your project's Plugins/ directory.
2. Drop the per-platform static archives into the plugin's ThirdParty/ tree (the conventional UE5 location for vendored static libs that the .Build.cs wires up via PublicAdditionalLibraries):
RootHerald.lib→Plugins/RootHeraldUE/ThirdParty/RootHerald/Win64/librootherald.a→Plugins/RootHeraldUE/ThirdParty/RootHerald/Mac/librootherald.a→Plugins/RootHeraldUE/ThirdParty/RootHerald/Linux/rootherald.h,protocol.h→Plugins/RootHeraldUE/ThirdParty/RootHerald/Include/
3. Regenerate project files, then enable the plugin via Edit → Plugins → RootHeraldUE.
RootHeraldUE.Build.cs
UE5's .Build.cs system handles static libs viaPublicAdditionalLibraries (the linker input list) and PublicIncludePaths (the header search path). The pattern below is conditional per-platform so a Windows shipping build picks up RootHerald.lib while a Mac build picks up librootherald.a:
using UnrealBuildTool;
using System.IO;
public class RootHeraldUE : ModuleRules
{
public RootHeraldUE(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(new[] { "Core", "CoreUObject", "Engine" });
string ThirdParty = Path.Combine(ModuleDirectory, "..", "..", "ThirdParty", "RootHerald");
PublicIncludePaths.Add(Path.Combine(ThirdParty, "Include"));
if (Target.Platform == UnrealTargetPlatform.Win64)
{
PublicAdditionalLibraries.Add(Path.Combine(ThirdParty, "Win64", "RootHerald.lib"));
// System libraries the static archive depends on.
PublicSystemLibraries.AddRange(new[] {
"ncrypt.lib", "tbs.lib", "winhttp.lib", "bcrypt.lib", "crypt32.lib"
});
}
else if (Target.Platform == UnrealTargetPlatform.Mac)
{
PublicAdditionalLibraries.Add(Path.Combine(ThirdParty, "Mac", "librootherald.a"));
PublicFrameworks.AddRange(new[] { "Security", "Foundation" });
}
else if (Target.Platform == UnrealTargetPlatform.Linux)
{
PublicAdditionalLibraries.Add(Path.Combine(ThirdParty, "Linux", "librootherald.a"));
PublicSystemLibraries.AddRange(new[] {
"tss2-esys", "tss2-tctildr", "tss2-mu", "curl", "pthread"
});
}
}
}There is no RuntimeDependencies.Add(...)entry for Root Herald — that's the point. The library is fully baked into your shipping executable.
C++ usage
The client is keyless — NewObject takes no key and no endpoint. It collects an evidence blob over a nonce your backend issued (your matchmaker or auth service called POST /api/v1/attestations/challenge with its rh_sk_ secret). You relay the opaque blob to your backend, which POSTs it to /api/v1/attestations/verify and enforces the verdict. The verdict never travels through the game client.
#include "RootHeraldClient.h"
void AMyGameMode::OnPlayerJoined(APlayerController* PC)
{
URootHeraldClient* Client = NewObject<URootHeraldClient>();
// 1. Fetch a fresh nonce from your backend (server-to-server, rh_sk_ secret).
const FString Nonce = MyBackend::ChallengeNonce(TEXT("match-join"));
// 2. Collect a keyless quote-over-nonce evidence blob locally.
FString Evidence;
if (!Client->CollectEvidence(Nonce, Evidence))
{
UE_LOG(LogGame, Warning, TEXT("RootHerald evidence collection failed"));
return;
}
// 3. Relay the opaque blob to your backend, which appraises it and tells
// you whether to admit the player. The client never sees a verdict.
if (!MyBackend::VerifyAndAuthorize(TEXT("match-join"), Evidence))
{
PC->ClientWasKicked(FText::FromString(TEXT("Device not trusted")));
}
}Blueprint usage
The plugin exposes Collect Evidence as a Blueprint-callable node returning the opaque evidence FString. A sample BP at SampleContent/BP_LauncherGate.uasset wires:
- Event BeginPlay
- Get Challenge Nonce (from your backend's HTTP endpoint)
- Collect Evidence (nonce → opaque blob)
- Relay To Backend → Branch on your backend's allow/deny → Open Level / Show Error
Sample project
The complete plugin template ships with the plugin when it's released. It includes the .uplugin manifest, the .Build.cs with platform-conditional binary inclusion, and the C++ wrapper around the public C ABI.
On Linux dedicated servers, do not ship the client library; appraise on the server by relaying the evidence blob your client collected to /api/v1/attestations/verify with your rh_sk_ secret. The dedi server owns the verdict; the game client only collects evidence.