Skip to content
SDKs · Unreal EngineComing soon

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.

Coming soon — in development

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.libPlugins/RootHeraldUE/ThirdParty/RootHerald/Win64/
  • librootherald.aPlugins/RootHeraldUE/ThirdParty/RootHerald/Mac/
  • librootherald.aPlugins/RootHeraldUE/ThirdParty/RootHerald/Linux/
  • rootherald.h, protocol.hPlugins/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:

RootHeraldUE.Build.cscsharp
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.

LauncherGate.cppcpp
#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:

  1. Event BeginPlay
  2. Get Challenge Nonce (from your backend's HTTP endpoint)
  3. Collect Evidence (nonce → opaque blob)
  4. Relay To BackendBranch 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.

Dedicated server builds

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.